Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with configuring Android to use assets #8599

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions okhttp-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}

sourceSets {
getByName("main") {
assets {
srcDir("$buildDir/generated/sources/assets")
}
}
}
}

val copyPublicSuffixDatabase = tasks.register<Copy>("copyPublicSuffixDatabase") {
from(project(":okhttp").file("src/main/resources/okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz"))
into("$buildDir/generated/sources/assets/okhttp3/internal/publicsuffix")
}

dependencies {
Expand All @@ -55,6 +68,7 @@ dependencies {
debugImplementation(libs.findbugs.jsr305)
compileOnly(libs.animalsniffer.annotations)
compileOnly(libs.robolectric.android)
implementation("androidx.startup:startup-runtime:1.2.0")

testImplementation(libs.junit)
testImplementation(libs.junit.ktx)
Expand All @@ -69,6 +83,8 @@ dependencies {
androidTestImplementation(libs.assertk)
androidTestImplementation(projects.mockwebserver3Junit4)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation("androidx.test.ext:junit-ktx:1.2.1")
androidTestImplementation("androidx.test:core:1.6.1")
}

mavenPublishing {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2022 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package okhttp3.android

import assertk.assertThat
import assertk.assertions.isNotNull
import okhttp3.internal.platform.Platform
import okhttp3.internal.platform.android.AndroidContextPlatform
import okhttp3.internal.publicsuffix.PublicSuffixDatabase
import org.junit.Test

/**
* Run with "./gradlew :android-test:connectedCheck -PandroidBuild=true" and make sure ANDROID_SDK_ROOT is set.
*/
class PublicSuffixDatabaseTest {

@Test
fun testFromAsset() {
assertThat((Platform.get() as AndroidContextPlatform).context).isNotNull()

val db = PublicSuffixDatabase.get()
db.getEffectiveTldPlusOne("www.smh.com.au")
}
}
14 changes: 13 additions & 1 deletion okhttp-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="okhttp.android">
xmlns:tools="http://schemas.android.com/tools"
package="okhttp.android">

<uses-permission android:name="android.permission.INTERNET" />

<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="okhttp3.android.OkHttpStartupInitializer"
android:value="androidx.startup" />
</provider>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2024 Block, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package okhttp3.android

import android.content.Context
import androidx.startup.Initializer
import okhttp3.internal.platform.AndroidPlatform
import okhttp3.internal.platform.Platform
import okhttp3.internal.platform.android.AndroidContextPlatform

/**
* An [Initializer] that initializes the OkHttp [Platform] instance.
*
* This initializer sets the Android context for the platform if it's an [AndroidPlatform].
* This allows OkHttp to access Android-specific features like the application's cache directory.
*
* This initializer has no dependencies.
*/
class OkHttpStartupInitializer : Initializer<Platform> {
override fun create(context: Context): Platform {
val platform = Platform.get()

if (platform is AndroidContextPlatform) {
platform.setAndroidContext(context)
}

return platform
}

override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
15 changes: 15 additions & 0 deletions okhttp-android/src/main/resources/META-INF/proguard/okhttp3.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# A resource is loaded with a relative path so the package of this class must be preserved.
-keeppackagenames okhttp3.internal.publicsuffix.*
-adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz

# OkHttp platform used only on JVM and when Conscrypt and other security providers are available.
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**
4 changes: 2 additions & 2 deletions okhttp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ val copyKotlinTemplates = tasks.register<Copy>("copyKotlinTemplates") {
into("$buildDir/generated/sources/kotlinTemplates")

// Tag as an input to regenerate after an update
inputs.file("src/test/resources/okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz")
inputs.file("src/main/resources/okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz")

filteringCharset = Charsets.UTF_8.toString()

val databaseGz = project.file("src/test/resources/okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz")
val databaseGz = project.file("src/main/resources/okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz")
val listBytes = databaseGz.readBytes().toByteStringExpression()

expand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import okhttp3.Protocol
import okhttp3.internal.SuppressSignatureCheck
import okhttp3.internal.platform.android.Android10SocketAdapter
import okhttp3.internal.platform.android.AndroidCertificateChainCleaner
import okhttp3.internal.platform.android.AndroidContextPlatform
import okhttp3.internal.platform.android.AndroidSocketAdapter
import okhttp3.internal.platform.android.BouncyCastleSocketAdapter
import okhttp3.internal.platform.android.ConscryptSocketAdapter
Expand All @@ -34,7 +35,7 @@ import okhttp3.internal.tls.CertificateChainCleaner

/** Android 10+ (API 29+). */
@SuppressSignatureCheck
class Android10Platform : Platform() {
class Android10Platform : AndroidContextPlatform() {
private val socketAdapters =
listOfNotNull(
Android10SocketAdapter.buildIfSupported(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package okhttp3.internal.platform

import android.content.Context
import android.os.Build
import android.security.NetworkSecurityPolicy
import java.io.IOException
Expand All @@ -30,18 +31,34 @@ import javax.net.ssl.X509TrustManager
import okhttp3.Protocol
import okhttp3.internal.SuppressSignatureCheck
import okhttp3.internal.platform.android.AndroidCertificateChainCleaner
import okhttp3.internal.platform.android.AndroidContextPlatform
import okhttp3.internal.platform.android.AndroidSocketAdapter
import okhttp3.internal.platform.android.BouncyCastleSocketAdapter
import okhttp3.internal.platform.android.ConscryptSocketAdapter
import okhttp3.internal.platform.android.DeferredSocketAdapter
import okhttp3.internal.platform.android.StandardAndroidSocketAdapter
import okhttp3.internal.publicsuffix.PublicSuffixList
import okhttp3.internal.publicsuffix.ResourcePublicSuffixList
import okhttp3.internal.tls.BasicTrustRootIndex
import okhttp3.internal.tls.CertificateChainCleaner
import okhttp3.internal.tls.TrustRootIndex
import okio.source

/** Android 5 to 9 (API 21 to 28). */
@SuppressSignatureCheck
class AndroidPlatform : Platform() {
class AndroidPlatform : AndroidContextPlatform() {

override fun publicSuffixList(): PublicSuffixList {
val context = this.context
return if (context == null) {
super.publicSuffixList()
} else {
return ResourcePublicSuffixList {
context.assets.open("okhttp3/internal/publicsuffix/PublicSuffixDatabase").source()
}
}
}

private val socketAdapters =
listOfNotNull(
StandardAndroidSocketAdapter.buildIfSupported(),
Expand Down
5 changes: 5 additions & 0 deletions okhttp/src/main/kotlin/okhttp3/internal/platform/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import javax.net.ssl.X509TrustManager
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.internal.platform.android.AndroidLog
import okhttp3.internal.publicsuffix.EmbeddedPublicSuffixList
import okhttp3.internal.publicsuffix.PublicSuffixList
import okhttp3.internal.readFieldOrNull
import okhttp3.internal.tls.BasicCertificateChainCleaner
import okhttp3.internal.tls.BasicTrustRootIndex
Expand Down Expand Up @@ -72,9 +74,12 @@ import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
* Supported on Android 6.0+ via `NetworkSecurityPolicy`.
*/
open class Platform {

/** Prefix used on custom headers. */
fun getPrefix() = "OkHttp"

internal open fun publicSuffixList(): PublicSuffixList = EmbeddedPublicSuffixList

open fun newSSLContext(): SSLContext = SSLContext.getInstance("TLS")

open fun platformTrustManager(): X509TrustManager {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2024 Block, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.platform.android

import android.content.Context
import okhttp3.internal.platform.Platform

/**
* Platform interface for accessing the Android Context.
*
* This interface provides a way to access the Android Context from Platform implementations.
*/
abstract class AndroidContextPlatform: Platform() {
var context: Context? = null

fun setAndroidContext(context: Context) {
this.context = context
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package okhttp3.internal.publicsuffix

import java.net.IDN
import okhttp3.internal.and
import okhttp3.internal.platform.Platform
import okio.ByteString
import okio.ByteString.Companion.encodeUtf8

Expand Down Expand Up @@ -153,7 +154,7 @@ class PublicSuffixDatabase internal constructor(

private const val EXCEPTION_MARKER = '!'

private val instance = PublicSuffixDatabase(EmbeddedPublicSuffixList)
private val instance = PublicSuffixDatabase(Platform.get().publicSuffixList())

fun get(): PublicSuffixDatabase {
return instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import okio.ByteString
/**
* Basic I/O for the PublicSuffixDatabase.gz.
*/
internal interface PublicSuffixList {
interface PublicSuffixList {
fun ensureLoaded()

val bytes: ByteString
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import java.io.InterruptedIOException
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicBoolean
import okhttp3.internal.platform.Platform
import okhttp3.internal.platform.android.AndroidContextPlatform
import okio.ByteString
import okio.FileSystem
import okio.GzipSource
import okio.Path
import okio.Path.Companion.toPath
import okio.Source
import okio.buffer

internal class ResourcePublicSuffixList(
val path: Path = PUBLIC_SUFFIX_RESOURCE,
val fileSystem: FileSystem = FileSystem.RESOURCES,
class ResourcePublicSuffixList(
val sourceProvider: () -> Source,
) : PublicSuffixList {
/** True after we've attempted to read the list for the first time. */
private val listRead = AtomicBoolean(false)
Expand All @@ -29,13 +30,24 @@ internal class ResourcePublicSuffixList(
override lateinit var bytes: ByteString
override lateinit var exceptionBytes: ByteString

constructor(
path: Path = PUBLIC_SUFFIX_RESOURCE,
fileSystem: FileSystem = FileSystem.RESOURCES,
) : this({
val platform = Platform.get()
check(!Platform.isAndroid || (platform as? AndroidContextPlatform)?.context == null) {
"PublicSuffixDatabase.gz loaded from resources on Android"
}
GzipSource(fileSystem.source(path))
})

@Throws(IOException::class)
private fun readTheList() {
var publicSuffixListBytes: ByteString?
var publicSuffixExceptionListBytes: ByteString?

try {
GzipSource(fileSystem.source(path)).buffer().use { bufferedSource ->
sourceProvider().buffer().use { bufferedSource ->
val totalBytes = bufferedSource.readInt()
publicSuffixListBytes = bufferedSource.readByteString(totalBytes.toLong())

Expand Down
Loading