com.gft.unity.android.AndroidIO.java Source code

Java tutorial

Introduction

Here is the source code for com.gft.unity.android.AndroidIO.java

Source

/*
 Copyright (c) 2012 GFT Appverse, S.L., Sociedad Unipersonal.
    
 This Source  Code Form  is subject to the  terms of  the Appverse Public License 
 Version 2.0  ("APL v2.0").  If a copy of  the APL  was not  distributed with this 
 file, You can obtain one at http://appverse.org/legal/appverse-license/.
    
 Redistribution and use in  source and binary forms, with or without modification, 
 are permitted provided that the  conditions  of the  AppVerse Public License v2.0 
 are met.
    
 THIS SOFTWARE IS PROVIDED BY THE  COPYRIGHT HOLDERS  AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS  OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT LIMITED TO,   THE IMPLIED
 WARRANTIES   OF  MERCHANTABILITY   AND   FITNESS   FOR A PARTICULAR  PURPOSE  ARE
 DISCLAIMED. EXCEPT IN CASE OF WILLFUL MISCONDUCT OR GROSS NEGLIGENCE, IN NO EVENT
 SHALL THE  COPYRIGHT OWNER  OR  CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL,  SPECIAL,   EXEMPLARY,  OR CONSEQUENTIAL DAMAGES  (INCLUDING, BUT NOT
 LIMITED TO,  PROCUREMENT OF SUBSTITUTE  GOODS OR SERVICES;  LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) 
 ARISING  IN  ANY WAY OUT  OF THE USE  OF THIS  SOFTWARE,  EVEN  IF ADVISED OF THE 
 POSSIBILITY OF SUCH DAMAGE.
 */
package com.gft.unity.android;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpVersion;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.spongycastle.asn1.ASN1Primitive;
import org.spongycastle.asn1.DEROctetString;
import org.spongycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.spongycastle.asn1.x509.AccessDescription;
import org.spongycastle.asn1.x509.AuthorityInformationAccess;
import org.spongycastle.asn1.x509.X509Extension;
import org.spongycastle.asn1.x509.X509Extensions;
import org.spongycastle.jce.provider.X509CertParser;
import org.spongycastle.jce.provider.X509CertificateObject;
import org.spongycastle.ocsp.BasicOCSPResp;
import org.spongycastle.ocsp.CertificateID;
import org.spongycastle.ocsp.OCSPException;
import org.spongycastle.ocsp.OCSPReq;
import org.spongycastle.ocsp.OCSPReqGenerator;
import org.spongycastle.ocsp.OCSPResp;
import org.spongycastle.ocsp.RevokedStatus;
import org.spongycastle.ocsp.SingleResp;
import org.spongycastle.x509.util.StreamParsingException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import android.content.Context;
import android.os.Build;

import com.gft.unity.core.io.AbstractIO;
import com.gft.unity.core.io.HTTPProtocolVersion;
import com.gft.unity.core.io.IOCookie;
import com.gft.unity.core.io.IOHeader;
import com.gft.unity.core.io.IORequest;
import com.gft.unity.core.io.IOResponse;
import com.gft.unity.core.io.IOResponseHandle;
import com.gft.unity.core.io.IOResponseHandler;
import com.gft.unity.core.io.IOService;
import com.gft.unity.core.io.IOServiceEndpoint;
import com.gft.unity.core.io.IOSessionContext;
import com.gft.unity.core.io.RequestMethod;
import com.gft.unity.core.io.ServiceType;
import com.gft.unity.core.system.IOperatingSystem;
import com.gft.unity.core.system.SystemLogger.Module;

public class AndroidIO extends AbstractIO implements Runnable {

    private static final AndroidSystemLogger LOG = AndroidSystemLogger.getSuperClassInstance();
    private static String _VALIDATECERTIFICATES = "$ValidateCertificates$";

    private static String _VALIDATEFINGERPRINTS = "$ValidateFingerprints$";

    private static final String DEFAULT_SERVICE_TYPE = "XMLRPC_JSON";
    private static final String DEFAULT_SERVICE_METHOD = "POST";
    private static final String DEFAULT_ENCODING = "UTF-8";
    private static final String SERVICE_NODE_ATTRIBUTE = "SERVICE";
    private static final String ENDPOINT_NODE_ATTRIBUTE = "END-POINT";
    private static final String TYPE_ATTRIBUTE = "type";
    private static final String REQ_METHOD_ATTRIBUTE = "req-method";
    private static final String HOST_ATTRIBUTE = "host";
    private static final String PORT_ATTRIBUTE = "port";
    private static final String PATH_ATTRIBUTE = "path";
    private static final String PROXY_ATTRIBUTE = "proxy";
    private static final String SCHEME_ATTRIBUTE = "scheme";
    private static final String SERVICE_ATTRIBUTE = "name";
    private static final String FINGERPRINT_ATTRIBUTE = "fingerprint";

    // private static final String HTTP_SCHEME = "http";
    private static final String HTTPS_SCHEME = "https";

    private static int DEFAULT_READWRITE_TIMEOUT = 15000; // 15 seconds timeout establishing connection
    private static int DEFAULT_RESPONSE_TIMEOUT = 100000; // 100 seconds timeout reading response

    // reading response parameters
    private static int DEFAULT_BUFFER_READ_SIZE = 4096; // 4 KB
    private static int MAX_BINARY_SIZE = 8 * 1024 * 1024; // 8 MB

    private static DefaultHttpClient httpClient = new DefaultHttpClient();
    private static DefaultHttpClient httpSSLClient = null;
    private static final CookieStore cookieStore = httpClient.getCookieStore();

    public static HashMap<String, String[]> FINGERPRINTS;

    public AndroidIO() {
        loadServicesConfig();
        FINGERPRINTS = getFingerprints();
        (new Thread(this)).start();
    }

    private HashMap<String, String[]> getFingerprints() {
        HashMap<String, String[]> result = new HashMap<String, String[]>();
        for (IOService serv : servicesConfig.getServices()) {
            if (serv.getEndpoint().getFingerprint() != null) {
                try {
                    URL aURL = new URL(serv.getEndpoint().getHost());
                    result.put(aURL.getHost(), serv.getEndpoint().getFingerprint().split(","));
                    //LOG.LogDebug(Module.PLATFORM,"*************** getFingerprints: " +aURL.getHost()+":"+serv.getEndpoint().getFingerprint());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }

            }
        }
        IOService[] services = servicesConfig.getServices();
        for (IOService serv : services) {
            serv.getEndpoint().setFingerprint(null);
        }

        return result;
    }

    public boolean Validatecertificates() {
        return Boolean.parseBoolean(AndroidIO._VALIDATECERTIFICATES);
    }

    public static boolean ValidateFingerprints() {
        return Boolean.parseBoolean(AndroidIO._VALIDATEFINGERPRINTS);
    }

    private void loadServicesConfig() {
        Context context = AndroidServiceLocator.getContext();
        ArrayList<IOService> servicesList = new ArrayList<IOService>();
        try {

            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XmlPullParser xpp = factory.newPullParser();
            xpp.setInput(AndroidUtils.getInstance().getAssetInputStream(context.getAssets(), SERVICES_CONFIG_FILE),
                    DEFAULT_ENCODING);
            int eventType = xpp.getEventType();
            IOService service = null;
            String serviceName = "";
            String serviceType = "";
            String serviceMethod = "";
            String fingerprint = "";
            IOServiceEndpoint serviceEndpoint = null;
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    if (xpp.getName().toUpperCase().equals(SERVICE_NODE_ATTRIBUTE)) {
                        service = new IOService();
                        serviceName = xpp.getAttributeValue(null, SERVICE_ATTRIBUTE);
                        serviceType = xpp.getAttributeValue(null, TYPE_ATTRIBUTE);
                        serviceMethod = xpp.getAttributeValue(null, REQ_METHOD_ATTRIBUTE);
                    } else if (xpp.getName().toUpperCase().equals(ENDPOINT_NODE_ATTRIBUTE)) {
                        serviceEndpoint = new IOServiceEndpoint();
                        serviceEndpoint.setHost(xpp.getAttributeValue(null, HOST_ATTRIBUTE));
                        String port = xpp.getAttributeValue(null, PORT_ATTRIBUTE);
                        if (port != null) {
                            serviceEndpoint.setPort(Integer.valueOf(port));
                        }
                        serviceEndpoint.setPath(xpp.getAttributeValue(null, PATH_ATTRIBUTE));
                        serviceEndpoint.setProxyUrl(xpp.getAttributeValue(null, PROXY_ATTRIBUTE));
                        serviceEndpoint.setScheme(xpp.getAttributeValue(null, SCHEME_ATTRIBUTE));
                        fingerprint = xpp.getAttributeValue(null, FINGERPRINT_ATTRIBUTE);
                        if (fingerprint != null)
                            fingerprint = fingerprint.toUpperCase();
                        serviceEndpoint.setFingerprint(fingerprint);
                        //LOG.LogDebug(Module.PLATFORM, "LoadConfig fingerprint [" + fingerprint + "]");
                    }
                } else if (eventType == XmlPullParser.END_TAG) {
                    if (xpp.getName().toUpperCase().equals(SERVICE_NODE_ATTRIBUTE)) {
                        service.setName(serviceName);
                        if (serviceType == null) {
                            serviceType = DEFAULT_SERVICE_TYPE;
                        }
                        service.setType(ServiceType.valueOf(serviceType));
                        if (serviceMethod == null) {
                            serviceMethod = DEFAULT_SERVICE_METHOD;
                        }
                        service.setRequestMethod(RequestMethod.valueOf(serviceMethod));
                        service.setEndpoint(serviceEndpoint);
                        servicesList.add(service);

                    }
                }
                eventType = xpp.next();

            }
        } catch (Exception ex) {
            LOG.LogDebug(Module.PLATFORM, "LoadConfig error [" + SERVICES_CONFIG_FILE + "]: " + ex.getMessage());
        }
        servicesConfig.setServices(servicesList.toArray(new IOService[servicesList.size()]));
    }

    private String formatRequestUriString(IORequest request, IOServiceEndpoint endpoint, String requestMethod) {
        String requestUriString = endpoint.getHost() + ":" + endpoint.getPort() + endpoint.getPath();
        if (endpoint.getPort() == 0) {
            requestUriString = endpoint.getHost() + endpoint.getPath();
        }

        if (requestMethod.equalsIgnoreCase(RequestMethod.GET.toString())) {
            // add request content to the URI string when GET method.
            if (request.getContent() != null) {
                requestUriString += request.getContent();
            }
        }

        // JUST FOR LOCAL TESTING, DO NOT UNCOMMENT FOR PLATFORM RELEASE
        // LOG.LogDebug(Module.PLATFORM, "Requesting service: " + requestUriString);
        LOG.LogDebug(Module.PLATFORM, "Request method: " + requestMethod);

        return requestUriString;
    }

    private boolean applySecurityValidations(String requestUriString)
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException,
            KeyManagementException, UnrecoverableKeyException {
        if (requestUriString.startsWith(HTTPS_SCHEME)) {
            LOG.LogDebug(Module.PLATFORM, "Applying Custom HTTPSClient (requested URI contains HTTPS protocol)");
            if (httpSSLClient == null) {
                LOG.LogDebug(Module.PLATFORM,
                        "Custom HTTPSClient not yet intialized on first request, forcing creating it...");
                createHttpClients();
            }
            httpClient = httpSSLClient; // reusing HTTP SSL client
        } else {
            LOG.LogDebug(Module.PLATFORM, "Applying DefaultHTTPClient");
            httpClient = new DefaultHttpClient();
            // [MOBPLAT-200] : allow gzip, deflate decompression modes
            httpClient.addResponseInterceptor(new GzipHttpResponseInterceptor());
        }

        return true;
    }

    /**
     * 
     * @param request
     * @param endpoint
     * @throws URISyntaxException
     */
    private void addingHttpClientParms(IORequest request, IOServiceEndpoint endpoint) throws URISyntaxException {

        // preserving the cookies between requests
        httpClient.setCookieStore(cookieStore);

        if (request.getProtocolVersion() == HTTPProtocolVersion.HTTP11) {
            httpClient.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_1);
        } else {
            httpClient.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_0); // not chunked requests
        }

        httpClient.getParams().setIntParameter("http.connection.timeout", DEFAULT_READWRITE_TIMEOUT);
        httpClient.getParams().setIntParameter("http.socket.timeout", DEFAULT_RESPONSE_TIMEOUT);

        if (endpoint.getProxyUrl() != null && !endpoint.getProxyUrl().equals("")
                && !endpoint.getProxyUrl().equals("null")) {
            URI proxyUrl = new URI(endpoint.getProxyUrl());
            HttpHost proxyHost = new HttpHost(proxyUrl.getHost(), proxyUrl.getPort(), proxyUrl.getScheme());
            httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
        }
    }

    private HttpEntityEnclosingRequestBase buildWebRequest(IORequest request, IOService service,
            String requestUriString, String requestMethod) throws UnsupportedEncodingException, URISyntaxException {

        HttpEntityEnclosingRequestBase httpRequest = new HttpAppverse(new URI(requestUriString), requestMethod);

        /*************
         * adding content as entity, for request methods != GET
         *************/
        if (!requestMethod.equalsIgnoreCase(RequestMethod.GET.toString())) {
            if (request.getContent() != null && request.getContent().length() > 0) {
                httpRequest.setEntity(new StringEntity(request.getContent(), HTTP.UTF_8));
            }
        }

        /*************
         * CONTENT TYPE
         *************/
        String contentType = contentTypes.get(service.getType()).toString();
        if (request.getContentType() != null) {
            contentType = request.getContentType();

        }
        httpRequest.setHeader("Content-Type", contentType);

        /*************
         * CUSTOM HEADERS HANDLING
         *************/
        if (request.getHeaders() != null && request.getHeaders().length > 0) {
            for (IOHeader header : request.getHeaders()) {
                httpRequest.setHeader(header.getName(), header.getValue());
            }
        }

        /*************
         * COOKIES HANDLING
         *************/
        if (request.getSession() != null && request.getSession().getCookies() != null
                && request.getSession().getCookies().length > 0) {
            StringBuffer buffer = new StringBuffer();
            IOCookie[] cookies = request.getSession().getCookies();
            for (int i = 0; i < cookies.length; i++) {
                IOCookie cookie = cookies[i];
                buffer.append(cookie.getName());
                buffer.append("=");
                buffer.append(cookie.getValue());
                if (i + 1 < cookies.length) {
                    buffer.append(" ");
                }
            }
            httpRequest.setHeader("Cookie", buffer.toString());
        }

        /*************
         * DEFAULT HEADERS
         *************/
        httpRequest.setHeader("Accept", contentType); // Accept header should be the same as the request content type used (could be override by the request, or use the service default)
        // httpRequest.setHeader("content-length",
        // String.valueOf(request.getContentLength()));
        httpRequest.setHeader("keep-alive", String.valueOf(false));

        // TODO: set conn timeout ???

        /*************
         * setting user-agent
         *************/
        IOperatingSystem system = (IOperatingSystem) AndroidServiceLocator.GetInstance()
                .GetService(AndroidServiceLocator.SERVICE_TYPE_SYSTEM);
        HttpProtocolParams.setUserAgent(httpClient.getParams(), system.GetOSUserAgent());

        return httpRequest;
    }

    /**
     * 
     * @param httpResponse
     * @param service
     * @return
     * @throws IOException 
     * @throws IllegalStateException 
     */
    private IOResponse readWebResponse(HttpResponse httpResponse, IOService service)
            throws IllegalStateException, IOException {

        IOResponse response = new IOResponse();
        response.setSession(new IOSessionContext());

        byte[] resultBinary = null;

        String responseMimeTypeOverride = (httpResponse.getLastHeader("Content-Type") != null
                ? httpResponse.getLastHeader("Content-Type").getValue()
                : null);
        LOG.LogDebug(Module.PLATFORM, "response content type: " + responseMimeTypeOverride);

        // getting response input stream
        InputStream responseStream = httpResponse.getEntity().getContent();

        int lengthContent = -1;
        int bufferReadSize = DEFAULT_BUFFER_READ_SIZE;

        try {
            lengthContent = (int) httpResponse.getEntity().getContentLength();
            if (lengthContent >= 0 && lengthContent <= bufferReadSize) {
                bufferReadSize = lengthContent;
            }
        } catch (Exception e) {
            LOG.LogDebug(Module.PLATFORM,
                    "Error while getting Content-Length header from response: " + e.getMessage());
        }

        if (lengthContent > MAX_BINARY_SIZE) {
            LOG.LogDebug(Module.PLATFORM,
                    "WARNING! - file exceeds the maximum size defined in platform (" + MAX_BINARY_SIZE + " bytes)");
        } else {
            LOG.LogDebug(Module.PLATFORM, "reading response stream content length: " + lengthContent);
            /* WE DON'T READ IN A FULL BLOCK ANYMORE
             * 
            // Read in block, if content length provided.
            // Create the byte array to hold the data
            resultBinary = new byte[length];
                
            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset < resultBinary.length
                  && (numRead = responseStream.read(resultBinary,
                 offset, resultBinary.length - offset)) >= 0) {
               offset += numRead;
            }
                
            */

            // Read in buffer blocks till the end of stream.
            ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();

            byte[] readBuffer = new byte[bufferReadSize];
            int readLen = 0;
            int totalReadLen = 0;
            try {
                while ((readLen = responseStream.read(readBuffer, 0, readBuffer.length)) > 0) {
                    outBuffer.write(readBuffer, 0, readLen);
                    totalReadLen = totalReadLen + readLen;
                }
            } finally {
                resultBinary = outBuffer.toByteArray();
                outBuffer.close();
                outBuffer = null;
            }
            LOG.LogDebug(Module.PLATFORM, "total read length: " + totalReadLen);
        }

        /*************
         * COOKIES HANDLING
         *************/
        LOG.LogDebug(Module.PLATFORM, "reading cookies.. ");
        if (cookieStore.getCookies().size() > 0) {
            LOG.LogDebug(Module.PLATFORM, "cookies store found: " + cookieStore.getCookies().size());
            // using hashmap to remove duplicated cookies keys
            Map<String, IOCookie> cookiesMap = new HashMap<String, IOCookie>();
            //Map<String,Cookie> cookiesStoreMap = new HashMap<String,Cookie>();

            // rollback
            //IOCookie[] cookies = new IOCookie[cookieStore.getCookies() .size()];

            for (int i = 0; i < cookieStore.getCookies().size(); i++) {
                Cookie cookie = cookieStore.getCookies().get(i);
                IOCookie ioCookie = new IOCookie();
                ioCookie.setName(cookie.getName());
                ioCookie.setValue(cookie.getValue());
                LOG.LogDebug(Module.PLATFORM, "cookies found: " + cookie.getName() + "=" + cookie.getValue());

                cookiesMap.put(ioCookie.getName(), ioCookie);
                LOG.LogDebug(Module.PLATFORM, "cookies stored: " + cookiesMap.get(ioCookie.getName()).getName()
                        + "=" + cookiesMap.get(ioCookie.getName()).getValue());
                //cookiesStoreMap.put(cookie.getName(), cookie);

                // rollback
                //cookies[i] = ioCookie;

            }

            // clearing cookie store
            /*
            LOG.LogDebug(Module.PLATFORM, "cookiestor. length: "+ cookieStore.getCookies().size());
            cookieStore.clear();
            LOG.LogDebug(Module.PLATFORM, "cookiestore cleared. length: "+  cookieStore.getCookies().size());
            Cookie[] newCookiesToStore = cookiesStoreMap.values().toArray(new Cookie[0]);
            for(int j=0; j < newCookiesToStore.length; j++) 
               cookieStore.addCookie(newCookiesToStore[j]);
            LOG.LogDebug(Module.PLATFORM, "cookiestore stored. length: "+  cookieStore.getCookies().size());
            */

            IOCookie[] cookies = cookiesMap.values().toArray(new IOCookie[0]);
            LOG.LogDebug(Module.PLATFORM, "cookies processed (duplicated removed): " + cookies.length);

            // rollback
            //LOG.LogDebug(Module.PLATFORM, "cookies processed: " + cookies.length);

            response.getSession().setCookies(cookies);
        }

        /*************
         * CACHE
         *************/
        LOG.LogDebug(Module.PLATFORM, "reading cache header.. ");
        // preserve cache-control header from remote server, if any
        String cacheControlHeader = (httpResponse.getLastHeader("Cache-Control") != null
                ? httpResponse.getLastHeader("Cache-Control").getValue()
                : null);
        if (cacheControlHeader != null && !cacheControlHeader.isEmpty()) {
            LOG.LogDebug(Module.PLATFORM, "Found Cache-Control header on response: " + cacheControlHeader
                    + ", using it on internal response...");

            IOHeader cacheHeader = new IOHeader();
            cacheHeader.setName("Cache-Control");
            cacheHeader.setValue(cacheControlHeader);

            List<IOHeader> headers = new ArrayList<IOHeader>();
            if (response.getHeaders() != null) {
                headers = Arrays.asList(response.getHeaders());
            }
            headers.add(cacheHeader);
            response.setHeaders((IOHeader[]) headers.toArray(new IOHeader[0]));
        }

        // Close the input stream and return bytes
        responseStream.close();

        LOG.LogDebug(Module.PLATFORM, "checking binary service type... ");
        if (ServiceType.OCTET_BINARY.equals(service.getType())) {
            if (responseMimeTypeOverride != null
                    && !responseMimeTypeOverride.equals(contentTypes.get(service.getType()))) {
                response.setContentType(responseMimeTypeOverride);
            } else {
                response.setContentType(contentTypes.get(service.getType()).toString());
            }
            response.setContentBinary(resultBinary); // Assign binary
            // content here
        } else {
            response.setContentType(contentTypes.get(service.getType()).toString());
            response.setContent(new String(resultBinary));
        }
        LOG.LogDebug(Module.PLATFORM, "END reading response.. ");
        return response;
    }

    /**
     * 
     * @return
     */
    private String GetDirectoryRoot() {
        // should match the same directory root used by other platform APIs

        Context context = AndroidServiceLocator.getContext();
        return context.getFilesDir().getAbsolutePath();
    }

    /**
     * 
     * @param httpResponse
     * @param service
     * @param storePath
     * @return
     * @throws IllegalStateException
     * @throws IOException
     */
    private String readWebResponseAndStore(HttpResponse httpResponse, IOService service, String storePath)
            throws IllegalStateException, IOException {

        // getting response input stream
        InputStream responseStream = httpResponse.getEntity().getContent();

        int lengthContent = -1;
        int bufferReadSize = DEFAULT_BUFFER_READ_SIZE;

        try {
            lengthContent = (int) httpResponse.getEntity().getContentLength();
            if (lengthContent >= 0 && lengthContent <= bufferReadSize) {
                bufferReadSize = lengthContent;
            }
        } catch (Exception e) {
            LOG.LogDebug(Module.PLATFORM,
                    "Error while getting Content-Length header from response: " + e.getMessage());
        }
        LOG.LogDebug(Module.PLATFORM, "reading response stream content length: " + lengthContent);
        BufferedInputStream bis = new BufferedInputStream(responseStream);

        int size;
        byte[] buffer = new byte[bufferReadSize];

        File fullStorePath = new File(this.GetDirectoryRoot(), storePath);
        LOG.LogDebug(Module.PLATFORM, "Storing file at: " + fullStorePath.getAbsolutePath());

        FileOutputStream fos = new FileOutputStream(fullStorePath);
        BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length);
        int totalReadLen = 0;
        while ((size = bis.read(buffer, 0, buffer.length)) != -1) {
            bos.write(buffer, 0, size);
            // just for testing // LOG.LogDebug(Module.PLATFORM, "read length: " + size);
            totalReadLen = totalReadLen + size;
        }

        LOG.LogDebug(Module.PLATFORM, "total read length: " + totalReadLen);

        bos.flush();
        bos.close();
        fos.close();

        bis.close();

        // Close the input stream and return bytes
        responseStream.close();

        return storePath;
    }

    @Override
    public IOResponse InvokeService(IORequest request, IOService service) {

        IOServiceEndpoint endpoint = service.getEndpoint();
        IOResponse response = new IOResponse();

        if (service != null) {
            // JUST FOR LOCAL TESTING, DO NOT UNCOMMENT FOR PLATFORM RELEASE
            // LOG.LogDebug(Module.PLATFORM, "Request content: " + request.getContent());

            if (endpoint == null) {
                LOG.LogDebug(Module.PLATFORM, "No endpoint configured for this service name: " + service.getName());
                return response;
            }

            String requestMethod = service.getRequestMethod().toString();
            if (request.getMethod() != null && request.getMethod().length() > 0)
                requestMethod = request.getMethod().toUpperCase();

            String requestUriString = formatRequestUriString(request, endpoint, requestMethod);
            Thread timeoutThread = null;

            try {

                // Security - VALIDATIONS
                if (!this.applySecurityValidations(requestUriString)) {
                    return null;
                }

                // Adding HTTP Client Parameters
                this.addingHttpClientParms(request, endpoint);

                // Building Web Request to send
                HttpEntityEnclosingRequestBase httpRequest = this.buildWebRequest(request, service,
                        requestUriString, requestMethod);

                LOG.LogDebug(Module.PLATFORM, "Downloading service content");

                // Throw a new Thread to check absolute timeout
                timeoutThread = new Thread(new CheckTimeoutThread(httpRequest));
                timeoutThread.start();

                long start = System.currentTimeMillis();
                HttpResponse httpResponse = httpClient.execute(httpRequest);
                LOG.LogDebug(Module.PLATFORM,
                        "Content downloaded in " + (System.currentTimeMillis() - start) + "ms");

                // Read response
                response = this.readWebResponse(httpResponse, service);

            } catch (Exception ex) {
                LOG.Log(Module.PLATFORM, "Unnandled Exception requesting service.", ex);
                response.setContentType(contentTypes.get(ServiceType.REST_JSON).toString());
                response.setContent("Unhandled Exception Requesting Service. Message: " + ex.getMessage());
            } finally {
                // abort any previous timeout checking thread
                if (timeoutThread != null && timeoutThread.isAlive()) {
                    timeoutThread.interrupt();
                }
            }
        }

        LOG.LogDebug(Module.PLATFORM, "invoke service finished");
        return response;
    }

    @Override
    public String InvokeServiceForBinary(IORequest request, IOService service, String storePath) {

        IOServiceEndpoint endpoint = service.getEndpoint();

        if (service != null) {
            // JUST FOR LOCAL TESTING, DO NOT UNCOMMENT FOR PLATFORM RELEASE
            // LOG.LogDebug(Module.PLATFORM, "Request content (for binary): " + request.getContent());

            if (endpoint == null) {
                LOG.LogDebug(Module.PLATFORM, "No endpoint configured for this service name: " + service.getName());
                return null;
            }

            if (!ServiceType.OCTET_BINARY.equals(service.getType())) {
                LOG.LogDebug(Module.PLATFORM, "This method only admits OCTET_BINARY service types");
                return null;
            }

            String requestMethod = service.getRequestMethod().toString();
            if (request.getMethod() != null && request.getMethod().length() > 0)
                requestMethod = request.getMethod().toUpperCase();

            String requestUriString = formatRequestUriString(request, endpoint, requestMethod);
            Thread timeoutThread = null;

            try {

                // Security - VALIDATIONS
                if (!this.applySecurityValidations(requestUriString)) {
                    return null;
                }

                // Adding HTTP Client Parameters
                this.addingHttpClientParms(request, endpoint);

                // Building Web Request to send
                HttpEntityEnclosingRequestBase httpRequest = this.buildWebRequest(request, service,
                        requestUriString, requestMethod);

                LOG.LogDebug(Module.PLATFORM, "Downloading service content");

                // Throw a new Thread to check absolute timeout
                timeoutThread = new Thread(new CheckTimeoutThread(httpRequest));
                timeoutThread.start();

                long start = System.currentTimeMillis();
                HttpResponse httpResponse = httpClient.execute(httpRequest);
                LOG.LogDebug(Module.PLATFORM,
                        "Content downloaded in " + (System.currentTimeMillis() - start) + "ms");

                // Read response and store to local filestystem
                return this.readWebResponseAndStore(httpResponse, service, storePath);

            } catch (Exception ex) {
                LOG.Log(Module.PLATFORM, "Unnandled Exception requesting service.", ex);
            } finally {
                // abort any previous timeout checking thread
                if (timeoutThread != null && timeoutThread.isAlive()) {
                    timeoutThread.interrupt();
                }
            }
        }

        LOG.LogDebug(Module.PLATFORM, "invoke service (for binary) finished");
        return null;
    }

    @Override
    public IOResponseHandle InvokeService(IORequest request, IOService service, IOResponseHandler handler) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private class CheckTimeoutThread implements Runnable {

        private final long ABSOLUTE_INVOKE_TIMEOUT = 60000; // 60 seconds
        private HttpUriRequest httpRequest = null;

        public CheckTimeoutThread(HttpUriRequest httpRequest) {
            super();
            this.httpRequest = httpRequest;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(ABSOLUTE_INVOKE_TIMEOUT);

                LOG.LogDebug(Module.PLATFORM,
                        "*** INVOKE SERVICE TIMEOUT *** Absolute timeout checking completed.");
                if (httpRequest != null) {
                    LOG.LogDebug(Module.PLATFORM, "*** INVOKE SERVICE TIMEOUT *** Aborting request...");
                    httpRequest.abort();
                }

            } catch (InterruptedException e) {
                LOG.LogDebug(Module.PLATFORM,
                        "*** INVOKE SERVICE TIMEOUT *** Absolute timeout checking interrupted.");
            }

        }
    }

    public class HttpAppverse extends HttpEntityEnclosingRequestBase {

        private String method = null;

        public HttpAppverse(final URI uri, String method) {
            this.setURI(uri);
            this.method = method;
        }

        @Override
        public String getMethod() {
            return method;
        }
    }

    public class MySSLSocketFactory extends SSLSocketFactory {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
                KeyStoreException, UnrecoverableKeyException {
            super(truststore);

            TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Accepting all certificates");
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            sslContext.init(null, new TrustManager[] { tm }, null);
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
                throws IOException, UnknownHostException {
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        }

        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    }

    public static class ValidatingSSLSocketFactory extends SSLSocketFactory {
        protected static SSLContext sslContext;
        protected static Map<Integer, Long> myCertificateList;
        protected static ValidatingSSLSocketFactory singletonFactory;
        public String requestHostUri = null;

        public static ValidatingSSLSocketFactory GetInstance(KeyStore truststore) {
            if (singletonFactory == null) {
                try {
                    singletonFactory = new ValidatingSSLSocketFactory(truststore);
                } catch (KeyManagementException e) {
                    // TODO Auto-generated catch block
                    return null;
                } catch (NoSuchAlgorithmException e) {
                    // TODO Auto-generated catch block
                    return null;
                } catch (KeyStoreException e) {
                    // TODO Auto-generated catch block
                    return null;
                } catch (UnrecoverableKeyException e) {
                    // TODO Auto-generated catch block
                    return null;
                }
            }
            return singletonFactory;
        }

        private ValidatingSSLSocketFactory(final KeyStore truststore) throws NoSuchAlgorithmException,
                KeyManagementException, KeyStoreException, UnrecoverableKeyException {
            super(truststore);
            sslContext = SSLContext.getInstance("TLS");
            myCertificateList = new HashMap<Integer, Long>();

            Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());

            TrustManager tm = new X509TrustManager() {

                private char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
                        'E', 'F' };

                public X509Certificate[] getAcceptedIssuers() {
                    try {
                        X509Certificate[] returnIssuers = new X509Certificate[truststore.size()];
                        Enumeration aliases = truststore.aliases();

                        int i = 0;
                        while (aliases.hasMoreElements()) {
                            returnIssuers[i] = (X509Certificate) truststore
                                    .getCertificate((String) aliases.nextElement());
                            //LOG.LogDebug(Module.PLATFORM, "TRUSTED CERT " + i + ": NAME " + returnIssuers[i].getSubjectDN().getName() + " ;ISSUER NAME: " + returnIssuers[i].getIssuerDN().getName());
                            i++;
                        }
                        return returnIssuers;
                    } catch (Exception ex) {
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Couldnt retrieve Accepted Issuers");
                    }
                    return null;

                }

                public void checkClientTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {

                    boolean bErrorsFound = false;
                    try {
                        LOG.LogDebug(Module.PLATFORM,
                                "Certificate Validation: Starting Certificate Validation process");
                        if (chain != null && chain.length > 0)
                            LOG.LogDebug(Module.PLATFORM,
                                    "Certificate Validation: Certificate chain elements: " + chain.length);
                        //check the certificate is in memory
                        X509Certificate endCert = chain[0];
                        if (!certificateIsTheSame(endCert)) {
                            if (certChainIsValid(endCert, chain)) {
                                /* Checking only last certificate in the chain
                                for(int i=0; i< chain.length; i++){
                                   X509Certificate chainCert = chain[i];
                                   LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate Name: " + chainCert.getSubjectDN().getName() + " ;SN: " + chainCert.getSerialNumber().toString() );
                                       
                                   X509CertParser parser = new X509CertParser();
                                   ByteArrayInputStream bais = new ByteArrayInputStream(chainCert.getEncoded());
                                    parser.engineInit(bais);
                                    bais.close();
                                        
                                   X509CertificateObject cert = (X509CertificateObject)parser.engineRead();
                                   if(certIsValidNow(cert)){
                                      if(!certIsSelfSigned(cert)){
                                //if(certChainIsValid(cert, chain)){
                                   //all checks went OK. Add certificate to memory with current date and time
                                   //myCertificateList.put(Integer.valueOf(chain[0].hashCode()), Long.valueOf(System.currentTimeMillis()));
                                   LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is Valid");
                                //}
                                      }else{ 
                                bErrorsFound = true;
                                LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is Self Signed");
                                      }
                                   }else{ 
                                      bErrorsFound = true;
                                      LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is expired");
                                   }
                                }
                                */

                                // ThHis magic is needed to verify self-signed
                                X509CertParser parser = new X509CertParser();
                                ByteArrayInputStream bais = new ByteArrayInputStream(endCert.getEncoded());
                                parser.engineInit(bais);
                                bais.close();

                                X509CertificateObject cert = (X509CertificateObject) parser.engineRead();
                                if (certIsValidNow(cert)) {
                                    if (!certIsSelfSigned(cert)) {

                                        if (AndroidIO.ValidateFingerprints()) {
                                            LOG.LogDebug(Module.PLATFORM,
                                                    "Certificate Validation: VALIDATING FINGERPRINT");
                                            if (verifyFingerprint(endCert)) {
                                                LOG.LogDebug(Module.PLATFORM,
                                                        "Certificate Validation: Certificate Fingerprint is valid");
                                            } else {
                                                bErrorsFound = true;
                                                LOG.LogDebug(Module.PLATFORM,
                                                        "Certificate Validation: Certificate Fingerprint not valid");
                                            }
                                        } else {
                                            LOG.LogDebug(Module.PLATFORM,
                                                    "Certificate Validation: DO NOT VALIDATE FINGERPRINT");
                                        }
                                    } else {
                                        bErrorsFound = true;
                                        LOG.LogDebug(Module.PLATFORM,
                                                "Certificate Validation: Certificate is Self Signed");
                                    }
                                } else {
                                    bErrorsFound = true;
                                    LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is expired");
                                }

                                if (!bErrorsFound)
                                    myCertificateList.put(Integer.valueOf(chain[0].hashCode()),
                                            Long.valueOf(System.currentTimeMillis()));
                            } else {
                                bErrorsFound = true;
                                LOG.LogDebug(Module.PLATFORM,
                                        "Certificate Validation: Certificate Chain is not valid");
                            }
                        } else {
                            LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Trusted Certificate");
                        }

                    } catch (StreamParsingException e) {
                        bErrorsFound = true;
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate chain error");
                    } catch (IOException e) {
                        bErrorsFound = true;
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Error in certificate chain");
                    } catch (Exception e) {
                        bErrorsFound = true;
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Unhandled error: " + e.getMessage());
                    }
                    if (bErrorsFound) {
                        throw new CertificateException(
                                "Certificate Validation: Errors found in the Certificate Chain");
                    }
                }

                /**
                 * Checks the certificate fingerprint is the expected
                 * @param cert
                 *             Certificate to check
                 * @return
                 *             True if the certificate fingerprint was the expected one; false otherwise
                 */
                private boolean verifyFingerprint(X509Certificate endCert) {
                    try {
                        LOG.LogDebug(Module.PLATFORM,
                                "ValidatingSSLSocketFactory - verifyFingerprint requestHostUri: " + requestHostUri);
                        MessageDigest md;
                        md = MessageDigest.getInstance("SHA1");
                        md.update(endCert.getEncoded());
                        String thumbprint = dumpHex(md.digest());

                        String[] fingerprint = null;
                        if (FINGERPRINTS.containsKey(requestHostUri)) {
                            fingerprint = FINGERPRINTS.get(requestHostUri);
                            //LOG.LogDebug(Module.PLATFORM, "******** Certificate Validation: requestHostUri ["+ requestHostUri +"] fingerprint "+ Arrays.toString(FINGERPRINTS.get(requestHostUri)));
                        }

                        if (fingerprint != null) {
                            //LOG.LogDebug(Module.PLATFORM, "******** Certificate Validation: allowed fringerprint: " + Arrays.toString(fingerprint));
                            //LOG.LogDebug(Module.PLATFORM, "******** Certificate Validation: tocheck fringerprint: ["+thumbprint+"]");

                            if (!Arrays.asList(fingerprint).contains(thumbprint)) {

                                LOG.LogDebug(Module.PLATFORM,
                                        "Certificate Validation: WRONG CERTIFICATE FINGERPRINT");
                                return false;
                            }
                        } else {
                            LOG.LogDebug(Module.PLATFORM,
                                    "WARNING Certificate Validation: NO FINGERPRINT FOUND (you should provide a valid fingerprint in your io-services-config.xml in order to validate HTTPS web certificates)");
                            return false;
                        }
                        return true;
                    } catch (NoSuchAlgorithmException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (CertificateEncodingException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();

                    }
                    return false;

                }

                /**
                 * Get certificate fringerprint
                 * @param data
                 * @return fingerprint
                 */
                private String dumpHex(byte[] data) {
                    final int n = data.length;
                    final StringBuilder sb = new StringBuilder(n * 3 - 1);
                    for (int i = 0; i < n; i++) {
                        if (i > 0) {
                            sb.append(' ');
                        }
                        sb.append(HEX_CHARS[(data[i] >> 4) & 0x0F]);
                        sb.append(HEX_CHARS[data[i] & 0x0F]);
                    }
                    return sb.toString();
                }

                /**
                 * Check the certificate is in memory and is still valid
                 * @param cert
                 *             Certificate to check
                 * @return
                 *             True if certificate is valid and in memory, otherwise false
                 */
                private boolean certificateIsTheSame(X509Certificate cert) {
                    removeTimedOutCertificates();
                    if (!myCertificateList.isEmpty()
                            && myCertificateList.containsKey(Integer.valueOf(cert.hashCode()))
                            && certIsValidNow(cert)) {
                        return true;
                    } else {
                        return false;
                    }
                }

                /**
                 * Remove the certificates that have been accessed and visited after 10 minutes from memory
                 */
                private void removeTimedOutCertificates() {
                    long currentTimeMillis = System.currentTimeMillis();
                    Set<Integer> certsToRemove = new HashSet<Integer>();
                    if (!myCertificateList.isEmpty()) {
                        for (Map.Entry<Integer, Long> listEntry : myCertificateList.entrySet()) {
                            long certAccessTime = listEntry.getValue().longValue();
                            if (TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis - certAccessTime) >= 600) {
                                certsToRemove.add(listEntry.getKey());
                            }
                        }
                        myCertificateList.keySet().removeAll(certsToRemove);
                    }
                }

                /**
                 * Checks the Certificate Chain is well formed and certificates not revoked
                 * @param cert
                 *             End certificate the request is consuming
                 * @param chain
                 *             Certificate chain containing the rest of certificates
                 * @return
                 *             True if the Certificate chain is valid, otherwise False
                 */
                private boolean certChainIsValid(X509Certificate cert, X509Certificate[] chain) {
                    // DO NOT check OCSP revocation URLs. The time consuming this is expensive.
                    // TODO make this configurable and asynchronously in the case of enabled
                    // !verifyCertificateOCSP(chain)  ---> ASYNC
                    //if(!verifyCertificateOCSP(chain)){
                    LOG.LogDebug(Module.PLATFORM,
                            "*************** OCSP Verification (certificate revocation check) is DISABLED for this build");
                    try {
                        //Selector to point out the end certificate
                        X509CertSelector selector = new X509CertSelector();
                        selector.setCertificate(cert);

                        //Trust anchor to point out the root certificate
                        Set<TrustAnchor> trust = new HashSet<TrustAnchor>();
                        trust.add(new TrustAnchor(chain[chain.length - 1], null));

                        //Params containing the selector, trust anchors and certificate chain. We disable CRL checks
                        PKIXBuilderParameters pParams = new PKIXBuilderParameters(trust, selector);
                        pParams.setRevocationEnabled(false);
                        Set<X509Certificate> setchain = new HashSet<X509Certificate>();
                        for (X509Certificate SCCert : chain) {
                            setchain.add(SCCert);
                        }
                        CertStore allcerts = CertStore.getInstance("Collection",
                                new CollectionCertStoreParameters(setchain));
                        pParams.addCertStore(allcerts);

                        //Create the cert path result, if no exception is thrown means all went OK
                        CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
                        PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder.build(pParams);
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate Path is valid");
                        return true;
                    } catch (CertPathBuilderException e) {
                        LOG.LogDebug(Module.PLATFORM,
                                "Certificate Validation: Errors found in the Certificate Path ");
                    } catch (NoSuchAlgorithmException e) {
                        LOG.LogDebug(Module.PLATFORM,
                                "Certificate Validation: Errors found in the Certificate Path ");
                    } catch (InvalidAlgorithmParameterException e) {
                        LOG.LogDebug(Module.PLATFORM,
                                "Certificate Validation: Errors found in the Certificate Path ");
                    }
                    //}else{return false;}
                    return false;
                }

                /**
                 * Checks that all the certificates in the chain are not revoked. The checks are made via OCSP 
                 * @param chain
                 *             Certificate chain to check
                 * @return
                 *             True if any certificate is Revoked, otherwise False
                 */
                private boolean verifyCertificateOCSP(X509Certificate[] chain) {
                    ArrayList<URL> certsUrls = new ArrayList<URL>();
                    ArrayList<OCSPReq> requestList = new ArrayList<OCSPReq>();
                    boolean bCertificateIsRevoked = false;
                    try {
                        for (X509Certificate cert : chain) {
                            //Read the OCSP extension from the certificates
                            byte[] extensionBytes = cert
                                    .getExtensionValue(X509Extensions.AuthorityInfoAccess.getId());
                            if (extensionBytes != null && extensionBytes.length > 0) {

                                DEROctetString derObjAccessDescriptors = (DEROctetString) ASN1Primitive
                                        .fromByteArray(extensionBytes);
                                ASN1Primitive primitiveObject = ASN1Primitive
                                        .fromByteArray(derObjAccessDescriptors.getOctets());
                                AccessDescription[] descriptors = AuthorityInformationAccess
                                        .getInstance(primitiveObject).getAccessDescriptions();
                                //If the URL is already in the list do not add it
                                if (descriptors != null && descriptors.length > 0) {
                                    String urlContent = descriptors[0].getAccessLocation().getName().toString();
                                    if (urlContent.startsWith("http://")) {
                                        URL url = new URL(urlContent);
                                        if (!certsUrls.contains(url))
                                            certsUrls.add(url);
                                    }
                                }
                            }
                        }

                        if (!certsUrls.isEmpty()) {
                            //Create the OCSP request content for each certificate
                            OCSPReqGenerator OCSPRequestGenerator;
                            for (int i = 0; i < (chain.length - 1); i++) {
                                OCSPRequestGenerator = new OCSPReqGenerator();
                                CertificateID ID = new CertificateID(CertificateID.HASH_SHA1, chain[i + 1],
                                        chain[i].getSerialNumber());
                                OCSPRequestGenerator.addRequest(ID);
                                BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
                                Vector oids = new Vector();
                                Vector values = new Vector();
                                oids.add(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
                                values.add(new X509Extension(false, new DEROctetString(nonce.toByteArray())));
                                requestList.add(OCSPRequestGenerator.generate());
                            }

                            //Create the OCSP request and get the response
                            for (int i = 0; i < certsUrls.size() && !bCertificateIsRevoked; i++) {
                                for (int j = 0; j < requestList.size() && !bCertificateIsRevoked; j++) {
                                    HttpURLConnection requestToOCSPServer = (HttpURLConnection) certsUrls.get(i)
                                            .openConnection();
                                    requestToOCSPServer.setRequestProperty("Content-Type",
                                            "application/ocsp-request");
                                    requestToOCSPServer.setRequestProperty("Accept", "application/ocsp-response");
                                    requestToOCSPServer.setDoOutput(true);
                                    requestToOCSPServer.setReadTimeout(DEFAULT_RESPONSE_TIMEOUT);
                                    requestToOCSPServer.setConnectTimeout(DEFAULT_READWRITE_TIMEOUT);

                                    byte[] bRequestBytes = requestList.get(j).getEncoded();
                                    OutputStream out = requestToOCSPServer.getOutputStream();
                                    BufferedOutputStream buf = new BufferedOutputStream(out);
                                    DataOutputStream dataOut = new DataOutputStream(buf);
                                    dataOut.write(bRequestBytes);
                                    dataOut.flush();
                                    dataOut.close();
                                    buf.close();
                                    out.close();

                                    InputStream in = (InputStream) requestToOCSPServer.getContent();
                                    OCSPResp OCSPResponse = new OCSPResp(in);
                                    BasicOCSPResp basicOCSPResponse = (BasicOCSPResp) OCSPResponse
                                            .getResponseObject();
                                    //Check the response for certificate status
                                    if (basicOCSPResponse != null) {
                                        for (SingleResp singleResponse : basicOCSPResponse.getResponses()) {
                                            Object certStatus = singleResponse.getCertStatus();
                                            if (certStatus instanceof RevokedStatus)
                                                bCertificateIsRevoked = true;
                                        }
                                    }

                                }
                            }
                        }
                    } catch (IOException e) {
                        LOG.LogDebug(Module.PLATFORM,
                                "Certificate Validation: Errors found in the Certificate OCSP Validation ");
                        bCertificateIsRevoked = true;
                    } catch (OCSPException e) {
                        LOG.LogDebug(Module.PLATFORM,
                                "Certificate Validation: Errors found in the Certificate OCSP Validation");
                        bCertificateIsRevoked = true;
                    }
                    return bCertificateIsRevoked;
                }

                /**
                 * Checks the certificate is valid at the current date and time
                 * @param cert
                 *             Certificate to check
                 * @return
                 *             True if is valid, otherwise false
                 */
                private boolean certIsValidNow(X509Certificate cert) {
                    try {
                        cert.checkValidity();
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is not expired");
                        return true;
                    } catch (CertificateExpiredException e) {
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is expired");
                    } catch (CertificateNotYetValidException e) {
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is not yet valid");
                    }
                    return false;
                }

                /**
                 * Checks the certificate is self signed
                 * @param cert
                 *             Certificate to check
                 * @return
                 *             True if the certificate is self signed, otherwise False
                 */
                private boolean certIsSelfSigned(X509Certificate cert) {
                    try {
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: checking if self-signed");
                        cert.verify(cert.getPublicKey());
                        LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is self-signed");
                        for (X509Certificate trustedCert : this.getAcceptedIssuers()) {
                            //If trusted and cert have same name
                            if (trustedCert.getSubjectX500Principal().getName()
                                    .equals(cert.getIssuerX500Principal().getName())) {
                                LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Trusted self-signed found.");
                                return false;
                            }
                        }
                        /*
                        //ROOT TOKEN READ
                        List<String> rootAuth = null;
                        boolean bLookForRoots = false;
                        if(!_VALIDROOTAUTHORITIES.isEmpty() ){
                           LOG.LogDebug(Module.PLATFORM, "Certificate Validation: List of Root Authorities Found");
                           bLookForRoots = true;
                           if(_VALIDROOTAUTHORITIES.indexOf(';')!=-1){
                              rootAuth = Arrays.asList(_VALIDROOTAUTHORITIES.split("/\\;/g"));
                              LOG.LogDebug(Module.PLATFORM, "Certificate Validation: List of Root Authorities Found : " + rootAuth.get(0));
                           }else 
                           {
                              rootAuth = Arrays.asList(new String[]{_VALIDROOTAUTHORITIES});
                              LOG.LogDebug(Module.PLATFORM, "Certificate Validation: List of Root Authorities Found : " + rootAuth.get(0));
                           }
                        }
                        /////////////
                            
                        // LOOK FOR TRUSTED ROOT CERTS AND TRUSTED ROOT ISSUER MUST CONTAIN A VALID NAME
                        boolean bRootFound = false;
                        for(X509Certificate trustedCert : this.getAcceptedIssuers()){
                           //If trusted and cert have same name
                           if(trustedCert.getSubjectX500Principal().getName().equals(cert.getIssuerX500Principal().getName())){
                              //if we have a list of valid Root Authorities
                              if(bLookForRoots){
                                 //If list is filled
                                 if(rootAuth!= null){
                          LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Checking Valid Root Authorities");
                          for(String caRootName : rootAuth){
                             caRootName = caRootName.replace(';', ' ').trim().toLowerCase();
                             if(!caRootName.isEmpty() && trustedCert.getIssuerX500Principal().getName().toLowerCase().contains(caRootName)) {
                                bRootFound = true;
                                LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Valid Authority found");
                                //Found a match, do not look for more
                                break;
                             }
                          }
                                 }else{
                          //SOMETHING WEIRD. CANNOT LOOK FOR AUTHORITIES AND NOT HAVING AUTHORITIES
                          bRootFound = false;
                          LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Inconsistency. Cannot look for Roots and not having a list");
                          //END THE LOOP AND GIVE IT AS A BAD ATTEMPT
                          break;
                                 }
                              }else{
                                 //NOT LOOKING FOR AUTHORITIES, WE FOUND A MATCH WITH NAMES
                                 bRootFound = true;
                                 LOG.LogDebug(Module.PLATFORM, "Certificate Validation: All authorities Valid");
                              }
                                  
                              if(bRootFound){
                                 LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Trusted Certificate found");
                                 return false;
                              }
                           }
                        }
                        /*////////////////

                        return true;
                    } catch (InvalidKeyException e) {
                    } catch (CertificateException e) {
                    } catch (NoSuchAlgorithmException e) {
                    } catch (NoSuchProviderException e) {
                    } catch (SignatureException e) {
                    }
                    LOG.LogDebug(Module.PLATFORM, "Certificate Validation: Certificate is not self signed");
                    return false;
                }

                /* NOT TO USE
                 * Reason: Takes too much time depending the CRL file. 1Mb Crl file download in 15 secs
                     
                private boolean verifyCertificateCRLs(X509Certificate cert){
                   boolean bContinueValidating = true;
                   try{
                 List<String> crlDistPoints = getCrlDistributionPoints(cert);
                 if(crlDistPoints.size()>0){
                     crlDistPoints.get(0);
                     for (int i =0; i< crlDistPoints.size() && bContinueValidating;i++) {
                        String crlDP = crlDistPoints.get(i);
                         X509CRL crl = downloadCRL(crlDP);
                         if (crl!= null && crl.isRevoked(cert)) {
                            bContinueValidating = false;
                             LOG.LogDebug(Module.PLATFORM, "Certificate " + cert.getSubjectX500Principal().getName() + " is revoked.");
                         }
                     }
                 }
                  return bContinueValidating;
                   }catch(Exception e){
                 e.printStackTrace();
                   }
                   return false;
                }
                    
                    
                private X509CRL downloadCRL(String crlURL) throws IOException,CertificateException, CRLException, NamingException {
                    if (crlURL.startsWith("http://") || crlURL.startsWith("https://") || crlURL.startsWith("ftp://")) {
                  LOG.LogDebug(Module.PLATFORM, "Downloading CRL from : " + crlURL);
                   X509CRL crl = downloadCRLFromWeb(crlURL);
                   return crl;
                    }
                    return null;
                }
                    
                private X509CRL downloadCRLFromWeb(String crlURL) throws MalformedURLException, IOException, CertificateException,CRLException {
                    URL url = new URL(crlURL);
                    InputStream crlStream = null;
                    try {
                  crlStream = url.openStream();
                   CertificateFactory cf = CertificateFactory.getInstance("X.509");
                   X509CRL crl = (X509CRL) cf.generateCRL(crlStream);
                   return crl;
                    } finally {
                   if(crlStream != null) crlStream.close();
                    }
                }
                    
                private List<String> getCrlDistributionPoints(X509Certificate cert) throws IOException{
                    byte[] crldpExt = cert.getExtensionValue(X509Extensions.CRLDistributionPoints.getId());
                    if (crldpExt == null) {
                   List<String> emptyList = new ArrayList<String>();
                   return emptyList;
                    }
                    DEROctetString derObjCrlDP = (DEROctetString)ASN1Primitive.fromByteArray(crldpExt);
                    ASN1Primitive pp = ASN1Primitive.fromByteArray(derObjCrlDP.getOctets());
                    CRLDistPoint distPoint = CRLDistPoint.getInstance(pp);
                    List<String> crlUrls = new ArrayList<String>();
                    for (DistributionPoint dp : distPoint.getDistributionPoints()) {
                   DistributionPointName dpn = dp.getDistributionPoint();
                   // Look for URIs in fullName
                   if (dpn != null) {
                       if (dpn.getType() == DistributionPointName.FULL_NAME) {
                           GeneralName[] genNames = GeneralNames.getInstance(dpn.getName()).getNames();
                           // Look for an URI
                           for (int j = 0; j < genNames.length; j++) {
                               if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) {
                                   String url = DERIA5String.getInstance(genNames[j].getName()).getString();
                                   crlUrls.add(url);
                               }
                           }
                       }
                   }
                    }
                    return crlUrls;
                }
                 */

            };

            sslContext.init(null, new TrustManager[] { tm }, null);
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
                throws IOException, UnknownHostException {
            requestHostUri = host;
            LOG.LogDebug(Module.PLATFORM,
                    "ValidatingSSLSocketFactory - Create Socket for host requestHostUri: " + requestHostUri);
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        }

        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    }

    /**
     * This response interceptor is used to inflate GZIp responses
     * @author maps
     * [MOBPLAT-200]: allow gzip, deflate decompression modes
     */
    private class GzipHttpResponseInterceptor implements HttpResponseInterceptor {

        @Override
        public void process(final HttpResponse response, final HttpContext context) {
            final HttpEntity entity = response.getEntity();
            final Header encoding = entity.getContentEncoding();
            if (encoding != null) {
                LOG.LogDebug(Module.PLATFORM, "Response has content-enconding headers #"
                        + ((encoding.getElements() != null) ? encoding.getElements().length : "NULL"));
                inflateGzip(response, encoding);
            }
        }

        private void inflateGzip(final HttpResponse response, final Header encoding) {
            for (HeaderElement element : encoding.getElements()) {
                if (element.getName().equalsIgnoreCase("gzip")) {
                    LOG.LogDebug(Module.PLATFORM, "Response GZIP Encoding found. Inflating response content...");
                    response.setEntity(new GzipInflatingEntity(response.getEntity()));
                    break;
                }
            }
        }
    }

    /**
     * Inflated Response Entity
     * @author maps
     * [MOBPLAT-200]: allow gzip, deflate decompression modes
     */
    private class GzipInflatingEntity extends HttpEntityWrapper {
        public GzipInflatingEntity(final HttpEntity wrapped) {
            super(wrapped);
        }

        @Override
        public InputStream getContent() throws IOException {
            LOG.LogDebug(Module.PLATFORM, "Returning response entity as GZIPInputStream");
            return new GZIPInputStream(wrappedEntity.getContent());
        }

        @Override
        public long getContentLength() {
            return -1;
        }
    }

    @Override
    public void run() {
        if (this.Validatecertificates()) {
            try {
                createHttpClients();
            } catch (KeyManagementException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (UnrecoverableKeyException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (CertificateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (KeyStoreException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    public void createHttpClients() throws NoSuchAlgorithmException, CertificateException, IOException,
            KeyStoreException, KeyManagementException, UnrecoverableKeyException {

        SSLSocketFactory socketFactory;
        SchemeRegistry registry = new SchemeRegistry();

        LOG.LogDebug(Module.PLATFORM, "Certificate Validation Enabled = " + this.Validatecertificates());

        if (this.Validatecertificates()) {
            HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
            // Set verifier
            HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

            /******************************** 
             * USING DEFAULT ANDROID DEVICE SSLSocketFactory
             * the default factory was throwing errors verifying ssl certificates chains for some specific CA Authorities
             * (for example, Verisign root ceritificate G5 is not available on android devices <=2.3)
             * See more details on jira ticket [MOBPLAT-63]
             ******************************** 
            SSLSocketFactory socketFactory = SSLSocketFactory
                  .getSocketFactory();
            socketFactory
                  .setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
            */

            /*
              /******************************** 
             * USING VALIDATING SSLSocketFactory - Validating certificates per demand
             * See more details on jira ticket [MOBPLAT-63]
             ********************************
             */
            KeyStore trustStore;
            if (Build.VERSION.SDK_INT >= 14) {
                trustStore = KeyStore.getInstance("AndroidCAStore");
                trustStore.load(null, null);
            } else {
                try {
                    trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                    ;
                    String filename = "/system/etc/security/cacerts.bks".replace('/', File.separatorChar);
                    FileInputStream is = new FileInputStream(filename);
                    trustStore.load(is, "changeit".toCharArray());
                    is.close();
                } catch (Exception ex) {
                    try {
                        /*
                          /******************************** 
                         * HTC 2.3.5 Access Keystore problem
                         * See more details on jira ticket [MOBPLAT-91]
                         ********************************
                         */
                        trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                        String filename = "/system/etc/security/cacerts.bks".replace('/', File.separatorChar);
                        FileInputStream is = new FileInputStream(filename);
                        trustStore.load(is, null);
                        is.close();
                    } catch (Exception e) {
                        trustStore = null;
                        LOG.Log(Module.PLATFORM, "A problem has been detected while accessing the device keystore.",
                                e);
                    }
                }
            }
            socketFactory = ValidatingSSLSocketFactory.GetInstance(trustStore);
            socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);

            LOG.LogDebug(Module.PLATFORM, "Using ValidatingSSLSocketFactory (custom socket Factory)");

        } else {
            /*
             * ******************************* 
             * USING CUSTOM SSLSocketFactory - accept all certificates
             * See more details on jira ticket [MOBPLAT-63]
             ********************************
            */
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            socketFactory = new MySSLSocketFactory(trustStore);

            LOG.LogDebug(Module.PLATFORM,
                    "Using MySSLSocketFactory (custom socket factory - accepting all certificates)");
        }

        registry.register(new Scheme("https", socketFactory, 443));
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(new DefaultHttpClient().getParams(),
                registry);
        httpSSLClient = new DefaultHttpClient(mgr, new DefaultHttpClient().getParams());

        // [MOBPLAT-200] : allow gzip, deflate decompression modes
        httpSSLClient.addResponseInterceptor(new GzipHttpResponseInterceptor());

        LOG.LogDebug(Module.PLATFORM, "httpSSLClient stored for next HTTPS access");

    }

}