Always update stub APK when upgrade

This commit is contained in:
topjohnwu 2024-03-14 14:31:02 -07:00
parent 050a073771
commit c951b208a1
3 changed files with 30 additions and 67 deletions

View File

@ -13,29 +13,26 @@ import android.os.Bundle
import androidx.collection.SparseArrayCompat
import androidx.collection.isNotEmpty
import androidx.core.content.getSystemService
import androidx.core.net.toFile
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.ActivityTracker
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.JobService
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.cmp
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.intent
import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.ktx.copyAndClose
import com.topjohnwu.magisk.core.ktx.cachedFile
import com.topjohnwu.magisk.core.ktx.copyAll
import com.topjohnwu.magisk.core.ktx.copyAndClose
import com.topjohnwu.magisk.core.ktx.forEach
import com.topjohnwu.magisk.core.ktx.selfLaunchIntent
import com.topjohnwu.magisk.core.ktx.set
import com.topjohnwu.magisk.core.ktx.withStreams
import com.topjohnwu.magisk.core.ktx.writeTo
import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.core.utils.ProgressInputStream
import com.topjohnwu.magisk.utils.APKInstall
@ -46,11 +43,9 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import okhttp3.ResponseBody
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.Properties
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
@ -169,7 +164,7 @@ class DownloadEngine(
when (subject) {
is Subject.App -> handleApp(stream, subject)
is Subject.Module -> handleModule(stream, subject.file)
is Subject.Test -> stream.copyAndClose(subject.file.outputStream())
else -> stream.copyAndClose(subject.file.outputStream())
}
val activity = ActivityTracker.foreground
if (activity != null && subject.autoLaunch) {
@ -178,7 +173,6 @@ class DownloadEngine(
} else {
notifyFinish(subject)
}
subject.postDownload?.invoke()
} catch (e: IOException) {
Timber.e(e)
notifyFail(subject)
@ -271,50 +265,32 @@ class DownloadEngine(
}
private suspend fun handleApp(stream: InputStream, subject: Subject.App) {
suspend fun writeTee(output: OutputStream) {
val uri = MediaStoreUtils.getFile("${subject.title}.apk").uri
val external = uri.outputStream()
stream.copyAndClose(TeeOutputStream(external, output))
}
val external = subject.file.outputStream()
if (isRunningAsStub) {
val updateApk = StubApk.update(context)
try {
// Download full APK to stub update path
writeTee(updateApk.outputStream())
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
val zf = ZipFile(updateApk)
val prop = Properties()
prop.load(ByteArrayInputStream(zf.comment.toByteArray()))
val stubVersion = prop.getProperty("stubVersion").toIntOrNull() ?: -1
if (Info.stub!!.version < stubVersion) {
// Also upgrade stub
notifyUpdate(subject.notifyId) {
it.setProgress(0, 0, true)
.setContentTitle(context.getString(R.string.hide_app_title))
.setContentText("")
}
// Extract stub
val apk = subject.file.toFile()
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
zf.close()
// Patch and install
subject.intent = HideAPK.upgrade(context, apk)
?: throw IOException("HideAPK patch error")
apk.delete()
} else {
ActivityTracker.foreground?.let {
// Relaunch the process if we are foreground
StubApk.restartProcess(it)
} ?: run {
// Or else kill the current process after posting notification
subject.intent = context.selfLaunchIntent()
subject.postDownload = { Runtime.getRuntime().exit(0) }
}
return
// Also upgrade stub
notifyUpdate(subject.notifyId) {
it.setProgress(0, 0, true)
.setContentTitle(context.getString(R.string.hide_app_title))
.setContentText("")
}
// Extract stub
val zf = ZipFile(updateApk)
val apk = context.cachedFile("stub.apk")
apk.delete()
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
zf.close()
// Patch and install
subject.intent = HideAPK.upgrade(context, apk)
?: throw IOException("HideAPK patch error")
apk.delete()
} catch (e: Exception) {
// If any error occurred, do not let stub load the new APK
updateApk.delete()
@ -322,14 +298,14 @@ class DownloadEngine(
}
} else {
val session = APKInstall.startSession(context)
writeTee(session.openStream(context))
stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
subject.intent = session.waitIntent()
}
}
private suspend fun handleModule(src: InputStream, file: Uri) {
val input = ZipInputStream(src.buffered())
val output = ZipOutputStream(file.outputStream().buffered())
val input = ZipInputStream(src)
val output = ZipOutputStream(file.outputStream())
withStreams(input, output) { zin, zout ->
zout.putNextEntry(ZipEntry("META-INF/"))
@ -337,7 +313,7 @@ class DownloadEngine(
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
context.assets.open("module_installer.sh").copyAll(zout)
context.assets.open("module_installer.sh").use { it.copyAll(zout) }
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
zout.write("#MAGISK\n".toByteArray())

View File

@ -20,11 +20,6 @@ import kotlinx.parcelize.Parcelize
import java.io.File
import java.util.UUID
enum class Action {
Flash,
Download
}
sealed class Subject : Parcelable {
abstract val url: String
@ -32,19 +27,17 @@ sealed class Subject : Parcelable {
abstract val title: String
abstract val notifyId: Int
open val autoLaunch: Boolean get() = true
open val postDownload: (() -> Unit)? get() = null
open fun pendingIntent(context: Context): PendingIntent? = null
@Parcelize
class Module(
val module: OnlineModule,
val action: Action,
private val module: OnlineModule,
override val autoLaunch: Boolean,
override val notifyId: Int = Notifications.nextId()
) : Subject() {
override val url: String get() = module.zipUrl
override val title: String get() = module.downloadFilename
override val autoLaunch: Boolean get() = action == Action.Flash
@IgnoredOnParcel
override val file by lazy {
@ -65,12 +58,9 @@ sealed class Subject : Parcelable {
@IgnoredOnParcel
override val file by lazy {
AppContext.cachedFile("manager.apk").apply { delete() }.toUri()
MediaStoreUtils.getFile("${title}.apk").uri
}
@IgnoredOnParcel
override var postDownload: (() -> Unit)? = null
@IgnoredOnParcel
var intent: Intent? = null
override fun pendingIntent(context: Context) = intent?.toPending(context)

View File

@ -2,7 +2,6 @@ package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.Action
import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.model.module.OnlineModule
@ -22,9 +21,7 @@ class OnlineModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog
dialog.apply {
fun download(install: Boolean) {
val action = if (install) Action.Flash else Action.Download
val subject = Subject.Module(item, action)
DownloadEngine.startWithActivity(activity, subject)
DownloadEngine.startWithActivity(activity, Subject.Module(item, install))
}
val title = context.getString(R.string.repo_install_title,