Skip to content

Commit

Permalink
Always update stub APK when upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
topjohnwu committed Mar 14, 2024
1 parent 050a073 commit c951b20
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 67 deletions.
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -178,7 +173,6 @@ class DownloadEngine(
} else {
notifyFinish(subject)
}
subject.postDownload?.invoke()
} catch (e: IOException) {
Timber.e(e)
notifyFail(subject)
Expand Down Expand Up @@ -271,73 +265,55 @@ 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())

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("")
}
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))

// 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()
throw e
}
} 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/"))
zout.putNextEntry(ZipEntry("META-INF/com/"))
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())
Expand Down
16 changes: 3 additions & 13 deletions app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt
Expand Up @@ -20,31 +20,24 @@ 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
abstract val file: Uri
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 {
Expand All @@ -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)
Expand Down
Expand Up @@ -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
Expand All @@ -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,
Expand Down

0 comments on commit c951b20

Please sign in to comment.