Download Magisk Manager via new service

This commit is contained in:
topjohnwu
2019-07-29 00:26:18 -07:00
parent 3d81f167ea
commit 85f5ff3c14
14 changed files with 170 additions and 128 deletions
@@ -14,9 +14,9 @@ import com.topjohnwu.magisk.extensions.provide
import com.topjohnwu.magisk.model.entity.internal.Configuration.*
import com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Magisk
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Module
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.utils.APKInstall
import java.io.File
import kotlin.random.Random.Default.nextInt
@@ -33,6 +33,7 @@ open class DownloadService : RemoteFileService() {
override fun onFinished(file: File, subject: DownloadSubject, id: Int) = when (subject) {
is Magisk -> onFinishedInternal(file, subject, id)
is Module -> onFinishedInternal(file, subject, id)
is Manager -> onFinishedInternal(file, subject, id)
}
private fun onFinishedInternal(
@@ -55,6 +56,18 @@ open class DownloadService : RemoteFileService() {
else -> Unit
}
private fun onFinishedInternal(
file: File,
subject: Manager,
id: Int
) {
remove(id)
when (subject.configuration) {
is APK.Upgrade -> APKInstall.install(this, file)
else -> Unit
}
}
// ---
override fun NotificationCompat.Builder.addActions(
@@ -63,6 +76,7 @@ open class DownloadService : RemoteFileService() {
) = when (subject) {
is Magisk -> addActionsInternal(file, subject)
is Module -> addActionsInternal(file, subject)
is Manager -> addActionsInternal(file, subject)
}
private fun NotificationCompat.Builder.addActionsInternal(
@@ -87,6 +101,14 @@ open class DownloadService : RemoteFileService() {
else -> this
}
private fun NotificationCompat.Builder.addActionsInternal(
file: File,
subject: Manager
) = when (subject.configuration) {
APK.Upgrade -> setContentIntent(APKInstall.installIntent(context, file))
else -> this
}
@Suppress("ReplaceSingleLineLet")
private fun NotificationCompat.Builder.setContentIntent(intent: Intent) =
PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT)
@@ -0,0 +1,67 @@
package com.topjohnwu.magisk.model.download
import android.content.ComponentName
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.ClassMap
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Restore
import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Upgrade
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.ui.SplashActivity
import com.topjohnwu.magisk.utils.PatchAPK
import com.topjohnwu.magisk.utils.RootUtils
import com.topjohnwu.superuser.Shell
import dalvik.system.DexClassLoader
import timber.log.Timber
import java.io.File
private fun RemoteFileService.patchPackage(apk: File, id: Int): File {
if (packageName != BuildConfig.APPLICATION_ID) {
update(id) { notification ->
notification.setProgress(0, 0, true)
.setProgress(0, 0, true)
.setContentTitle(getString(R.string.hide_manager_title))
.setContentText("")
}
val patched = File(apk.parent, "patched.apk")
try {
// Try using the new APK to patch itself
val loader = DexClassLoader(apk.path, apk.parent, null, ClassLoader.getSystemClassLoader())
loader.loadClass("a.a")
.getMethod("patchAPK", String::class.java, String::class.java, String::class.java)
.invoke(null, apk.path, patched.path, packageName)
} catch (e: Exception) {
Timber.e(e)
// Fallback to use the current implementation
PatchAPK.patch(apk.path, patched.path, packageName)
}
apk.delete()
return patched
} else {
return apk
}
}
private fun RemoteFileService.restore(apk: File, id: Int): File {
update(id) { notification ->
notification.setProgress(0, 0, true)
.setProgress(0, 0, true)
.setContentTitle(getString(R.string.restore_img_msg))
.setContentText("")
}
Config.export()
// Make it world readable
apk.setReadable(true, false)
if (Shell.su("pm install $apk").exec().isSuccess)
RootUtils.rmAndLaunch(packageName,
ComponentName(BuildConfig.APPLICATION_ID,
ClassMap.get<Class<*>>(SplashActivity::class.java).name))
return apk
}
fun RemoteFileService.handleAPK(apk: File, subject: DownloadSubject.Manager)
= when (subject.configuration) {
is Upgrade -> patchPackage(apk, subject.hashCode())
is Restore -> restore(apk, subject.hashCode())
}
@@ -27,7 +27,7 @@ abstract class NotificationService : Service() {
// --
protected fun update(
fun update(
id: Int,
body: (NotificationCompat.Builder) -> Unit = {}
) {
@@ -11,8 +11,7 @@ import com.topjohnwu.magisk.extensions.firstMap
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Magisk
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Module
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import com.topjohnwu.magisk.utils.ProgInputStream
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.superuser.ShellUtils
@@ -80,6 +79,11 @@ abstract class RemoteFileService : NotificationService() {
.map { stream.toModule(subject.file, it.byteStream()); subject.file }
else -> Single.fromCallable { stream.writeTo(subject.file); subject.file }
}
}.map {
when (subject) {
is Manager -> handleAPK(it, subject)
else -> it
}
}
// ---
@@ -17,7 +17,6 @@ data class UninstallerJson(
val link: String = ""
)
@Parcelize
@JsonSerializable
data class MagiskJson(
val version: String = "",
@@ -25,12 +24,13 @@ data class MagiskJson(
val link: String = "",
val note: String = "",
@Json(name = "md5") val hash: String = ""
) : Parcelable
)
@Parcelize
@JsonSerializable
data class ManagerJson(
val version: String = "",
val versionCode: Int = -1,
val link: String = "",
val note: String = ""
)
) : Parcelable
@@ -16,6 +16,15 @@ sealed class Configuration : Parcelable {
}
sealed class APK : Configuration() {
@Parcelize
object Upgrade : APK()
@Parcelize
object Restore : APK()
}
@Parcelize
object Download : Configuration()
@@ -7,6 +7,7 @@ import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.extensions.cachedFile
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.model.entity.MagiskJson
import com.topjohnwu.magisk.model.entity.ManagerJson
import com.topjohnwu.magisk.model.entity.module.Repo
import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize
@@ -20,8 +21,8 @@ sealed class DownloadSubject : Parcelable {
@Parcelize
data class Module(
val module: Repo,
val configuration: Configuration
val module: Repo,
val configuration: Configuration
) : DownloadSubject() {
override val url: String get() = module.zipUrl
@@ -31,6 +32,27 @@ sealed class DownloadSubject : Parcelable {
}
}
@Parcelize
data class Manager(
val configuration: Configuration.APK
) : DownloadSubject() {
@IgnoredOnParcel
val manager: ManagerJson = Info.remote.app
override val title: String
get() = "MagiskManager-v${manager.version}(${manager.versionCode})"
override val url: String
get() = manager.link
@IgnoredOnParcel
override val file by lazy {
get<Context>().cachedFile("manager.apk")
}
}
sealed class Magisk : DownloadSubject() {
abstract val configuration: Configuration
@@ -11,8 +11,11 @@ import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.data.database.base.su
import com.topjohnwu.magisk.extensions.inject
import com.topjohnwu.magisk.extensions.reboot
import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.entity.ManagerJson
import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.utils.DownloadApp
import com.topjohnwu.magisk.utils.SuLogger
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts
@@ -73,9 +76,12 @@ open class GeneralReceiver : BroadcastReceiver() {
}
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context)
Const.Key.BROADCAST_MANAGER_UPDATE -> {
Info.remote = Info.remote.copy(app = Info.remote.app.copy(
link = intent.getStringExtra(Const.Key.INTENT_SET_LINK) ?: ""))
DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME))
intent.getParcelableExtra<ManagerJson>(Const.Key.INTENT_SET_APP)?.let {
Info.remote = Info.remote.copy(app = it)
}
DownloadService(context) {
subject = DownloadSubject.Manager(Configuration.APK.Upgrade)
}
}
Const.Key.BROADCAST_REBOOT -> reboot()
}