fix: bundles not loading on Android 14

This commit is contained in:
Ax333l 2023-10-20 18:49:44 +02:00
parent 4b12ae1531
commit 18cfb56b45
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
4 changed files with 43 additions and 17 deletions

View File

@ -10,8 +10,10 @@ import java.nio.file.StandardCopyOption
class LocalPatchBundle(name: String, id: Int, directory: File) : PatchBundleSource(name, id, directory) {
suspend fun replace(patches: InputStream? = null, integrations: InputStream? = null) {
withContext(Dispatchers.IO) {
patches?.let {
Files.copy(it, patchesFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
patches?.let { inputStream ->
patchBundleOutputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
integrations?.let {
Files.copy(it, this@LocalPatchBundle.integrationsFile.toPath(), StandardCopyOption.REPLACE_EXISTING)

View File

@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flowOf
import java.io.File
import java.io.OutputStream
/**
* A [PatchBundle] source.
@ -23,6 +24,16 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
*/
fun hasInstalled() = patchesFile.exists()
protected fun patchBundleOutputStream(): OutputStream = with(patchesFile) {
// Android 14+ requires dex containers to be readonly.
try {
setWritable(true, true)
outputStream()
} finally {
setReadOnly()
}
}
private fun load(): State {
if (!hasInstalled()) return State.Missing
@ -40,7 +51,7 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
sealed interface State {
fun patchBundleOrNull(): PatchBundle? = null
object Missing : State
data object Missing : State
data class Failed(val throwable: Throwable) : State
data class Loaded(val bundle: PatchBundle) : State {
override fun patchBundleOrNull() = bundle

View File

@ -33,16 +33,19 @@ sealed class RemotePatchBundle(name: String, id: Int, directory: File, val endpo
private suspend fun download(info: BundleInfo) = withContext(Dispatchers.IO) {
val (patches, integrations) = info
coroutineScope {
mapOf(
patches.url to patchesFile,
integrations.url to integrationsFile
).forEach { (asset, file) ->
launch {
http.download(file) {
url(asset)
launch {
patchBundleOutputStream().use {
http.streamTo(it) {
url(patches.url)
}
}
}
launch {
http.download(integrationsFile) {
url(integrations.url)
}
}
}
saveVersion(patches.version, integrations.version)

View File

@ -18,8 +18,11 @@ import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.isNotEmpty
import io.ktor.utils.io.core.readBytes
import it.skrape.core.htmlDocument
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import java.io.File
import java.io.OutputStream
/**
* @author Aliucord Authors, DiamondMiner88
@ -49,7 +52,10 @@ class HttpService(
null
}
Log.e(tag, "Failed to fetch: API error, http status: ${response.status}, body: $body")
Log.e(
tag,
"Failed to fetch: API error, http status: ${response.status}, body: $body"
)
APIResponse.Error(APIError(response.status, body))
}
} catch (t: Throwable) {
@ -59,20 +65,19 @@ class HttpService(
return response
}
suspend fun download(
saveLocation: File,
suspend fun streamTo(
outputStream: OutputStream,
builder: HttpRequestBuilder.() -> Unit
) {
http.prepareGet(builder).execute { httpResponse ->
if (httpResponse.status.isSuccess()) {
saveLocation.outputStream().use { stream ->
val channel: ByteReadChannel = httpResponse.body()
val channel: ByteReadChannel = httpResponse.body()
withContext(Dispatchers.IO) {
while (!channel.isClosedForRead) {
val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
while (packet.isNotEmpty) {
val bytes = packet.readBytes()
stream.write(bytes)
outputStream.write(bytes)
}
}
}
@ -83,6 +88,11 @@ class HttpService(
}
}
suspend fun download(
saveLocation: File,
builder: HttpRequestBuilder.() -> Unit
) = saveLocation.outputStream().use { streamTo(it, builder) }
suspend fun getHtml(builder: HttpRequestBuilder.() -> Unit) = htmlDocument(
html = http.get(builder).bodyAsText()
)