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) { class LocalPatchBundle(name: String, id: Int, directory: File) : PatchBundleSource(name, id, directory) {
suspend fun replace(patches: InputStream? = null, integrations: InputStream? = null) { suspend fun replace(patches: InputStream? = null, integrations: InputStream? = null) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
patches?.let { patches?.let { inputStream ->
Files.copy(it, patchesFile.toPath(), StandardCopyOption.REPLACE_EXISTING) patchBundleOutputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
} }
integrations?.let { integrations?.let {
Files.copy(it, this@LocalPatchBundle.integrationsFile.toPath(), StandardCopyOption.REPLACE_EXISTING) 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.asStateFlow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import java.io.File import java.io.File
import java.io.OutputStream
/** /**
* A [PatchBundle] source. * A [PatchBundle] source.
@ -23,6 +24,16 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
*/ */
fun hasInstalled() = patchesFile.exists() 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 { private fun load(): State {
if (!hasInstalled()) return State.Missing if (!hasInstalled()) return State.Missing
@ -40,7 +51,7 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
sealed interface State { sealed interface State {
fun patchBundleOrNull(): PatchBundle? = null fun patchBundleOrNull(): PatchBundle? = null
object Missing : State data object Missing : State
data class Failed(val throwable: Throwable) : State data class Failed(val throwable: Throwable) : State
data class Loaded(val bundle: PatchBundle) : State { data class Loaded(val bundle: PatchBundle) : State {
override fun patchBundleOrNull() = bundle 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) { private suspend fun download(info: BundleInfo) = withContext(Dispatchers.IO) {
val (patches, integrations) = info val (patches, integrations) = info
coroutineScope { coroutineScope {
mapOf( launch {
patches.url to patchesFile, patchBundleOutputStream().use {
integrations.url to integrationsFile http.streamTo(it) {
).forEach { (asset, file) -> url(patches.url)
launch {
http.download(file) {
url(asset)
} }
} }
} }
launch {
http.download(integrationsFile) {
url(integrations.url)
}
}
} }
saveVersion(patches.version, integrations.version) 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.isNotEmpty
import io.ktor.utils.io.core.readBytes import io.ktor.utils.io.core.readBytes
import it.skrape.core.htmlDocument import it.skrape.core.htmlDocument
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import java.io.OutputStream
/** /**
* @author Aliucord Authors, DiamondMiner88 * @author Aliucord Authors, DiamondMiner88
@ -49,7 +52,10 @@ class HttpService(
null 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)) APIResponse.Error(APIError(response.status, body))
} }
} catch (t: Throwable) { } catch (t: Throwable) {
@ -59,20 +65,19 @@ class HttpService(
return response return response
} }
suspend fun download( suspend fun streamTo(
saveLocation: File, outputStream: OutputStream,
builder: HttpRequestBuilder.() -> Unit builder: HttpRequestBuilder.() -> Unit
) { ) {
http.prepareGet(builder).execute { httpResponse -> http.prepareGet(builder).execute { httpResponse ->
if (httpResponse.status.isSuccess()) { if (httpResponse.status.isSuccess()) {
val channel: ByteReadChannel = httpResponse.body()
saveLocation.outputStream().use { stream -> withContext(Dispatchers.IO) {
val channel: ByteReadChannel = httpResponse.body()
while (!channel.isClosedForRead) { while (!channel.isClosedForRead) {
val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong()) val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
while (packet.isNotEmpty) { while (packet.isNotEmpty) {
val bytes = packet.readBytes() 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( suspend fun getHtml(builder: HttpRequestBuilder.() -> Unit) = htmlDocument(
html = http.get(builder).bodyAsText() html = http.get(builder).bodyAsText()
) )