conditional cronet

This commit is contained in:
mertalev
2026-01-17 02:31:35 -05:00
parent d83e74dc88
commit e739aa1667
7 changed files with 83 additions and 21 deletions
+1
View File
@@ -109,6 +109,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation "com.google.net.cronet:cronet-okhttp:0.1.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation "androidx.work:work-runtime-ktx:$work_version"
implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
@@ -19,6 +19,9 @@ object SSLConfig {
var trustManager: X509TrustManager? = null
private set
var requiresCustomSSL: Boolean = false
private set
private val listeners = mutableListOf<() -> Unit>()
private var configHash: Int = 0
@@ -34,7 +37,8 @@ object SSLConfig {
clientCertHash: Int
) {
val newHash = computeHash(allowSelfSigned, serverHost, clientCertHash)
if (newHash == configHash && sslSocketFactory != null) {
val newRequiresCustomSSL = allowSelfSigned || keyManagers != null
if (newHash == configHash && sslSocketFactory != null && requiresCustomSSL == newRequiresCustomSSL) {
return // Config unchanged, skip
}
@@ -43,6 +47,7 @@ object SSLConfig {
sslSocketFactory = sslContext.socketFactory
trustManager = trustManagers?.filterIsInstance<X509TrustManager>()?.firstOrNull()
?: getDefaultTrustManager()
requiresCustomSSL = newRequiresCustomSSL
configHash = newHash
notifyListeners()
}
@@ -8,6 +8,7 @@ import android.graphics.ImageDecoder
import android.os.Build
import android.os.CancellationSignal
import app.alextran.immich.core.SSLConfig
import com.google.net.cronet.okhttptransport.CronetCallFactory
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
@@ -16,6 +17,7 @@ import okhttp3.Response
import okhttp3.Cache
import okhttp3.ConnectionPool
import okhttp3.Dispatcher
import org.chromium.net.CronetEngine
import java.io.File
import java.io.IOException
import java.nio.ByteBuffer
@@ -33,6 +35,7 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
private val lockedBitmaps = ConcurrentHashMap<Long, Bitmap>()
init {
appContext = context.applicationContext
cacheDir = context.cacheDir
client = buildClient()
}
@@ -47,8 +50,10 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
private val decodePool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2 + 1)
private var appContext: Context? = null
private var cacheDir: File? = null
private var client: OkHttpClient? = null
private var client: Call.Factory? = null
private var cronetEngine: CronetEngine? = null
init {
System.loadLibrary("native_buffer")
@@ -62,15 +67,42 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
external fun unlockBitmapPixels(bitmap: Bitmap)
private fun invalidateClient() {
client?.let {
(client as? OkHttpClient)?.let {
it.dispatcher.cancelAll()
it.connectionPool.evictAll()
it.cache?.close()
}
cronetEngine?.shutdown()
cronetEngine = null
client = buildClient()
}
private fun buildClient(): OkHttpClient {
private fun buildClient(): Call.Factory {
val dir = cacheDir ?: throw IllegalStateException("Cache dir not set")
return if (SSLConfig.requiresCustomSSL) {
buildOkHttpClient(dir)
} else {
buildCronetClient(dir)
}
}
private fun buildCronetClient(cacheDir: File): Call.Factory {
val ctx = appContext ?: throw IllegalStateException("Context not set")
val storageDir = File(cacheDir, "cronet").apply { mkdirs() }
val engine = CronetEngine.Builder(ctx)
.enableHttp2(true)
.enableQuic(true)
.enableBrotli(true)
.setStoragePath(storageDir.absolutePath)
.build()
.also { cronetEngine = it }
return CronetCallFactory.newBuilder(engine).build()
}
private fun buildOkHttpClient(cacheDir: File): OkHttpClient {
val dir = File(cacheDir, "okhttp")
val connectionPool = ConnectionPool(
maxIdleConnections = KEEP_ALIVE_CONNECTIONS,
keepAliveDuration = KEEP_ALIVE_DURATION_MINUTES,
@@ -81,10 +113,7 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
.dispatcher(Dispatcher().apply { maxRequestsPerHost = MAX_REQUESTS_PER_HOST })
.connectionPool(connectionPool)
cacheDir?.let { dir ->
val cacheSubdir = File(dir, "thumbnails")
builder.cache(Cache(cacheSubdir, CACHE_SIZE_BYTES))
}
builder.cache(Cache((File(dir, "thumbnails")), CACHE_SIZE_BYTES))
val sslSocketFactory = SSLConfig.sslSocketFactory
val trustManager = SSLConfig.trustManager