1

The function is expected to loop through a directory and post each image found from an android API 28 emulator to a Flask backend server, both running locally (API call from emulator:https://10.0.2.2:5000/upload).

The android logcat:

2020-03-18 15:51:29.417 8687-8940/com.screenomics E/MainActivity: Request{method=POST, url=https://10.0.2.2:5000/upload, tags={}}
2020-03-18 15:51:29.446 8687-8940/com.screenomics W/System.err: javax.net.ssl.SSLHandshakeException: Handshake failed
2020-03-18 15:51:29.446 8687-8940/com.screenomics W/System.err:     at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:286)
2020-03-18 15:51:29.446 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:320)
2020-03-18 15:51:29.446 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:284)
2020-03-18 15:51:29.447 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:169)
2020-03-18 15:51:29.447 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
2020-03-18 15:51:29.447 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
2020-03-18 15:51:29.447 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
2020-03-18 15:51:29.447 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
2020-03-18 15:51:29.448 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
2020-03-18 15:51:29.448 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
2020-03-18 15:51:29.448 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
2020-03-18 15:51:29.448 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
2020-03-18 15:51:29.448 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
2020-03-18 15:51:29.449 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
2020-03-18 15:51:29.450 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
2020-03-18 15:51:29.450 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
2020-03-18 15:51:29.450 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
2020-03-18 15:51:29.451 8687-8940/com.screenomics W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
2020-03-18 15:51:29.451 8687-8940/com.screenomics W/System.err:     at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:264)
2020-03-18 15:51:29.451 8687-8940/com.screenomics W/System.err:     at okhttp3.RealCall.execute(RealCall.java:93)

The Flask console:

127.0.0.1 - - [18/Mar/2020 15:51:30] code 400, message Bad request version ('À')
127.0.0.1 - - [18/Mar/2020 15:51:30] "   bñ³m¸È6ÁÝw^ëñÓëzߨX<.öúHÀ+Ì©À  À
" HTTPStatus.BAD_REQUEST -

The upload function

    private class UploadImage2 extends AsyncTask<Void, Void, Void> {
        private final MediaType PNG = MediaType.parse("image/*");
        private ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
                .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
                .cipherSuites(
                        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                        CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
                        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
                .build();
        private OkHttpClient.Builder client = new OkHttpClient.Builder()
                .connectionSpecs(Collections.singletonList(spec));
        private String dirpath;

        private String post(File file) {
            RequestBody body = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(),
                            RequestBody.create(PNG, file))
                    .build();
            Request request = new Request.Builder()
                    .header("Content-Type", "multipart/form-data")
                    .url(SERVER_ADDRESS)
                    .post(body)
                    .build();
            try {
                Log.e(TAG, request.toString());
                Response response = client.build().newCall(request).execute();
                Log.e(TAG, response.toString());
                return response.body().toString();
            } catch (Exception e) {
                e.printStackTrace();
                return e.toString();
            }
        }

        UploadImage2(String dirpath) {
            this.dirpath = dirpath;
        }

        @Override
        protected void onPreExecute() {}

        @Override
        protected Void doInBackground(Void... params) {
            File dir = new File(dirpath);
            if (dir.exists()) {
                File[] files = dir.listFiles();
                if (files != null) {
                    for (File file : files) {
                        try {
                            String response = post(file);
                            Toast.makeText(getApplicationContext(), "Upload successful: " + response, Toast.LENGTH_SHORT).show();
                            Log.e(TAG, response);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                return null;
            }
            Log.e(TAG, "directory not found");
            return null;        }
    }

The error occurs on this line Response response = client.build().newCall(request).execute();

Please help! thanks!

  • Handshake exceptions can occur when the ca-certificate is missing. Can you connect through the android webView Browser to your backend-server? – J.Gerbershagen Mar 18 '20 at 09:23
  • @J.Gerbershagen yeah I can't connect to my backend server through the emulator's browser. The flask server is a basic one with `app.run(host="0.0.0.0", debug=True)`. Do I have to reinstall a certificate on the emulator? – Andre Hadianto Mar 19 '20 at 02:35
  • I seems so. Installation is described on https://stackoverflow.com/questions/4461360/how-to-install-trusted-ca-certificate-on-android-device – J.Gerbershagen Mar 19 '20 at 06:01

2 Answers2

2
Here's the insecure OkHttpClient widely used as a workaround.

Do not use it on production, it's merely for dev purposes.

    import java.security.SecureRandom;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    import okhttp3.OkHttpClient;
    
    public class Http {
    
        private final static String SSL = "SSL";
    
        private static OkHttpClient InsecureHttpClient;
    
        public static OkHttpClient client () {
            if (InsecureHttpClient == null) {
                try {
                    InsecureHttpClient = insecureOkHttpClient ();
                } catch (Exception e) {
                    e.printStackTrace ();
                }
            }
    
            return InsecureHttpClient;
        }
    
        private static OkHttpClient insecureOkHttpClient () throws Exception {
            TrustManager [] trustAllCerts       = new TrustManager [] { trustManager () };
    
            SSLContext sslContext               = SSLContext.getInstance (SSL);
            sslContext.init (null, trustAllCerts, new SecureRandom ());
    
            SSLSocketFactory sslSocketFactory   = sslContext.getSocketFactory ();
    
            OkHttpClient.Builder builder        = new OkHttpClient.Builder ();
            builder.sslSocketFactory (sslSocketFactory, (X509TrustManager)trustAllCerts [0]);
            builder.hostnameVerifier (hostnameVerifier ());
    
            return builder.build ();
        }
    
        private static TrustManager trustManager () {
            return new X509TrustManager () {
    
                @Override
                public void checkClientTrusted (X509Certificate [] chain, String authType) throws CertificateException {  }
    
                @Override
                public void checkServerTrusted (X509Certificate[] chain, String authType) throws CertificateException {  }
    
                @Override
                public X509Certificate [] getAcceptedIssuers () {
                    return new X509Certificate [] {  };
                }
            };
        }
    
        private static HostnameVerifier hostnameVerifier () {
            return new HostnameVerifier () {
    
                @Override
                public boolean verify (String hostname, SSLSession session) {
                    return true;
                }
            };
        }
    }

You then obviously use the above client like the following test code for example: (which, by the way, works with your URL)

    final Request request = new Request.Builder ()
        .url ("your URL")
        .get ()
        .addHeader ("Accept", "text/html")
        .build ();
    
    final OkHttpClient httpClient = Http.client ();
new Thread (new Runnable () {

    @Override
    public void run () {
        try {
            Response response = httpClient.newCall (request).execute ();

            Logger.error (MainActivity.class.getSimpleName () + " --> Http Response", response.body ().string ());

        } catch (IOException e) {
            e.printStackTrace ();
        }
    }

}).start ();
Gayathri
  • 249
  • 3
  • 13
Nensi Kasundra
  • 1,980
  • 6
  • 21
  • 34
  • I created a new Java class called `InsecureHttp` containing a copy of your first chunk of code. I commented out my `ConnectionSpec spec` and `OkHttpClient.Builder client` and add a new line `private OkHttpClient client = InsecureHttp.client()`. However, it still fails on the line `Response response = client.newCall(request).execute. `Logcat` still shows `SSLHandshakeException: Handshake failed` but now it adds `Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xdfad1808: Failure in SSL library, usually a protocol error`. – Andre Hadianto Mar 19 '20 at 02:27
  • please look into this it's a solution of your problem https://stackoverflow.com/a/30302235/7846071 – Nensi Kasundra Mar 19 '20 at 05:20
0

See https://github.com/square/okhttp/blob/482f88300f78c3419b04379fc26c3683c10d6a9d/samples/guide/src/main/java/okhttp3/recipes/kt/DevServer.kt#L40

  val clientCertificates = HandshakeCertificates.Builder()
      .addPlatformTrustedCertificates()
      .addInsecureHost("192.168.0.8")
      .build()

  val client = OkHttpClient.Builder()
      .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)
      .build()
Yuri Schimke
  • 12,435
  • 3
  • 35
  • 69