diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt index 4f7500051..f959a380a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt @@ -8,10 +8,8 @@ import androidx.lifecycle.MutableLiveData import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.ForegroundTracker import com.topjohnwu.magisk.core.base.BaseService -import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.core.utils.ProgressInputStream import com.topjohnwu.magisk.data.repository.NetworkService -import com.topjohnwu.magisk.ktx.withStreams import com.topjohnwu.magisk.view.Notifications import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -72,11 +70,7 @@ abstract class BaseDownloader : BaseService(), KoinComponent { when (this) { is Subject.Module -> // Download and process on-the-fly stream.toModule(file, service.fetchInstaller().byteStream()) - else -> { - withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) } - if (this is Subject.Manager) - handleAPK(this) - } + is Subject.Manager -> handleAPK(this, stream) } val newId = notifyFinish(this) if (ForegroundTracker.hasForeground) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerHandler.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerHandler.kt index eb5168504..167986e90 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerHandler.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerHandler.kt @@ -7,9 +7,13 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.tasks.HideAPK +import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.ktx.relaunchApp +import com.topjohnwu.magisk.ktx.withStreams import com.topjohnwu.magisk.ktx.writeTo import java.io.File +import java.io.InputStream +import java.io.OutputStream private fun Context.patch(apk: File) { val patched = File(apk.parent, "patched.apk") @@ -26,22 +30,46 @@ private fun BaseDownloader.notifyHide(id: Int) { } } -suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) { - if (!isRunningAsStub) - return - val apk = subject.file.toFile() - val id = subject.notifyID() - // Move to upgrade location - apk.copyTo(DynAPK.update(this), overwrite = true) - apk.delete() - if (Info.stub!!.version < subject.stub.versionCode) { - notifyHide(id) - // Also upgrade stub - service.fetchFile(subject.stub.link).byteStream().writeTo(apk) - patch(apk) - } else { - // Simply relaunch the app - stopSelf() - relaunchApp(this) +private class DupOutputStream( + private val o1: OutputStream, + private val o2: OutputStream +) : OutputStream() { + override fun write(b: Int) { + o1.write(b) + o2.write(b) + } + override fun write(b: ByteArray?, off: Int, len: Int) { + o1.write(b, off, len) + o2.write(b, off, len) + } + override fun close() { + o1.close() + o2.close() + } +} + +suspend fun BaseDownloader.handleAPK(subject: Subject.Manager, stream: InputStream) { + fun write(output: OutputStream) { + val ext = subject.externalFile.outputStream() + val o = DupOutputStream(ext, output) + withStreams(stream, o) { src, out -> src.copyTo(out) } + } + + if (isRunningAsStub) { + val apk = subject.file.toFile() + val id = subject.notifyID() + write(DynAPK.update(this).outputStream()) + if (Info.stub!!.version < subject.stub.versionCode) { + // Also upgrade stub + notifyHide(id) + service.fetchFile(subject.stub.link).byteStream().writeTo(apk) + patch(apk) + } else { + // Simply relaunch the app + stopSelf() + relaunchApp(this) + } + } else { + write(subject.file.outputStream()) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/ModuleProcessor.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/ModuleProcessor.kt index 8aebc7f81..7c891fb45 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/ModuleProcessor.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/ModuleProcessor.kt @@ -1,8 +1,9 @@ package com.topjohnwu.magisk.core.download import android.net.Uri -import com.topjohnwu.magisk.ktx.withStreams import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream +import com.topjohnwu.magisk.ktx.forEach +import com.topjohnwu.magisk.ktx.withStreams import java.io.InputStream import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -25,8 +26,7 @@ fun InputStream.toModule(file: Uri, installer: InputStream) { zout.write("#MAGISK\n".toByteArray(charset("UTF-8"))) var off = -1 - var entry: ZipEntry? = zin.nextEntry - while (entry != null) { + zin.forEach { entry -> if (off < 0) { off = entry.name.indexOf('/') + 1 } @@ -38,8 +38,6 @@ fun InputStream.toModule(file: Uri, installer: InputStream) { zin.copyTo(zout) } } - - entry = zin.nextEntry } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt index b0e022a69..2dcc4b4a5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt @@ -51,6 +51,7 @@ sealed class Subject : Parcelable { cachedFile("manager.apk") } + val externalFile get() = MediaStoreUtils.getFile("$title.apk").uri } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt b/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt index 8cf123de1..e98e78b24 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt @@ -11,7 +11,7 @@ import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream -fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { +inline fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { var entry: ZipEntry? = nextEntry while (entry != null) { callback(entry) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 030718ce9..b1230c09a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -114,7 +114,7 @@ class HomeViewModel( fun onDeletePressed() = UninstallDialog().publish() fun onManagerPressed() = when (state) { - State.LOADED -> ManagerInstallDialog().publish() + State.LOADED -> withExternalRW { ManagerInstallDialog().publish() } State.LOADING -> SnackbarEvent(R.string.loading).publish() else -> SnackbarEvent(R.string.no_connection).publish() }