diff --git a/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt b/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt new file mode 100644 index 000000000000..6df00bf4e23b --- /dev/null +++ b/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt @@ -0,0 +1,69 @@ +package okhttp.android.test + +import android.os.StrictMode +import android.os.StrictMode.ThreadPolicy +import android.os.strictmode.Violation +import assertk.assertThat +import assertk.assertions.hasSize +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.internal.platform.Platform +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.Isolated + +@Isolated +class StrictModeTest { + private val violations = mutableListOf() + + @AfterEach + fun cleanup() { + StrictMode.setThreadPolicy( + ThreadPolicy.Builder() + .permitAll() + .build(), + ) + } + + @Test + fun testInit() { + Platform.resetForTests() + + applyStrictMode() + + // Not currently safe + // See https://github.com/square/okhttp/pull/8248 + OkHttpClient() + + assertThat(violations).hasSize(1) + assertThat(violations[0].message).isEqualTo("newSSLContext") + } + + @Test + fun testNewCall() { + Platform.resetForTests() + + val client = OkHttpClient() + + applyStrictMode() + + // Safe on main + client.newCall(Request("https://google.com/robots.txt".toHttpUrl())) + + assertThat(violations).isEmpty() + } + + private fun applyStrictMode() { + StrictMode.setThreadPolicy( + ThreadPolicy.Builder() + .detectCustomSlowCalls() + .penaltyListener({ it.run() }) { + violations.add(it) + } + .build(), + ) + } +} diff --git a/okhttp/src/main/kotlin/okhttp3/internal/platform/Android10Platform.kt b/okhttp/src/main/kotlin/okhttp3/internal/platform/Android10Platform.kt index 02df7e3fdfde..ea31d548fcec 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/platform/Android10Platform.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/platform/Android10Platform.kt @@ -17,8 +17,10 @@ package okhttp3.internal.platform import android.annotation.SuppressLint import android.os.Build +import android.os.StrictMode import android.security.NetworkSecurityPolicy import android.util.CloseGuard +import javax.net.ssl.SSLContext import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -31,6 +33,7 @@ import okhttp3.internal.platform.android.BouncyCastleSocketAdapter import okhttp3.internal.platform.android.ConscryptSocketAdapter import okhttp3.internal.platform.android.DeferredSocketAdapter import okhttp3.internal.tls.CertificateChainCleaner +import okhttp3.internal.tls.TrustRootIndex /** Android 10+ (API 29+). */ @SuppressSignatureCheck @@ -48,6 +51,18 @@ class Android10Platform : Platform() { socketAdapters.find { it.matchesSocketFactory(sslSocketFactory) } ?.trustManager(sslSocketFactory) + override fun newSSLContext(): SSLContext { + StrictMode.noteSlowCall("newSSLContext") + + return super.newSSLContext() + } + + override fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex { + StrictMode.noteSlowCall("buildTrustRootIndex") + + return super.buildTrustRootIndex(trustManager) + } + override fun configureTlsExtensions( sslSocket: SSLSocket, hostname: String?, diff --git a/okhttp/src/main/kotlin/okhttp3/internal/platform/AndroidPlatform.kt b/okhttp/src/main/kotlin/okhttp3/internal/platform/AndroidPlatform.kt index 8a6f67a8670f..1d4e007530ba 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/platform/AndroidPlatform.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/platform/AndroidPlatform.kt @@ -16,6 +16,7 @@ package okhttp3.internal.platform import android.os.Build +import android.os.StrictMode import android.security.NetworkSecurityPolicy import java.io.IOException import java.lang.reflect.InvocationTargetException @@ -24,6 +25,7 @@ import java.net.InetSocketAddress import java.net.Socket import java.security.cert.TrustAnchor import java.security.cert.X509Certificate +import javax.net.ssl.SSLContext import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -70,6 +72,12 @@ class AndroidPlatform : Platform() { } } + override fun newSSLContext(): SSLContext { + StrictMode.noteSlowCall("newSSLContext") + + return super.newSSLContext() + } + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = socketAdapters.find { it.matchesSocketFactory(sslSocketFactory) } ?.trustManager(sslSocketFactory) @@ -100,6 +108,8 @@ class AndroidPlatform : Platform() { override fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex = try { + StrictMode.noteSlowCall("buildTrustRootIndex") + // From org.conscrypt.TrustManagerImpl, we want the method with this signature: // private TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate lastCert); val method =