From d495bb6b572f86511caa67477d5022472ab46e1d Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Fri, 29 Dec 2023 16:57:27 +0530 Subject: [PATCH] Kotlinify `StatusOkHttpClientFactory.java` (#18318) This commit converts `android/app/src/main/java/im/status/ethereum/StatusOkHttpClientFactory.java` to `android/app/src/main/java/im/status/ethereum/StatusOkHttpClientFactory.kt` --- .../ethereum/StatusOKHttpClientFactory.kt | 88 +++++++++++++++++ .../ethereum/StatusOkHttpClientFactory.java | 95 ------------------- 2 files changed, 88 insertions(+), 95 deletions(-) create mode 100644 android/app/src/main/java/im/status/ethereum/StatusOKHttpClientFactory.kt delete mode 100644 android/app/src/main/java/im/status/ethereum/StatusOkHttpClientFactory.java diff --git a/android/app/src/main/java/im/status/ethereum/StatusOKHttpClientFactory.kt b/android/app/src/main/java/im/status/ethereum/StatusOKHttpClientFactory.kt new file mode 100644 index 0000000000..a8458b8d38 --- /dev/null +++ b/android/app/src/main/java/im/status/ethereum/StatusOKHttpClientFactory.kt @@ -0,0 +1,88 @@ +package im.status.ethereum + +import android.util.Log +import com.facebook.react.modules.network.OkHttpClientFactory +import com.facebook.react.modules.network.OkHttpClientProvider +import okhttp3.OkHttpClient +import okhttp3.tls.HandshakeCertificates +import java.io.ByteArrayInputStream +import java.lang.RuntimeException +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.security.cert.CertificateFactory +import java.security.cert.X509Certificate +import im.status.ethereum.module.StatusPackage + +class StatusOkHttpClientFactory : OkHttpClientFactory { + + companion object { + private const val TAG = "StatusOkHttpClientFactory" + private const val SLEEP_DURATION = 500L // milliseconds + } + + override fun createNewNetworkModuleClient(): OkHttpClient? { + val certPem = getCertificatePem().takeIf { it.isNotEmpty() } + ?: return logAndReturnNull("Certificate is empty, cannot create OkHttpClient without a valid certificate") + + val cert = try { + induceSleep() + // Convert PEM certificate string to X509Certificate object + CertificateFactory.getInstance("X.509") + .generateCertificate(ByteArrayInputStream(certPem.toByteArray())) as? X509Certificate + ?: return logAndReturnNull("Certificate could not be parsed as non-null") + } catch (e: Exception) { + return logAndReturnNull("Could not parse certificate", e) + } + + val clientCertificates = try { + induceSleep() + // Create HandshakeCertificates object with our certificate + HandshakeCertificates.Builder() + .addPlatformTrustedCertificates() + .addTrustedCertificate(cert) + .build() + } catch (e: Exception) { + return logAndReturnNull("Could not build HandshakeCertificates", e) + } + + return try { + OkHttpClientProvider.createClientBuilder() + .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager) + .build() + } catch (e: Exception) { + logAndReturnNull("Could not create OkHttpClient", e) + } + } + + private fun getCertificatePem(): String { + return try { + // Create OkHttpClient with custom SSL socket factory and trust manager + StatusPackage.getImageTLSCert().takeIf { !it.isNullOrBlank() } + ?: logAndReturnEmpty("Certificate PEM string is null or empty") + } catch (e: Exception) { + logAndReturnEmpty("Could not getImageTLSCert", e) + } + } + + private fun induceSleep() { + try { + // induce half second sleep because sometimes a cert is not immediately available + // TODO : remove sleep if App no longer crashes on Android 10 devices with + // java.lang.RuntimeException: Could not invoke WebSocketModule.connect + Thread.sleep(SLEEP_DURATION) + } catch (e: InterruptedException) { + Log.e(TAG, "Sleep interrupted", e) + } + } + + private fun logAndReturnNull(message: String, e: Exception? = null): Nothing? { + Log.e(TAG, message, e) + return null + } + + private fun logAndReturnEmpty(message: String, e: Exception? = null): String { + Log.e(TAG, message, e) + return "" + } +} + diff --git a/android/app/src/main/java/im/status/ethereum/StatusOkHttpClientFactory.java b/android/app/src/main/java/im/status/ethereum/StatusOkHttpClientFactory.java deleted file mode 100644 index 9db192e7e4..0000000000 --- a/android/app/src/main/java/im/status/ethereum/StatusOkHttpClientFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -package im.status.ethereum; - -import android.util.Log; - -import com.facebook.react.modules.network.OkHttpClientFactory; -import com.facebook.react.modules.network.OkHttpClientProvider; - -import okhttp3.OkHttpClient; -import okhttp3.Interceptor; -import okhttp3.tls.HandshakeCertificates; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.lang.RuntimeException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import im.status.ethereum.module.StatusPackage; - -class StatusOkHttpClientFactory implements OkHttpClientFactory { - - private static final String TAG = "StatusOkHttpClientFactory"; - - public OkHttpClient createNewNetworkModuleClient() { - X509Certificate cert = null; - HandshakeCertificates clientCertificates; - String certPem = ""; - // Get TLS PEM certificate from status-go - try { - // induce half second sleep because sometimes a cert is not immediately available - // TODO : remove sleep if App no longer crashes on Android 10 devices with - // java.lang.RuntimeException: Could not invoke WebSocketModule.connect - Thread.sleep(500); - certPem = getCertificatePem(); - } catch(Exception e) { - Log.e(TAG, "Could not getImageTLSCert",e); - } - - if (certPem.isEmpty()) { - Log.e(TAG, "Certificate is empty, cannot create OkHttpClient without a valid certificate"); - return null; - } - - // Convert PEM certificate string to X509Certificate object - try { - // induce half second sleep because sometimes a cert is not immediately available - // TODO : remove sleep if App no longer crashes on Android 10 devices - // java.lang.RuntimeException: Could not invoke WebSocketModule.connect - Thread.sleep(500); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certPem.getBytes())); - } catch(Exception e) { - Log.e(TAG, "Could not parse certificate",e); - } - // Create HandshakeCertificates object with our certificate - try { - // induce half second sleep because sometimes a cert is not immediately available - // TODO : remove sleep if App no longer crashes on Android 10 devices - // java.lang.RuntimeException: Could not invoke WebSocketModule.connect - Thread.sleep(500); - clientCertificates = new HandshakeCertificates.Builder() - .addPlatformTrustedCertificates() - .addTrustedCertificate(cert) - .build(); - } catch(Exception e) { - Log.e(TAG, "Could not build HandshakeCertificates", e); - return null; - } - - // Create OkHttpClient with custom SSL socket factory and trust manager - try { - return OkHttpClientProvider.createClientBuilder() - .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager()) - .build(); - } catch(Exception e) { - Log.e(TAG, "Could not create OkHttpClient", e); - return null; - } - } - private String getCertificatePem() { - try { - String certPem = StatusPackage.getImageTLSCert(); - if (certPem == null || certPem.trim().isEmpty()) { - Log.e(TAG, "Certificate PEM string is null or empty"); - return ""; - } - return certPem; - } catch (Exception e) { - Log.e(TAG, "Could not getImageTLSCert", e); - return ""; - } - } -}