Merge Magisk install zip into Magisk Manager
Distribute Magisk directly with Magisk Manager APK. The APK will contain all required binaries and scripts for installation and uninstallation. App versions will now align with Magisk releases. Extra effort is spent to make the APK itself also a flashable zip that can be used in custom recoveries, so those still prefer to install Magisk with recoveries will not be affected with this change. As a bonus, this makes the whole installation and uninstallation process 100% offline. The existing Magisk Manager was not really functional without an Internet connection, as the installation process was highly tied to zips hosted on the server. An additional bonus: since all binaries are now shipped as "native libraries" of the APK, we can finally bump the target SDK version higher than 28. The target SDK version was stuck at 28 for a long time because newer SELinux restricts running executables from internal storage. More details can be found here: https://github.com/termux/termux-app/issues/1072 The target SDK bump will be addressed in a future commit. Co-authored with @vvb2060
This commit is contained in:
parent
61d52991f1
commit
ec8fffe61c
5
app/.gitignore
vendored
5
app/.gitignore
vendored
@ -6,6 +6,7 @@
|
|||||||
app/release
|
app/release
|
||||||
*.hprof
|
*.hprof
|
||||||
.externalNativeBuild/
|
.externalNativeBuild/
|
||||||
public.certificate.x509.pem
|
|
||||||
private.key.pk8
|
|
||||||
*.apk
|
*.apk
|
||||||
|
src/main/assets
|
||||||
|
src/main/jniLibs
|
||||||
|
src/main/resources
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@ -22,8 +23,8 @@ android {
|
|||||||
applicationId = "com.topjohnwu.magisk"
|
applicationId = "com.topjohnwu.magisk"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
multiDexEnabled = true
|
multiDexEnabled = true
|
||||||
versionName = Config.appVersion
|
versionName = Config.version
|
||||||
versionCode = Config.appVersionCode
|
versionCode = Config.versionCode
|
||||||
|
|
||||||
javaCompileOptions.annotationProcessorOptions.arguments(
|
javaCompileOptions.annotationProcessorOptions.arguments(
|
||||||
mapOf("room.incremental" to "true")
|
mapOf("room.incremental" to "true")
|
||||||
@ -51,13 +52,14 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
exclude("/META-INF/**")
|
exclude("/META-INF/*")
|
||||||
exclude("/org/bouncycastle/**")
|
exclude("/org/bouncycastle/**")
|
||||||
exclude("/kotlin/**")
|
exclude("/kotlin/**")
|
||||||
exclude("/kotlinx/**")
|
exclude("/kotlinx/**")
|
||||||
exclude("/okhttp3/**")
|
exclude("/okhttp3/**")
|
||||||
exclude("/*.txt")
|
exclude("/*.txt")
|
||||||
exclude("/*.bin")
|
exclude("/*.bin")
|
||||||
|
doNotStrip("**/*.so")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
@ -65,10 +67,82 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) {
|
val syncLibs by tasks.registering(Sync::class) {
|
||||||
from(rootProject.file("scripts/util_functions.sh"))
|
into("src/main/jniLibs")
|
||||||
into("src/main/res/raw")
|
into("armeabi-v7a") {
|
||||||
})
|
from(rootProject.file("native/out/armeabi-v7a")) {
|
||||||
|
include("busybox", "magiskboot", "magiskinit", "magisk")
|
||||||
|
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
|
||||||
|
}
|
||||||
|
from(rootProject.file("native/out/arm64-v8a")) {
|
||||||
|
include("magisk")
|
||||||
|
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into("x86") {
|
||||||
|
from(rootProject.file("native/out/x86")) {
|
||||||
|
include("busybox", "magiskboot", "magiskinit", "magisk")
|
||||||
|
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
|
||||||
|
}
|
||||||
|
from(rootProject.file("native/out/x86_64")) {
|
||||||
|
include("magisk")
|
||||||
|
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doFirst {
|
||||||
|
if (inputs.sourceFiles.files.size != 10)
|
||||||
|
throw StopExecutionException("Build binary files first")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val createStubLibs by tasks.registering {
|
||||||
|
dependsOn(syncLibs)
|
||||||
|
doLast {
|
||||||
|
val arm64 = project.file("src/main/jniLibs/arm64-v8a/libstub.so")
|
||||||
|
arm64.parentFile.mkdirs()
|
||||||
|
arm64.createNewFile()
|
||||||
|
val x64 = project.file("src/main/jniLibs/x86_64/libstub.so")
|
||||||
|
x64.parentFile.mkdirs()
|
||||||
|
x64.createNewFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val syncAssets by tasks.registering(Sync::class) {
|
||||||
|
dependsOn(createStubLibs)
|
||||||
|
inputs.property("version", Config.version)
|
||||||
|
inputs.property("versionCode", Config.versionCode)
|
||||||
|
into("src/main/assets")
|
||||||
|
from(rootProject.file("scripts")) {
|
||||||
|
include("util_functions.sh", "boot_patch.sh", "magisk_uninstaller.sh", "addon.d.sh")
|
||||||
|
}
|
||||||
|
into("chromeos") {
|
||||||
|
from(rootProject.file("tools/futility"))
|
||||||
|
from(rootProject.file("tools/keys")) {
|
||||||
|
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filesMatching("**/util_functions.sh") {
|
||||||
|
filter {
|
||||||
|
it.replace("#MAGISK_VERSION_STUB",
|
||||||
|
"MAGISK_VER='${Config.version}'\n" +
|
||||||
|
"MAGISK_VER_CODE=${Config.versionCode}")
|
||||||
|
}
|
||||||
|
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val syncResources by tasks.registering(Sync::class) {
|
||||||
|
dependsOn(syncAssets)
|
||||||
|
into("src/main/resources/META-INF/com/google/android")
|
||||||
|
from(rootProject.file("scripts/update_binary.sh")) {
|
||||||
|
rename { "update-binary" }
|
||||||
|
}
|
||||||
|
from(rootProject.file("scripts/flash_script.sh")) {
|
||||||
|
rename { "updater-script" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks["preBuild"]?.dependsOn(syncResources)
|
||||||
|
|
||||||
android.applicationVariants.all {
|
android.applicationVariants.all {
|
||||||
val keysDir = rootProject.file("tools/keys")
|
val keysDir = rootProject.file("tools/keys")
|
||||||
@ -178,6 +252,5 @@ dependencies {
|
|||||||
implementation("androidx.transition:transition:1.3.1")
|
implementation("androidx.transition:transition:1.3.1")
|
||||||
implementation("androidx.multidex:multidex:2.0.1")
|
implementation("androidx.multidex:multidex:2.0.1")
|
||||||
implementation("androidx.core:core-ktx:1.3.2")
|
implementation("androidx.core:core-ktx:1.3.2")
|
||||||
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
|
||||||
implementation("com.google.android.material:material:1.2.1")
|
implementation("com.google.android.material:material:1.2.1")
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:name="a.e"
|
android:name="a.e"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
android:multiArch="true"
|
||||||
|
android:extractNativeLibs="true"
|
||||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
||||||
|
|
||||||
<!-- Splash -->
|
<!-- Splash -->
|
||||||
|
@ -19,6 +19,7 @@ import com.topjohnwu.superuser.Shell
|
|||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
open class App() : Application() {
|
open class App() : Application() {
|
||||||
@ -61,6 +62,12 @@ open class App() : Application() {
|
|||||||
val wrapped = impl.wrap()
|
val wrapped = impl.wrap()
|
||||||
super.attachBaseContext(wrapped)
|
super.attachBaseContext(wrapped)
|
||||||
|
|
||||||
|
val info = base.applicationInfo
|
||||||
|
val libDir = runCatching {
|
||||||
|
info.javaClass.getDeclaredField("secondaryNativeLibraryDir").get(info) as String?
|
||||||
|
}.getOrNull() ?: info.nativeLibraryDir
|
||||||
|
Const.NATIVE_LIB_DIR = File(libDir)
|
||||||
|
|
||||||
// Normal startup
|
// Normal startup
|
||||||
startKoin {
|
startKoin {
|
||||||
androidContext(wrapped)
|
androidContext(wrapped)
|
||||||
|
@ -1,13 +1,30 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
object Const {
|
object Const {
|
||||||
|
|
||||||
|
val CPU_ABI: String
|
||||||
|
val CPU_ABI_32: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
CPU_ABI = Build.SUPPORTED_ABIS[0]
|
||||||
|
CPU_ABI_32 = Build.SUPPORTED_32_BIT_ABIS[0]
|
||||||
|
} else {
|
||||||
|
CPU_ABI = Build.CPU_ABI
|
||||||
|
CPU_ABI_32 = CPU_ABI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
lateinit var MAGISKTMP: String
|
lateinit var MAGISKTMP: String
|
||||||
|
lateinit var NATIVE_LIB_DIR: File
|
||||||
val MAGISK_PATH get() = "$MAGISKTMP/modules"
|
val MAGISK_PATH get() = "$MAGISKTMP/modules"
|
||||||
const val TMP_FOLDER_PATH = "/dev/tmp"
|
const val TMPDIR = "/dev/tmp"
|
||||||
const val MAGISK_LOG = "/cache/magisk.log"
|
const val MAGISK_LOG = "/cache/magisk.log"
|
||||||
|
|
||||||
// Versions
|
// Versions
|
||||||
@ -19,11 +36,9 @@ object Const {
|
|||||||
val USER_ID = Process.myUid() / 100000
|
val USER_ID = Process.myUid() / 100000
|
||||||
|
|
||||||
object Version {
|
object Version {
|
||||||
const val MIN_VERSION = "v19.0"
|
const val MIN_VERSION = "v20.4"
|
||||||
const val MIN_VERCODE = 19000
|
const val MIN_VERCODE = 20400
|
||||||
|
|
||||||
fun atLeast_20_2() = Info.env.magiskVersionCode >= 20200 || isCanary()
|
|
||||||
fun atLeast_20_4() = Info.env.magiskVersionCode >= 20400 || isCanary()
|
|
||||||
fun atLeast_21_0() = Info.env.magiskVersionCode >= 21000 || isCanary()
|
fun atLeast_21_0() = Info.env.magiskVersionCode >= 21000 || isCanary()
|
||||||
fun atLeast_21_2() = Info.env.magiskVersionCode >= 21200 || isCanary()
|
fun atLeast_21_2() = Info.env.magiskVersionCode >= 21200 || isCanary()
|
||||||
fun isCanary() = Info.env.magiskVersionCode % 100 != 0
|
fun isCanary() = Info.env.magiskVersionCode % 100 != 0
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.download
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Parcelable
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
|
|
||||||
sealed class Action : Parcelable {
|
|
||||||
|
|
||||||
sealed class Flash : Action() {
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
object Primary : Flash()
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
object Secondary : Flash()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
object Download : Action()
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
object Uninstall : Action()
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
object EnvFix : Action()
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class Patch(val fileUri: Uri) : Action()
|
|
||||||
|
|
||||||
}
|
|
@ -8,7 +8,6 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.ForegroundTracker
|
import com.topjohnwu.magisk.core.ForegroundTracker
|
||||||
import com.topjohnwu.magisk.core.base.BaseService
|
import com.topjohnwu.magisk.core.base.BaseService
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.checkSum
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||||
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||||
@ -69,17 +68,14 @@ abstract class BaseDownloader : BaseService(), KoinComponent {
|
|||||||
// -- Download logic
|
// -- Download logic
|
||||||
|
|
||||||
private suspend fun Subject.startDownload() {
|
private suspend fun Subject.startDownload() {
|
||||||
val skip = this is Subject.Magisk && file.checkSum("MD5", magisk.md5)
|
val stream = service.fetchFile(url).toProgressStream(this)
|
||||||
if (!skip) {
|
when (this) {
|
||||||
val stream = service.fetchFile(url).toProgressStream(this)
|
is Subject.Module -> // Download and process on-the-fly
|
||||||
when (this) {
|
stream.toModule(file, service.fetchInstaller().byteStream())
|
||||||
is Subject.Module -> // Download and process on-the-fly
|
else -> {
|
||||||
stream.toModule(file, service.fetchInstaller().byteStream())
|
withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) }
|
||||||
else -> {
|
if (this is Subject.Manager)
|
||||||
withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) }
|
handleAPK(this)
|
||||||
if (this is Subject.Manager)
|
|
||||||
handleAPK(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val newId = notifyFinish(this)
|
val newId = notifyFinish(this)
|
||||||
|
@ -7,11 +7,10 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import com.topjohnwu.magisk.core.download.Action.*
|
import com.topjohnwu.magisk.core.download.Action.Flash
|
||||||
import com.topjohnwu.magisk.core.download.Action.Flash.Secondary
|
import com.topjohnwu.magisk.core.download.Subject.Manager
|
||||||
import com.topjohnwu.magisk.core.download.Subject.*
|
import com.topjohnwu.magisk.core.download.Subject.Module
|
||||||
import com.topjohnwu.magisk.core.intent
|
import com.topjohnwu.magisk.core.intent
|
||||||
import com.topjohnwu.magisk.core.tasks.EnvFixTask
|
|
||||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||||
import com.topjohnwu.magisk.utils.APKInstall
|
import com.topjohnwu.magisk.utils.APKInstall
|
||||||
import kotlin.random.Random.Default.nextInt
|
import kotlin.random.Random.Default.nextInt
|
||||||
@ -22,25 +21,12 @@ open class DownloadService : BaseDownloader() {
|
|||||||
private val context get() = this
|
private val context get() = this
|
||||||
|
|
||||||
override suspend fun onFinish(subject: Subject, id: Int) = when (subject) {
|
override suspend fun onFinish(subject: Subject, id: Int) = when (subject) {
|
||||||
is Magisk -> subject.onFinish(id)
|
|
||||||
is Module -> subject.onFinish(id)
|
is Module -> subject.onFinish(id)
|
||||||
is Manager -> subject.onFinish(id)
|
is Manager -> subject.onFinish(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun Magisk.onFinish(id: Int) = when (val action = action) {
|
|
||||||
Uninstall -> FlashFragment.uninstall(file, id)
|
|
||||||
EnvFix -> {
|
|
||||||
remove(id)
|
|
||||||
EnvFixTask(file).exec()
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
is Patch -> FlashFragment.patch(file, action.fileUri, id)
|
|
||||||
is Flash -> FlashFragment.flash(file, action is Secondary, id)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.onFinish(id: Int) = when (action) {
|
private fun Module.onFinish(id: Int) = when (action) {
|
||||||
is Flash -> FlashFragment.install(file, id)
|
Flash -> FlashFragment.install(file, id)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,22 +39,13 @@ open class DownloadService : BaseDownloader() {
|
|||||||
|
|
||||||
override fun Notification.Builder.setIntent(subject: Subject)
|
override fun Notification.Builder.setIntent(subject: Subject)
|
||||||
= when (subject) {
|
= when (subject) {
|
||||||
is Magisk -> setIntent(subject)
|
|
||||||
is Module -> setIntent(subject)
|
is Module -> setIntent(subject)
|
||||||
is Manager -> setIntent(subject)
|
is Manager -> setIntent(subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Notification.Builder.setIntent(subject: Magisk)
|
|
||||||
= when (val action = subject.action) {
|
|
||||||
Uninstall -> setContentIntent(FlashFragment.uninstallIntent(context, subject.file))
|
|
||||||
is Flash -> setContentIntent(FlashFragment.flashIntent(context, subject.file, action is Secondary))
|
|
||||||
is Patch -> setContentIntent(FlashFragment.patchIntent(context, subject.file, action.fileUri))
|
|
||||||
else -> setContentIntent(Intent())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Notification.Builder.setIntent(subject: Module)
|
private fun Notification.Builder.setIntent(subject: Module)
|
||||||
= when (subject.action) {
|
= when (subject.action) {
|
||||||
is Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
|
Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
|
||||||
else -> setContentIntent(Intent())
|
else -> setContentIntent(Intent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import android.net.Uri
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.model.MagiskJson
|
|
||||||
import com.topjohnwu.magisk.core.model.ManagerJson
|
import com.topjohnwu.magisk.core.model.ManagerJson
|
||||||
import com.topjohnwu.magisk.core.model.StubJson
|
import com.topjohnwu.magisk.core.model.StubJson
|
||||||
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||||
@ -53,57 +52,12 @@ sealed class Subject : Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
abstract class Magisk : Subject() {
|
|
||||||
|
sealed class Action : Parcelable {
|
||||||
val magisk: MagiskJson = Info.remote.magisk
|
@Parcelize
|
||||||
|
object Flash : Action()
|
||||||
@Parcelize
|
|
||||||
private class Internal(
|
@Parcelize
|
||||||
override val action: Action
|
object Download : Action()
|
||||||
) : Magisk() {
|
|
||||||
override val url: String get() = magisk.link
|
|
||||||
override val title: String get() = "Magisk-${magisk.version}(${magisk.versionCode})"
|
|
||||||
|
|
||||||
@IgnoredOnParcel
|
|
||||||
override val file by lazy {
|
|
||||||
cachedFile("magisk.zip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
private class Uninstall : Magisk() {
|
|
||||||
override val action get() = Action.Uninstall
|
|
||||||
override val url: String get() = Info.remote.uninstaller.link
|
|
||||||
override val title: String get() = "uninstall.zip"
|
|
||||||
|
|
||||||
@IgnoredOnParcel
|
|
||||||
override val file by lazy {
|
|
||||||
cachedFile(title)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
private class Download : Magisk() {
|
|
||||||
override val action get() = Action.Download
|
|
||||||
override val url: String get() = magisk.link
|
|
||||||
override val title: String get() = "Magisk-${magisk.version}(${magisk.versionCode}).zip"
|
|
||||||
|
|
||||||
@IgnoredOnParcel
|
|
||||||
override val file by lazy {
|
|
||||||
MediaStoreUtils.getFile(title).uri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
operator fun invoke(config: Action) = when (config) {
|
|
||||||
Action.Download -> Download()
|
|
||||||
Action.Uninstall -> Uninstall()
|
|
||||||
Action.EnvFix, is Action.Flash, is Action.Patch -> Internal(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,13 @@ package com.topjohnwu.magisk.core.tasks
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.net.toFile
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
||||||
import com.topjohnwu.magisk.core.utils.unzip
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||||
|
import com.topjohnwu.magisk.core.utils.unzip
|
||||||
import com.topjohnwu.magisk.ktx.writeTo
|
import com.topjohnwu.magisk.ktx.writeTo
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.core.KoinComponent
|
import org.koin.core.KoinComponent
|
||||||
@ -25,61 +24,50 @@ open class FlashZip(
|
|||||||
private val logs: MutableList<String>
|
private val logs: MutableList<String>
|
||||||
): KoinComponent {
|
): KoinComponent {
|
||||||
|
|
||||||
val context: Context by inject()
|
private val context: Context by inject()
|
||||||
private val installFolder = File(context.cacheDir, "flash").apply {
|
private val installDir = File(context.cacheDir, "flash")
|
||||||
if (!exists()) mkdirs()
|
private lateinit var zipFile: File
|
||||||
}
|
|
||||||
private val tmpFile: File = File(installFolder, "install.zip")
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun unzipAndCheck(): Boolean {
|
|
||||||
val parentFile = tmpFile.parentFile ?: return false
|
|
||||||
tmpFile.unzip(parentFile, "META-INF/com/google/android", true)
|
|
||||||
|
|
||||||
val updaterScript = File(parentFile, "updater-script")
|
|
||||||
return Shell
|
|
||||||
.su("grep -q '#MAGISK' $updaterScript")
|
|
||||||
.exec()
|
|
||||||
.isSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun flash(): Boolean {
|
private fun flash(): Boolean {
|
||||||
console.add("- Copying zip to temp directory")
|
installDir.deleteRecursively()
|
||||||
|
installDir.mkdirs()
|
||||||
|
|
||||||
runCatching {
|
zipFile = if (mUri.scheme == "file") {
|
||||||
mUri.inputStream().writeTo(tmpFile)
|
mUri.toFile()
|
||||||
}.getOrElse {
|
} else {
|
||||||
when (it) {
|
File(installDir, "install.zip").also {
|
||||||
is FileNotFoundException -> console.add("! Invalid Uri")
|
console.add("- Copying zip to temp directory")
|
||||||
is IOException -> console.add("! Cannot copy to cache")
|
try {
|
||||||
|
mUri.inputStream().writeTo(it)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
when (e) {
|
||||||
|
is FileNotFoundException -> console.add("! Invalid Uri")
|
||||||
|
else -> console.add("! Cannot copy to cache")
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val isMagiskModule = runCatching {
|
val isValid = runCatching {
|
||||||
unzipAndCheck()
|
zipFile.unzip(installDir, "META-INF/com/google/android", true)
|
||||||
|
val script = File(installDir, "updater-script")
|
||||||
|
script.readText().contains("#MAGISK")
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
console.add("! Unzip error")
|
console.add("! Unzip error")
|
||||||
throw it
|
throw it
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMagiskModule) {
|
if (!isValid) {
|
||||||
console.add("! This zip is not a Magisk Module!")
|
console.add("! This zip is not a Magisk module!")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
console.add("- Installing ${mUri.displayName}")
|
console.add("- Installing ${mUri.displayName}")
|
||||||
|
|
||||||
val parentFile = tmpFile.parent ?: return false
|
return Shell.su("sh $installDir/update-binary dummy 1 $zipFile")
|
||||||
|
.to(console, logs).exec().isSuccess
|
||||||
return Shell
|
|
||||||
.su(
|
|
||||||
"cd $parentFile",
|
|
||||||
"BOOTMODE=true sh update-binary dummy 1 $tmpFile"
|
|
||||||
)
|
|
||||||
.to(console, logs)
|
|
||||||
.exec().isSuccess
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun exec() = withContext(Dispatchers.IO) {
|
open suspend fun exec() = withContext(Dispatchers.IO) {
|
||||||
@ -94,25 +82,7 @@ open class FlashZip(
|
|||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
false
|
false
|
||||||
} finally {
|
} finally {
|
||||||
Shell.su("cd /", "rm -rf ${tmpFile.parent} ${Const.TMP_FOLDER_PATH}").submit()
|
Shell.su("cd /", "rm -rf $installDir ${Const.TMPDIR}").submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Uninstall(
|
|
||||||
uri: Uri,
|
|
||||||
console: MutableList<String>,
|
|
||||||
log: MutableList<String>
|
|
||||||
) : FlashZip(uri, console, log) {
|
|
||||||
|
|
||||||
override suspend fun exec(): Boolean {
|
|
||||||
val success = super.exec()
|
|
||||||
if (success) {
|
|
||||||
UiThreadHandler.handler.postDelayed(3000) {
|
|
||||||
Shell.su("pm uninstall " + context.packageName).exec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -91,8 +91,7 @@ object HideAPK {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun patchAndHide(context: Context, label: String): Boolean {
|
private suspend fun patchAndHide(context: Context, label: String): Boolean {
|
||||||
val dlStub = !isRunningAsStub && SDK_INT >= 28 && Const.Version.atLeast_20_2()
|
val src = if (!isRunningAsStub && SDK_INT >= 28) {
|
||||||
val src = if (dlStub) {
|
|
||||||
val stub = File(context.cacheDir, "stub.apk")
|
val stub = File(context.cacheDir, "stub.apk")
|
||||||
try {
|
try {
|
||||||
svc.fetchFile(Info.remote.stub.link).byteStream().use {
|
svc.fetchFile(Info.remote.stub.link).byteStream().use {
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package com.topjohnwu.magisk.core.tasks
|
package com.topjohnwu.magisk.core.tasks
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.os.postDelayed
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
|
import com.topjohnwu.magisk.DynAPK
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Const
|
||||||
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||||
import com.topjohnwu.magisk.di.Protected
|
import com.topjohnwu.magisk.di.Protected
|
||||||
import com.topjohnwu.magisk.events.dialog.EnvFixDialog
|
|
||||||
import com.topjohnwu.magisk.ktx.reboot
|
import com.topjohnwu.magisk.ktx.reboot
|
||||||
|
import com.topjohnwu.magisk.ktx.symlink
|
||||||
import com.topjohnwu.magisk.ktx.withStreams
|
import com.topjohnwu.magisk.ktx.withStreams
|
||||||
|
import com.topjohnwu.magisk.ktx.writeTo
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.signing.SignBoot
|
import com.topjohnwu.signing.SignBoot
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
@ -37,7 +37,6 @@ import org.kamranzafar.jtar.TarHeader
|
|||||||
import org.kamranzafar.jtar.TarInputStream
|
import org.kamranzafar.jtar.TarInputStream
|
||||||
import org.kamranzafar.jtar.TarOutputStream
|
import org.kamranzafar.jtar.TarOutputStream
|
||||||
import org.koin.core.KoinComponent
|
import org.koin.core.KoinComponent
|
||||||
import org.koin.core.get
|
|
||||||
import org.koin.core.inject
|
import org.koin.core.inject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.*
|
import java.io.*
|
||||||
@ -46,10 +45,8 @@ import java.security.SecureRandom
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
import kotlin.collections.set
|
|
||||||
|
|
||||||
abstract class MagiskInstallImpl protected constructor(
|
abstract class MagiskInstallImpl protected constructor(
|
||||||
private var zipUri: Uri,
|
|
||||||
protected val console: MutableList<String> = NOPList.getInstance(),
|
protected val console: MutableList<String> = NOPList.getInstance(),
|
||||||
private val logs: MutableList<String> = NOPList.getInstance()
|
private val logs: MutableList<String> = NOPList.getInstance()
|
||||||
) : KoinComponent {
|
) : KoinComponent {
|
||||||
@ -59,17 +56,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
|
|
||||||
private var tarOut: TarOutputStream? = null
|
private var tarOut: TarOutputStream? = null
|
||||||
private val service: NetworkService by inject()
|
private val service: NetworkService by inject()
|
||||||
protected val context: Context by inject()
|
protected val context: Context by inject(Protected)
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val ABI_MAP = TreeMap<String, String>()
|
|
||||||
init {
|
|
||||||
ABI_MAP["armeabi-v7a"] = "arm"
|
|
||||||
ABI_MAP["arm64-v8a"] = "arm64"
|
|
||||||
ABI_MAP["x86"] = "x86"
|
|
||||||
ABI_MAP["x86_64"] = "x64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findImage(): Boolean {
|
private fun findImage(): Boolean {
|
||||||
srcBoot = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
srcBoot = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
||||||
@ -98,69 +85,85 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
private fun installDirFile(name: String): File {
|
||||||
private fun extractZip(): Boolean {
|
return if (installDir is SuFile)
|
||||||
val arch: String
|
SuFile(installDir, name)
|
||||||
val arch32: String
|
else
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
File(installDir, name)
|
||||||
arch = ABI_MAP[Build.SUPPORTED_ABIS[0]]!!
|
}
|
||||||
arch32 = ABI_MAP[Build.SUPPORTED_32_BIT_ABIS[0]]!!
|
|
||||||
|
private fun extractFiles(): Boolean {
|
||||||
|
console.add("- Device platform: ${Const.CPU_ABI}")
|
||||||
|
console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||||
|
|
||||||
|
val binDir = File(context.filesDir.parent, "install")
|
||||||
|
binDir.deleteRecursively()
|
||||||
|
binDir.mkdirs()
|
||||||
|
|
||||||
|
installDir = if (Shell.rootAccess()) {
|
||||||
|
SuFile("${Const.TMPDIR}/install")
|
||||||
} else {
|
} else {
|
||||||
arch = ABI_MAP[Build.CPU_ABI]!!
|
binDir
|
||||||
arch32 = arch
|
|
||||||
}
|
|
||||||
|
|
||||||
console.add("- Device platform: $arch")
|
|
||||||
console.add("- Magisk Manager: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
|
||||||
console.add("- Install target: ${Info.remote.magisk.version} (${Info.remote.magisk.versionCode})")
|
|
||||||
|
|
||||||
fun newFile(name: String): File {
|
|
||||||
return if (installDir is SuFile)
|
|
||||||
SuFile(installDir, name)
|
|
||||||
else
|
|
||||||
File(installDir, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ZipInputStream(zipUri.inputStream().buffered()).use { zi ->
|
// Extract binaries
|
||||||
lateinit var ze: ZipEntry
|
if (isRunningAsStub) {
|
||||||
while (zi.nextEntry?.let { ze = it } != null) {
|
ZipInputStream(DynAPK.current(context).inputStream().buffered()).use { zi ->
|
||||||
if (ze.isDirectory)
|
lateinit var ze: ZipEntry
|
||||||
continue
|
while (zi.nextEntry?.let { ze = it } != null) {
|
||||||
|
if (ze.isDirectory)
|
||||||
|
continue
|
||||||
|
|
||||||
var name: String? = null
|
val name = if (ze.name.startsWith("lib/${Const.CPU_ABI_32}/")) {
|
||||||
|
val n = ze.name.substring(ze.name.lastIndexOf('/') + 1)
|
||||||
if (ze.name.startsWith("chromeos/")) {
|
n.substring(3, n.length - 3)
|
||||||
name = ze.name
|
} else {
|
||||||
} else {
|
continue
|
||||||
for (n in listOf("$arch32/", "common/", "META-INF/com/google/android/update-binary")) {
|
|
||||||
if (ze.name.startsWith(n)) {
|
|
||||||
name = ze.name.substring(ze.name.lastIndexOf('/') + 1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val dest = File(binDir, name)
|
||||||
|
dest.outputStream().use { zi.copyTo(it) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
name ?: continue
|
} else {
|
||||||
|
val libs = Const.NATIVE_LIB_DIR.listFiles { _, name ->
|
||||||
val dest = newFile(name)
|
name.startsWith("lib") && name.endsWith(".so")
|
||||||
dest.parentFile!!.mkdirs()
|
} ?: emptyArray()
|
||||||
SuFileOutputStream(dest).use { s -> zi.copyTo(s) }
|
for (lib in libs) {
|
||||||
|
val name = lib.name.substring(3, lib.name.length - 3)
|
||||||
|
val bin = File(binDir, name)
|
||||||
|
symlink(lib.path, bin.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
|
||||||
console.add("! Cannot unzip zip")
|
// Extract scripts
|
||||||
|
for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) {
|
||||||
|
val dest = File(binDir, script)
|
||||||
|
context.assets.open(script).use { it.writeTo(dest) }
|
||||||
|
}
|
||||||
|
// Extract chromeos tools
|
||||||
|
File(binDir, "chromeos").mkdir()
|
||||||
|
for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) {
|
||||||
|
val name = "chromeos/$file"
|
||||||
|
val dest = File(binDir, name)
|
||||||
|
context.assets.open(name).use { it.writeTo(dest) }
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
console.add("! Unable to extract files")
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val init64 = newFile("magiskinit64")
|
if (installDir !== binDir) {
|
||||||
if (init64.exists() && arch != arch32) {
|
arrayOf(
|
||||||
init64.renameTo(newFile("magiskinit"))
|
"rm -rf $installDir",
|
||||||
} else {
|
"mkdir -p $installDir",
|
||||||
init64.delete()
|
"cp_readlink $binDir $installDir",
|
||||||
|
"rm -rf $binDir"
|
||||||
|
).sh()
|
||||||
}
|
}
|
||||||
"cd $installDir; chmod 755 *".sh()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +180,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
lateinit var entry: TarEntry
|
lateinit var entry: TarEntry
|
||||||
|
|
||||||
fun decompressedStream() =
|
fun decompressedStream() =
|
||||||
if (entry.name.contains(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
||||||
|
|
||||||
while (tarIn.nextEntry?.let { entry = it } != null) {
|
while (tarIn.nextEntry?.let { entry = it } != null) {
|
||||||
if (entry.name.contains("boot.img") ||
|
if (entry.name.contains("boot.img") ||
|
||||||
@ -186,12 +189,9 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
console.add("-- Extracting: $name")
|
console.add("-- Extracting: $name")
|
||||||
|
|
||||||
val extract = File(installDir, name)
|
val extract = File(installDir, name)
|
||||||
FileOutputStream(extract).use { decompressedStream().copyTo(it) }
|
decompressedStream().writeTo(extract)
|
||||||
} else if (entry.name.contains("vbmeta.img")) {
|
} else if (entry.name.contains("vbmeta.img")) {
|
||||||
val rawData = ByteArrayOutputStream().let {
|
val rawData = decompressedStream().readBytes()
|
||||||
decompressedStream().copyTo(it)
|
|
||||||
it.toByteArray()
|
|
||||||
}
|
|
||||||
// Valid vbmeta.img should be at least 256 bytes
|
// Valid vbmeta.img should be at least 256 bytes
|
||||||
if (rawData.size < 256)
|
if (rawData.size < 256)
|
||||||
continue
|
continue
|
||||||
@ -208,8 +208,8 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
tarIn.copyTo(tarOut, bufferSize = 1024 * 1024)
|
tarIn.copyTo(tarOut, bufferSize = 1024 * 1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val boot = SuFile.open(installDir, "boot.img")
|
val boot = installDirFile("boot.img")
|
||||||
val recovery = SuFile.open(installDir, "recovery.img")
|
val recovery = installDirFile("recovery.img")
|
||||||
if (Config.recovery && recovery.exists() && boot.exists()) {
|
if (Config.recovery && recovery.exists() && boot.exists()) {
|
||||||
// Install Magisk to recovery
|
// Install Magisk to recovery
|
||||||
srcBoot = recovery.path
|
srcBoot = recovery.path
|
||||||
@ -306,10 +306,19 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix up binaries
|
||||||
|
if (installDir is SuFile) {
|
||||||
|
"fix_env $installDir".sh()
|
||||||
|
} else {
|
||||||
|
"cp_readlink $installDir".sh()
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun patchBoot(): Boolean {
|
private fun patchBoot(): Boolean {
|
||||||
|
"cd $installDir".sh()
|
||||||
|
|
||||||
var srcNand = ""
|
var srcNand = ""
|
||||||
if ("[ -c $srcBoot ] && nanddump -f boot.img $srcBoot".sh().isSuccess) {
|
if ("[ -c $srcBoot ] && nanddump -f boot.img $srcBoot".sh().isSuccess) {
|
||||||
srcNand = srcBoot
|
srcNand = srcBoot
|
||||||
@ -334,26 +343,20 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
"KEEPVERITY=${Config.keepVerity} " +
|
"KEEPVERITY=${Config.keepVerity} " +
|
||||||
"RECOVERYMODE=${Config.recovery}"
|
"RECOVERYMODE=${Config.recovery}"
|
||||||
|
|
||||||
if (!("$FLAGS sh update-binary sh boot_patch.sh $srcBoot").sh().isSuccess) {
|
if (!"$FLAGS sh boot_patch.sh $srcBoot".sh().isSuccess)
|
||||||
return false
|
return false
|
||||||
}
|
|
||||||
|
|
||||||
if (srcNand.isNotEmpty()) {
|
if (srcNand.isNotEmpty())
|
||||||
srcBoot = srcNand
|
srcBoot = srcNand
|
||||||
}
|
|
||||||
|
|
||||||
val job = Shell.sh(
|
val job = Shell.sh("./magiskboot cleanup", "cd /")
|
||||||
"./magiskboot cleanup",
|
|
||||||
"mv bin/busybox busybox",
|
|
||||||
"rm -rf magisk.apk bin boot.img update-binary",
|
|
||||||
"cd /")
|
|
||||||
|
|
||||||
val patched = File(installDir, "new-boot.img")
|
val patched = installDirFile("new-boot.img")
|
||||||
if (isSigned) {
|
if (isSigned) {
|
||||||
console.add("- Signing boot image with verity keys")
|
console.add("- Signing boot image with verity keys")
|
||||||
val signed = File(installDir, "signed.img")
|
val signed = installDirFile("signed.img")
|
||||||
try {
|
try {
|
||||||
withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) {
|
withStreams(SuFileInputStream(patched), SuFileOutputStream(signed)) {
|
||||||
input, out -> SignBoot.doSignature(null, null, input, out, "/boot")
|
input, out -> SignBoot.doSignature(null, null, input, out, "/boot")
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
@ -368,18 +371,10 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copySepolicyRules(): Boolean {
|
|
||||||
if (Info.remote.magisk.versionCode >= 21100) {
|
|
||||||
// Copy existing rules for migration
|
|
||||||
"copy_sepolicy_rules".sh()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flashBoot(): Boolean {
|
private fun flashBoot(): Boolean {
|
||||||
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
|
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
|
||||||
return false
|
return false
|
||||||
"run_migrations".sh()
|
arrayOf("run_migrations", "copy_sepolicy_rules").sh()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,24 +398,28 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun String.sh() = Shell.sh(this).to(console, logs).exec()
|
protected fun uninstall(): Boolean {
|
||||||
|
val apk = if (isRunningAsStub) {
|
||||||
|
DynAPK.current(context).path
|
||||||
|
} else {
|
||||||
|
context.packageCodePath
|
||||||
|
}
|
||||||
|
return "run_uninstaller $apk".sh().isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.sh() = Shell.sh(this).to(console, logs).exec()
|
||||||
private fun Array<String>.sh() = Shell.sh(*this).to(console, logs).exec()
|
private fun Array<String>.sh() = Shell.sh(*this).to(console, logs).exec()
|
||||||
private fun String.fsh() = ShellUtils.fastCmd(this)
|
private fun String.fsh() = ShellUtils.fastCmd(this)
|
||||||
private fun Array<String>.fsh() = ShellUtils.fastCmd(*this)
|
private fun Array<String>.fsh() = ShellUtils.fastCmd(*this)
|
||||||
|
|
||||||
protected fun doPatchFile(patchFile: Uri) = extractZip() && handleFile(patchFile)
|
protected fun doPatchFile(patchFile: Uri) = extractFiles() && handleFile(patchFile)
|
||||||
|
|
||||||
protected fun direct() = findImage() && extractZip() && patchBoot() &&
|
protected fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot()
|
||||||
copySepolicyRules() && flashBoot()
|
|
||||||
|
|
||||||
protected suspend fun secondSlot() = findSecondaryImage() && extractZip() &&
|
protected suspend fun secondSlot() =
|
||||||
patchBoot() && copySepolicyRules() && flashBoot() && postOTA()
|
findSecondaryImage() && extractFiles() && patchBoot() && flashBoot() && postOTA()
|
||||||
|
|
||||||
protected fun fixEnv(): Boolean {
|
protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess
|
||||||
installDir = SuFile("/data/adb/magisk")
|
|
||||||
Shell.su("rm -rf /data/adb/magisk/*").exec()
|
|
||||||
return extractZip() && Shell.su("fix_env").exec().isSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
protected abstract suspend fun operations(): Boolean
|
protected abstract suspend fun operations(): Boolean
|
||||||
@ -429,85 +428,84 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class MagiskInstaller(
|
abstract class MagiskInstaller(
|
||||||
zip: Uri,
|
|
||||||
console: MutableList<String>,
|
console: MutableList<String>,
|
||||||
logs: MutableList<String>
|
logs: MutableList<String>
|
||||||
) : MagiskInstallImpl(zip, console, logs) {
|
) : MagiskInstallImpl(console, logs) {
|
||||||
|
|
||||||
init {
|
|
||||||
installDir = File(get<Context>(Protected).filesDir.parent, "install")
|
|
||||||
"rm -rf $installDir".sh()
|
|
||||||
installDir.mkdirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun exec(): Boolean {
|
override suspend fun exec(): Boolean {
|
||||||
val success = super.exec()
|
val success = super.exec()
|
||||||
if (success) {
|
if (success) {
|
||||||
console.add("- All done!")
|
console.add("- All done!")
|
||||||
} else {
|
} else {
|
||||||
Shell.sh("rm -rf $installDir").submit()
|
if (installDir is SuFile) {
|
||||||
|
Shell.sh("rm -rf ${Const.TMPDIR}").submit()
|
||||||
|
} else {
|
||||||
|
Shell.sh("rm -rf $installDir").submit()
|
||||||
|
}
|
||||||
console.add("! Installation failed")
|
console.add("! Installation failed")
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
class Patch(
|
class Patch(
|
||||||
zip: Uri,
|
|
||||||
private val uri: Uri,
|
private val uri: Uri,
|
||||||
console: MutableList<String>,
|
console: MutableList<String>,
|
||||||
logs: MutableList<String>
|
logs: MutableList<String>
|
||||||
) : MagiskInstaller(zip, console, logs) {
|
) : MagiskInstaller(console, logs) {
|
||||||
override suspend fun operations() = doPatchFile(uri)
|
override suspend fun operations() = doPatchFile(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SecondSlot(
|
class SecondSlot(
|
||||||
zip: Uri,
|
|
||||||
console: MutableList<String>,
|
console: MutableList<String>,
|
||||||
logs: MutableList<String>
|
logs: MutableList<String>
|
||||||
) : MagiskInstaller(zip, console, logs) {
|
) : MagiskInstaller(console, logs) {
|
||||||
override suspend fun operations() = secondSlot()
|
override suspend fun operations() = secondSlot()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Direct(
|
class Direct(
|
||||||
zip: Uri,
|
|
||||||
console: MutableList<String>,
|
console: MutableList<String>,
|
||||||
logs: MutableList<String>
|
logs: MutableList<String>
|
||||||
) : MagiskInstaller(zip, console, logs) {
|
) : MagiskInstaller(console, logs) {
|
||||||
override suspend fun operations() = direct()
|
override suspend fun operations() = direct()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Emulator(
|
class Emulator(
|
||||||
zip: Uri,
|
|
||||||
console: MutableList<String>,
|
console: MutableList<String>,
|
||||||
logs: MutableList<String>
|
logs: MutableList<String>
|
||||||
) : MagiskInstallImpl(zip, console, logs) {
|
) : MagiskInstaller(console, logs) {
|
||||||
override suspend fun operations() = fixEnv()
|
override suspend fun operations() = fixEnv()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Uninstall(
|
||||||
|
console: MutableList<String>,
|
||||||
|
logs: MutableList<String>
|
||||||
|
) : MagiskInstallImpl(console, logs) {
|
||||||
|
override suspend fun operations() = uninstall()
|
||||||
|
|
||||||
override suspend fun exec(): Boolean {
|
override suspend fun exec(): Boolean {
|
||||||
val success = super.exec()
|
val success = super.exec()
|
||||||
if (success) {
|
if (success) {
|
||||||
console.add("- All done!")
|
UiThreadHandler.handler.postDelayed(3000) {
|
||||||
} else {
|
Shell.su("pm uninstall ${context.packageName}").exec()
|
||||||
console.add("! Installation failed")
|
}
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class EnvFixTask(zip: Uri) : MagiskInstallImpl(zip) {
|
class FixEnv(private val callback: () -> Unit) : MagiskInstallImpl() {
|
||||||
|
override suspend fun operations() = fixEnv()
|
||||||
|
|
||||||
override suspend fun operations() = fixEnv()
|
override suspend fun exec(): Boolean {
|
||||||
|
val success = super.exec()
|
||||||
override suspend fun exec(): Boolean {
|
callback()
|
||||||
val success = super.exec()
|
Utils.toast(
|
||||||
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(EnvFixDialog.DISMISS))
|
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
|
||||||
Utils.toast(
|
Toast.LENGTH_LONG
|
||||||
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
|
)
|
||||||
Toast.LENGTH_LONG
|
if (success)
|
||||||
)
|
UiThreadHandler.handler.postDelayed(5000) { reboot() }
|
||||||
if (success)
|
return success
|
||||||
UiThreadHandler.handler.postDelayed(5000) { reboot() }
|
}
|
||||||
return success
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,17 @@ package com.topjohnwu.magisk.core.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import com.topjohnwu.magisk.DynAPK
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.*
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.ktx.cachedFile
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.ktx.deviceProtectedContext
|
||||||
import com.topjohnwu.magisk.core.wrap
|
|
||||||
import com.topjohnwu.magisk.ktx.rawResource
|
import com.topjohnwu.magisk.ktx.rawResource
|
||||||
|
import com.topjohnwu.magisk.ktx.writeTo
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
class RootInit : Shell.Initializer() {
|
class RootInit : Shell.Initializer() {
|
||||||
|
|
||||||
@ -17,31 +20,50 @@ class RootInit : Shell.Initializer() {
|
|||||||
return init(context.wrap(), shell)
|
return init(context.wrap(), shell)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(context: Context, shell: Shell): Boolean {
|
private fun init(context: Context, shell: Shell): Boolean {
|
||||||
shell.newJob().apply {
|
|
||||||
add("export SDK_INT=${Build.VERSION.SDK_INT}")
|
|
||||||
if (Const.Version.atLeast_20_4()) {
|
|
||||||
add("export MAGISKTMP=\$(magisk --path)/.magisk")
|
|
||||||
} else {
|
|
||||||
add("export MAGISKTMP=/sbin/.magisk")
|
|
||||||
}
|
|
||||||
if (Const.Version.atLeast_21_0()) {
|
|
||||||
add("export ASH_STANDALONE=1")
|
|
||||||
add("[ -x /data/adb/magisk/busybox ] && exec /data/adb/magisk/busybox sh")
|
|
||||||
} else {
|
|
||||||
add("export PATH=\"\$MAGISKTMP/busybox:\$PATH\"")
|
|
||||||
}
|
|
||||||
add(context.rawResource(R.raw.manager))
|
|
||||||
if (shell.isRoot) {
|
|
||||||
add(context.rawResource(R.raw.util_functions))
|
|
||||||
}
|
|
||||||
add("mm_init")
|
|
||||||
}.exec()
|
|
||||||
|
|
||||||
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
|
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
|
||||||
fun getVar(name: String) = fastCmd("echo \$$name")
|
fun getVar(name: String) = fastCmd("echo \$$name")
|
||||||
fun getBool(name: String) = getVar(name).toBoolean()
|
fun getBool(name: String) = getVar(name).toBoolean()
|
||||||
|
|
||||||
|
shell.newJob().apply {
|
||||||
|
add("export SDK_INT=${Build.VERSION.SDK_INT}")
|
||||||
|
add("export ASH_STANDALONE=1")
|
||||||
|
|
||||||
|
val localBB: File
|
||||||
|
if (isRunningAsStub) {
|
||||||
|
val jar = JarFile(DynAPK.current(context))
|
||||||
|
val bb = jar.getJarEntry("lib/${Const.CPU_ABI_32}/libbusybox.so")
|
||||||
|
localBB = context.deviceProtectedContext.cachedFile("busybox")
|
||||||
|
jar.getInputStream(bb).writeTo(localBB)
|
||||||
|
localBB.setExecutable(true)
|
||||||
|
} else {
|
||||||
|
localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shell.isRoot) {
|
||||||
|
// Directly execute the file
|
||||||
|
add("exec $localBB sh")
|
||||||
|
} else {
|
||||||
|
// Copy it out of /data to workaround Samsung BS
|
||||||
|
add(
|
||||||
|
"export MAGISKTMP=\$(magisk --path)/.magisk",
|
||||||
|
"if [ -x \$MAGISKTMP/busybox/busybox ]; then",
|
||||||
|
" cp -af $localBB \$MAGISKTMP/busybox/busybox",
|
||||||
|
" exec \$MAGISKTMP/busybox/busybox sh",
|
||||||
|
"else",
|
||||||
|
" exec $localBB sh",
|
||||||
|
"fi"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
add(context.rawResource(R.raw.manager))
|
||||||
|
if (shell.isRoot) {
|
||||||
|
add(context.assets.open("util_functions.sh"))
|
||||||
|
}
|
||||||
|
add("mm_init")
|
||||||
|
}.exec()
|
||||||
|
|
||||||
Const.MAGISKTMP = getVar("MAGISKTMP")
|
Const.MAGISKTMP = getVar("MAGISKTMP")
|
||||||
Info.isSAR = getBool("SYSTEM_ROOT")
|
Info.isSAR = getBool("SYSTEM_ROOT")
|
||||||
Info.ramdisk = getBool("RAMDISKEXIST")
|
Info.ramdisk = getBool("RAMDISKEXIST")
|
||||||
|
@ -110,7 +110,7 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
|||||||
activity.startActivityForResult(intent, Const.ID.FETCH_ZIP) { code, intent ->
|
activity.startActivityForResult(intent, Const.ID.FETCH_ZIP) { code, intent ->
|
||||||
if (code == Activity.RESULT_OK && intent != null) {
|
if (code == Activity.RESULT_OK && intent != null) {
|
||||||
intent.data?.also {
|
intent.data?.also {
|
||||||
MainDirections.actionFlashFragment(it, Const.Value.FLASH_ZIP).navigate()
|
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,9 @@ abstract class DialogEvent : ViewEvent(), ActivityExecutor {
|
|||||||
protected lateinit var dialog: MagiskDialog
|
protected lateinit var dialog: MagiskDialog
|
||||||
|
|
||||||
override fun invoke(activity: BaseUIActivity<*, *>) {
|
override fun invoke(activity: BaseUIActivity<*, *>) {
|
||||||
dialog = MagiskDialog(activity).apply(this::build).reveal()
|
dialog = MagiskDialog(activity)
|
||||||
|
.apply { setOwnerActivity(activity) }
|
||||||
|
.apply(this::build).reveal()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun build(dialog: MagiskDialog)
|
abstract fun build(dialog: MagiskDialog)
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package com.topjohnwu.magisk.events.dialog
|
package com.topjohnwu.magisk.events.dialog
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import androidx.lifecycle.lifecycleScope
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.download.Action.EnvFix
|
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||||
import com.topjohnwu.magisk.core.download.DownloadService
|
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||||
import com.topjohnwu.magisk.core.download.Subject.Magisk
|
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class EnvFixDialog : DialogEvent() {
|
class EnvFixDialog : DialogEvent() {
|
||||||
|
|
||||||
@ -24,20 +20,17 @@ class EnvFixDialog : DialogEvent() {
|
|||||||
.applyMessage(R.string.setup_msg)
|
.applyMessage(R.string.setup_msg)
|
||||||
.resetButtons()
|
.resetButtons()
|
||||||
.cancellable(false)
|
.cancellable(false)
|
||||||
val lbm = LocalBroadcastManager.getInstance(dialog.context)
|
(dialog.ownerActivity as BaseActivity).lifecycleScope.launch {
|
||||||
lbm.registerReceiver(object : BroadcastReceiver() {
|
MagiskInstaller.FixEnv {
|
||||||
override fun onReceive(context: Context, intent: Intent?) {
|
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
lbm.unregisterReceiver(this)
|
}.exec()
|
||||||
}
|
}
|
||||||
}, IntentFilter(DISMISS))
|
|
||||||
DownloadService.start(dialog.context, Magisk(EnvFix))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||||
titleRes = android.R.string.cancel
|
titleRes = android.R.string.cancel
|
||||||
}
|
}
|
||||||
.let { Unit }
|
.let { }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DISMISS = "com.topjohnwu.magisk.ENV_DONE"
|
const val DISMISS = "com.topjohnwu.magisk.ENV_DONE"
|
||||||
|
@ -14,7 +14,7 @@ class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() {
|
|||||||
with(dialog) {
|
with(dialog) {
|
||||||
|
|
||||||
fun download(install: Boolean) {
|
fun download(install: Boolean) {
|
||||||
val config = if (install) Action.Flash.Primary else Action.Download
|
val config = if (install) Action.Flash else Action.Download
|
||||||
val subject = Subject.Module(item, config)
|
val subject = Subject.Module(item, config)
|
||||||
DownloadService.start(context, subject)
|
DownloadService.start(context, subject)
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import android.app.ProgressDialog
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.download.Action
|
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||||
import com.topjohnwu.magisk.core.download.DownloadService
|
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
@ -46,7 +44,7 @@ class UninstallDialog : DialogEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun completeUninstall() {
|
private fun completeUninstall() {
|
||||||
DownloadService.start(dialog.context, Subject.Magisk(Action.Uninstall))
|
FlashFragment.uninstall()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import android.graphics.drawable.LayerDrawable
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
|
import android.system.Os
|
||||||
import android.text.PrecomputedText
|
import android.text.PrecomputedText
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -59,6 +60,20 @@ import java.lang.reflect.Array as JArray
|
|||||||
|
|
||||||
val packageName: String get() = get<Context>().packageName
|
val packageName: String get() = get<Context>().packageName
|
||||||
|
|
||||||
|
fun symlink(oldPath: String, newPath: String) {
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
Os.symlink(oldPath, newPath)
|
||||||
|
} else {
|
||||||
|
// Just copy the files pre 5.0
|
||||||
|
val old = File(oldPath)
|
||||||
|
val newFile = File(newPath)
|
||||||
|
old.copyTo(newFile)
|
||||||
|
if (old.canExecute())
|
||||||
|
newFile.setExecutable(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
val ServiceInfo.isIsolated get() = (flags and FLAG_ISOLATED_PROCESS) != 0
|
val ServiceInfo.isIsolated get() = (flags and FLAG_ISOLATED_PROCESS) != 0
|
||||||
|
|
||||||
@get:SuppressLint("InlinedApi")
|
@get:SuppressLint("InlinedApi")
|
||||||
@ -83,6 +98,11 @@ fun Context.getBitmap(id: Int): Bitmap {
|
|||||||
return bitmap
|
return bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val Context.deviceProtectedContext: Context get() =
|
||||||
|
if (SDK_INT >= 24) {
|
||||||
|
createDeviceProtectedStorageContext()
|
||||||
|
} else { this }
|
||||||
|
|
||||||
fun Intent.startActivity(context: Context) = context.startActivity(this)
|
fun Intent.startActivity(context: Context) = context.startActivity(this)
|
||||||
|
|
||||||
fun Intent.startActivityWithRoot() {
|
fun Intent.startActivityWithRoot() {
|
||||||
|
@ -17,13 +17,12 @@ import com.topjohnwu.magisk.ui.MainActivity
|
|||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import com.topjohnwu.magisk.MainDirections.Companion.actionFlashFragment as toFlash
|
import com.topjohnwu.magisk.MainDirections.Companion.actionFlashFragment as toFlash
|
||||||
import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs as args
|
|
||||||
|
|
||||||
class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>() {
|
class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>() {
|
||||||
|
|
||||||
override val layoutRes = R.layout.fragment_flash_md2
|
override val layoutRes = R.layout.fragment_flash_md2
|
||||||
override val viewModel by viewModel<FlashViewModel> {
|
override val viewModel by viewModel<FlashViewModel> {
|
||||||
parametersOf(args.fromBundle(requireArguments()))
|
parametersOf(FlashFragmentArgs.fromBundle(requireArguments()))
|
||||||
}
|
}
|
||||||
|
|
||||||
private var defaultOrientation = -1
|
private var defaultOrientation = -1
|
||||||
@ -78,7 +77,7 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private fun createIntent(context: Context, args: args) =
|
private fun createIntent(context: Context, args: FlashFragmentArgs) =
|
||||||
NavDeepLinkBuilder(context)
|
NavDeepLinkBuilder(context)
|
||||||
.setGraph(R.navigation.main)
|
.setGraph(R.navigation.main)
|
||||||
.setComponentName(MainActivity::class.java.cmp(context.packageName))
|
.setComponentName(MainActivity::class.java.cmp(context.packageName))
|
||||||
@ -91,59 +90,34 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
|
|||||||
|
|
||||||
/* Flashing is understood as installing / flashing magisk itself */
|
/* Flashing is understood as installing / flashing magisk itself */
|
||||||
|
|
||||||
fun flashIntent(context: Context, file: Uri, isSecondSlot: Boolean, id: Int = -1) = args(
|
fun flash(isSecondSlot: Boolean) = toFlash(
|
||||||
installer = file,
|
action = flashType(isSecondSlot)
|
||||||
action = flashType(isSecondSlot),
|
|
||||||
dismissId = id
|
|
||||||
).let { createIntent(context, it) }
|
|
||||||
|
|
||||||
fun flash(file: Uri, isSecondSlot: Boolean, id: Int) = toFlash(
|
|
||||||
installer = file,
|
|
||||||
action = flashType(isSecondSlot),
|
|
||||||
dismissId = id
|
|
||||||
).let { BaseUIActivity.postDirections(it) }
|
).let { BaseUIActivity.postDirections(it) }
|
||||||
|
|
||||||
/* Patching is understood as injecting img files with magisk */
|
/* Patching is understood as injecting img files with magisk */
|
||||||
|
|
||||||
fun patchIntent(context: Context, file: Uri, uri: Uri, id: Int = -1) = args(
|
fun patch(uri: Uri) = toFlash(
|
||||||
installer = file,
|
|
||||||
action = Const.Value.PATCH_FILE,
|
action = Const.Value.PATCH_FILE,
|
||||||
additionalData = uri,
|
additionalData = uri
|
||||||
dismissId = id
|
|
||||||
).let { createIntent(context, it) }
|
|
||||||
|
|
||||||
fun patch(file: Uri, uri: Uri, id: Int) = toFlash(
|
|
||||||
installer = file,
|
|
||||||
action = Const.Value.PATCH_FILE,
|
|
||||||
additionalData = uri,
|
|
||||||
dismissId = id
|
|
||||||
).let { BaseUIActivity.postDirections(it) }
|
).let { BaseUIActivity.postDirections(it) }
|
||||||
|
|
||||||
/* Uninstalling is understood as removing magisk entirely */
|
/* Uninstalling is understood as removing magisk entirely */
|
||||||
|
|
||||||
fun uninstallIntent(context: Context, file: Uri, id: Int = -1) = args(
|
fun uninstall() = toFlash(
|
||||||
installer = file,
|
action = Const.Value.UNINSTALL
|
||||||
action = Const.Value.UNINSTALL,
|
|
||||||
dismissId = id
|
|
||||||
).let { createIntent(context, it) }
|
|
||||||
|
|
||||||
fun uninstall(file: Uri, id: Int) = toFlash(
|
|
||||||
installer = file,
|
|
||||||
action = Const.Value.UNINSTALL,
|
|
||||||
dismissId = id
|
|
||||||
).let { BaseUIActivity.postDirections(it) }
|
).let { BaseUIActivity.postDirections(it) }
|
||||||
|
|
||||||
/* Installing is understood as flashing modules / zips */
|
/* Installing is understood as flashing modules / zips */
|
||||||
|
|
||||||
fun installIntent(context: Context, file: Uri, id: Int = -1) = args(
|
fun installIntent(context: Context, file: Uri, id: Int = -1) = FlashFragmentArgs(
|
||||||
installer = file,
|
|
||||||
action = Const.Value.FLASH_ZIP,
|
action = Const.Value.FLASH_ZIP,
|
||||||
|
additionalData = file,
|
||||||
dismissId = id
|
dismissId = id
|
||||||
).let { createIntent(context, it) }
|
).let { createIntent(context, it) }
|
||||||
|
|
||||||
fun install(file: Uri, id: Int) = toFlash(
|
fun install(file: Uri, id: Int) = toFlash(
|
||||||
installer = file,
|
|
||||||
action = Const.Value.FLASH_ZIP,
|
action = Const.Value.FLASH_ZIP,
|
||||||
|
additionalData = file,
|
||||||
dismissId = id
|
dismissId = id
|
||||||
).let { BaseUIActivity.postDirections(it) }
|
).let { BaseUIActivity.postDirections(it) }
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,7 @@ import com.topjohnwu.superuser.Shell
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class FlashViewModel(
|
class FlashViewModel(args: FlashFragmentArgs) : BaseViewModel() {
|
||||||
args: FlashFragmentArgs
|
|
||||||
) : BaseViewModel() {
|
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var showReboot = Shell.rootAccess()
|
var showReboot = Shell.rootAccess()
|
||||||
@ -52,36 +50,35 @@ class FlashViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
args.dismissId.takeIf { it != -1 }?.also {
|
val (action, uri, id) = args
|
||||||
Notifications.mgr.cancel(it)
|
if (id != -1)
|
||||||
}
|
Notifications.mgr.cancel(id)
|
||||||
val (installer, action, uri) = args
|
startFlashing(action, uri)
|
||||||
startFlashing(installer, uri, action)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startFlashing(installer: Uri, uri: Uri?, action: String) {
|
private fun startFlashing(action: String, uri: Uri?) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val result = when (action) {
|
val result = when (action) {
|
||||||
Const.Value.FLASH_ZIP -> {
|
Const.Value.FLASH_ZIP -> {
|
||||||
FlashZip(installer, outItems, logItems).exec()
|
FlashZip(uri!!, outItems, logItems).exec()
|
||||||
}
|
}
|
||||||
Const.Value.UNINSTALL -> {
|
Const.Value.UNINSTALL -> {
|
||||||
showReboot = false
|
showReboot = false
|
||||||
FlashZip.Uninstall(installer, outItems, logItems).exec()
|
MagiskInstaller.Uninstall(outItems, logItems).exec()
|
||||||
}
|
}
|
||||||
Const.Value.FLASH_MAGISK -> {
|
Const.Value.FLASH_MAGISK -> {
|
||||||
if (Info.isEmulator)
|
if (Info.isEmulator)
|
||||||
MagiskInstaller.Emulator(installer, outItems, logItems).exec()
|
MagiskInstaller.Emulator(outItems, logItems).exec()
|
||||||
else
|
else
|
||||||
MagiskInstaller.Direct(installer, outItems, logItems).exec()
|
MagiskInstaller.Direct(outItems, logItems).exec()
|
||||||
}
|
}
|
||||||
Const.Value.FLASH_INACTIVE_SLOT -> {
|
Const.Value.FLASH_INACTIVE_SLOT -> {
|
||||||
MagiskInstaller.SecondSlot(installer, outItems, logItems).exec()
|
MagiskInstaller.SecondSlot(outItems, logItems).exec()
|
||||||
}
|
}
|
||||||
Const.Value.PATCH_FILE -> {
|
Const.Value.PATCH_FILE -> {
|
||||||
uri ?: return@launch
|
uri ?: return@launch
|
||||||
showReboot = false
|
showReboot = false
|
||||||
MagiskInstaller.Patch(installer, uri, outItems, logItems).exec()
|
MagiskInstaller.Patch(uri, outItems, logItems).exec()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
back()
|
back()
|
||||||
|
@ -7,7 +7,6 @@ import android.view.ViewGroup
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||||
import com.topjohnwu.magisk.core.download.BaseDownloader
|
|
||||||
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
|
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
|
||||||
import com.topjohnwu.magisk.ktx.coroutineScope
|
import com.topjohnwu.magisk.ktx.coroutineScope
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
@ -23,7 +22,6 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
|
|||||||
|
|
||||||
// Allow markwon to run in viewmodel scope
|
// Allow markwon to run in viewmodel scope
|
||||||
binding.releaseNotes.coroutineScope = viewModel.viewModelScope
|
binding.releaseNotes.coroutineScope = viewModel.viewModelScope
|
||||||
BaseDownloader.observeProgress(this, viewModel::onProgressUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -8,23 +8,19 @@ import com.topjohnwu.magisk.BR
|
|||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.download.Action
|
|
||||||
import com.topjohnwu.magisk.core.download.DownloadService
|
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||||
import com.topjohnwu.magisk.events.MagiskInstallFileEvent
|
import com.topjohnwu.magisk.events.MagiskInstallFileEvent
|
||||||
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
|
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
|
||||||
|
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||||
import com.topjohnwu.magisk.utils.set
|
import com.topjohnwu.magisk.utils.set
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.core.get
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class InstallViewModel(
|
class InstallViewModel(
|
||||||
svc: NetworkService
|
svc: NetworkService
|
||||||
) : BaseViewModel(State.LOADED) {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
val isRooted = Shell.rootAccess()
|
val isRooted = Shell.rootAccess()
|
||||||
val skipOptions = Info.isEmulator || (Info.ramdisk && !Info.isFDE && Info.isSAR)
|
val skipOptions = Info.isEmulator || (Info.ramdisk && !Info.isFDE && Info.isSAR)
|
||||||
@ -53,10 +49,6 @@ class InstallViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@get:Bindable
|
|
||||||
var progress = 0
|
|
||||||
set(value) = set(value, field, { field = it }, BR.progress)
|
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var data: Uri? = null
|
var data: Uri? = null
|
||||||
set(value) = set(value, field, { field = it }, BR.data)
|
set(value) = set(value, field, { field = it }, BR.data)
|
||||||
@ -75,34 +67,17 @@ class InstallViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onProgressUpdate(progress: Float, subject: Subject) {
|
|
||||||
if (subject !is Subject.Magisk) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.progress = progress.times(100).roundToInt()
|
|
||||||
if (this.progress >= 100) {
|
|
||||||
state = State.LOADED
|
|
||||||
} else if (this.progress < -150) {
|
|
||||||
state = State.LOADING_FAILED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun step(nextStep: Int) {
|
fun step(nextStep: Int) {
|
||||||
step = nextStep
|
step = nextStep
|
||||||
}
|
}
|
||||||
|
|
||||||
fun install() {
|
fun install() {
|
||||||
DownloadService.start(get(), Subject.Magisk(resolveAction()))
|
when (method) {
|
||||||
|
R.id.method_patch -> FlashFragment.patch(data!!)
|
||||||
|
R.id.method_direct -> FlashFragment.flash(false)
|
||||||
|
R.id.method_inactive_slot -> FlashFragment.flash(true)
|
||||||
|
else -> error("Unknown value")
|
||||||
|
}
|
||||||
state = State.LOADING
|
state = State.LOADING
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
private fun resolveAction() = when (method) {
|
|
||||||
R.id.method_download -> Action.Download
|
|
||||||
R.id.method_patch -> Action.Patch(data!!)
|
|
||||||
R.id.method_direct -> Action.Flash.Primary
|
|
||||||
R.id.method_inactive_slot -> Action.Flash.Secondary
|
|
||||||
else -> error("Unknown value")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,306 +25,217 @@
|
|||||||
app:fitsSystemWindowsInsets="top|bottom"
|
app:fitsSystemWindowsInsets="top|bottom"
|
||||||
tools:paddingTop="24dp">
|
tools:paddingTop="24dp">
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="@dimen/l_50">
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.card.MaterialCardView
|
||||||
goneUnless="@{viewModel.loaded && viewModel.progress <= 0}"
|
style="@style/WidgetFoundation.Card"
|
||||||
|
gone="@{viewModel.skipOptions}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="@dimen/l_50">
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="@style/WidgetFoundation.Card"
|
|
||||||
gone="@{viewModel.skipOptions}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_marginEnd="@dimen/l1"
|
|
||||||
android:focusable="false">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
style="@style/WidgetFoundation.Icon"
|
|
||||||
isSelected="@{viewModel.step > 0}"
|
|
||||||
android:layout_marginStart="@dimen/l_25"
|
|
||||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:text="@string/install_options_title"
|
|
||||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/WidgetFoundation.Button.Text"
|
|
||||||
gone="@{viewModel.step != 0}"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="@{() -> viewModel.step(1)}"
|
|
||||||
android:text="@string/install_next" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
gone="@{viewModel.step != 0}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_marginTop="@dimen/l_50"
|
|
||||||
android:layout_marginEnd="@dimen/l1"
|
|
||||||
android:layout_marginBottom="@dimen/l_50"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingStart="3dp"
|
|
||||||
android:paddingEnd="3dp"
|
|
||||||
tools:layout_gravity="center">
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
style="@style/WidgetFoundation.Checkbox"
|
|
||||||
gone="@{Info.isSAR}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:checked="@={Config.keepVerity}"
|
|
||||||
android:text="@string/keep_dm_verity"
|
|
||||||
tools:checked="true" />
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
style="@style/WidgetFoundation.Checkbox"
|
|
||||||
goneUnless="@{Info.isFDE}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:checked="@={Config.keepEnc}"
|
|
||||||
android:text="@string/keep_force_encryption"
|
|
||||||
app:tint="?colorPrimary" />
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
style="@style/WidgetFoundation.Checkbox"
|
|
||||||
gone="@{Info.ramdisk}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:checked="@={Config.recovery}"
|
|
||||||
android:text="@string/recovery_mode"
|
|
||||||
app:tint="?colorPrimary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="@style/WidgetFoundation.Card"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_marginTop="@dimen/l1"
|
|
||||||
android:layout_marginEnd="@dimen/l1"
|
|
||||||
android:focusable="false">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
style="@style/WidgetFoundation.Icon"
|
|
||||||
isSelected="@{viewModel.step > 1}"
|
|
||||||
android:layout_marginStart="@dimen/l_25"
|
|
||||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:text="@string/install_method_title"
|
|
||||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/WidgetFoundation.Button.Text"
|
|
||||||
gone="@{viewModel.step != 1}"
|
|
||||||
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="@{() -> viewModel.install()}"
|
|
||||||
android:text="@string/install_start"
|
|
||||||
app:icon="@drawable/ic_forth_md2"
|
|
||||||
app:iconGravity="textEnd" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
gone="@{viewModel.step != 1}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_marginTop="@dimen/l_50"
|
|
||||||
android:layout_marginEnd="@dimen/l1"
|
|
||||||
android:layout_marginBottom="@dimen/l_50"
|
|
||||||
android:checkedButton="@={viewModel.method}"
|
|
||||||
android:paddingStart="3dp"
|
|
||||||
android:paddingEnd="3dp">
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/method_download"
|
|
||||||
style="@style/WidgetFoundation.RadioButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/download_zip_only"
|
|
||||||
tools:checked="true" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/method_patch"
|
|
||||||
style="@style/WidgetFoundation.RadioButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/select_patch_file" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/method_direct"
|
|
||||||
style="@style/WidgetFoundation.RadioButton"
|
|
||||||
gone="@{!viewModel.rooted}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/direct_install" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/method_inactive_slot"
|
|
||||||
style="@style/WidgetFoundation.RadioButton"
|
|
||||||
gone="@{viewModel.noSecondSlot}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/install_inactive_slot" />
|
|
||||||
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="@style/WidgetFoundation.Card"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/l1"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
|
||||||
android:layout_marginEnd="@dimen/l1"
|
|
||||||
android:focusable="false">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/release_notes"
|
|
||||||
markdownText="@{viewModel.notes}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="15dp"
|
|
||||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:maxLines="5"
|
|
||||||
tools:text="@tools:sample/lorem/random"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
goneUnless="@{viewModel.loading}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginStart="@dimen/l1"
|
android:layout_marginStart="@dimen/l1"
|
||||||
android:layout_marginEnd="@dimen/l1"
|
android:layout_marginEnd="@dimen/l1"
|
||||||
android:gravity="center"
|
android:focusable="false">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:orientation="vertical">
|
||||||
android:text="@string/loading"
|
|
||||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/l1">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<ProgressBar
|
<ImageView
|
||||||
style="@style/WidgetFoundation.ProgressBar"
|
style="@style/WidgetFoundation.Icon"
|
||||||
invisible="@{viewModel.progress <= 0}"
|
isSelected="@{viewModel.step > 0}"
|
||||||
android:layout_width="100dp"
|
android:layout_marginStart="@dimen/l_25"
|
||||||
android:layout_gravity="center"
|
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||||
android:progress="@{viewModel.progress}" />
|
|
||||||
|
|
||||||
<ProgressBar
|
<TextView
|
||||||
style="@style/WidgetFoundation.ProgressBar.Indeterminate"
|
android:layout_width="0dp"
|
||||||
invisibleUnless="@{viewModel.progress <= 0}"
|
android:layout_height="match_parent"
|
||||||
android:layout_width="100dp"
|
android:layout_marginStart="@dimen/l1"
|
||||||
android:layout_gravity="center" />
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/install_options_title"
|
||||||
|
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
</FrameLayout>
|
<Button
|
||||||
|
style="@style/WidgetFoundation.Button.Text"
|
||||||
|
gone="@{viewModel.step != 0}"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="@{() -> viewModel.step(1)}"
|
||||||
|
android:text="@string/install_next" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
<LinearLayout
|
||||||
|
gone="@{viewModel.step != 0}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/l1"
|
||||||
|
android:layout_marginTop="@dimen/l_50"
|
||||||
|
android:layout_marginEnd="@dimen/l1"
|
||||||
|
android:layout_marginBottom="@dimen/l_50"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingEnd="3dp"
|
||||||
|
tools:layout_gravity="center">
|
||||||
|
|
||||||
<LinearLayout
|
<CheckBox
|
||||||
goneUnless="@{viewModel.loaded && viewModel.progress >= 100}"
|
style="@style/WidgetFoundation.Checkbox"
|
||||||
|
gone="@{Info.isSAR}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="@={Config.keepVerity}"
|
||||||
|
android:text="@string/keep_dm_verity"
|
||||||
|
tools:checked="true" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
style="@style/WidgetFoundation.Checkbox"
|
||||||
|
goneUnless="@{Info.isFDE}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="@={Config.keepEnc}"
|
||||||
|
android:text="@string/keep_force_encryption"
|
||||||
|
app:tint="?colorPrimary" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
style="@style/WidgetFoundation.Checkbox"
|
||||||
|
gone="@{Info.ramdisk}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="@={Config.recovery}"
|
||||||
|
android:text="@string/recovery_mode"
|
||||||
|
app:tint="?colorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/WidgetFoundation.Card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_marginStart="@dimen/l1"
|
||||||
android:gravity="center"
|
android:layout_marginTop="@dimen/l1"
|
||||||
android:orientation="vertical">
|
android:layout_marginEnd="@dimen/l1"
|
||||||
|
android:focusable="false">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:orientation="vertical">
|
||||||
android:text="@string/download_complete"
|
|
||||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
goneUnless="@{viewModel.loadFailed}"
|
style="@style/WidgetFoundation.Icon"
|
||||||
|
isSelected="@{viewModel.step > 1}"
|
||||||
|
android:layout_marginStart="@dimen/l_25"
|
||||||
|
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="@dimen/l1"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/install_method_title"
|
||||||
|
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/WidgetFoundation.Button.Text"
|
||||||
|
gone="@{viewModel.step != 1}"
|
||||||
|
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="@{() -> viewModel.install()}"
|
||||||
|
android:text="@string/install_start"
|
||||||
|
app:icon="@drawable/ic_forth_md2"
|
||||||
|
app:iconGravity="textEnd" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
gone="@{viewModel.step != 1}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/l1"
|
||||||
|
android:layout_marginTop="@dimen/l_50"
|
||||||
|
android:layout_marginEnd="@dimen/l1"
|
||||||
|
android:layout_marginBottom="@dimen/l_50"
|
||||||
|
android:checkedButton="@={viewModel.method}"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingEnd="3dp">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/method_patch"
|
||||||
|
style="@style/WidgetFoundation.RadioButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/select_patch_file" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/method_direct"
|
||||||
|
style="@style/WidgetFoundation.RadioButton"
|
||||||
|
gone="@{!viewModel.rooted}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/direct_install" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/method_inactive_slot"
|
||||||
|
style="@style/WidgetFoundation.RadioButton"
|
||||||
|
gone="@{viewModel.noSecondSlot}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/install_inactive_slot" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/WidgetFoundation.Card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_marginTop="@dimen/l1"
|
||||||
android:gravity="center"
|
android:layout_marginStart="@dimen/l1"
|
||||||
android:orientation="vertical">
|
android:layout_marginEnd="@dimen/l1"
|
||||||
|
android:focusable="false">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/release_notes"
|
||||||
|
markdownText="@{viewModel.notes}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:layout_margin="15dp"
|
||||||
android:text="@string/download_file_error"
|
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
android:visibility="gone"
|
||||||
|
tools:maxLines="5"
|
||||||
|
tools:text="@tools:sample/lorem/random"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</LinearLayout>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</FrameLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
@ -49,10 +49,6 @@
|
|||||||
android:label="FlashFragment"
|
android:label="FlashFragment"
|
||||||
tools:layout="@layout/fragment_flash_md2">
|
tools:layout="@layout/fragment_flash_md2">
|
||||||
|
|
||||||
<argument
|
|
||||||
android:name="installer"
|
|
||||||
app:argType="android.net.Uri" />
|
|
||||||
|
|
||||||
<argument
|
<argument
|
||||||
android:name="action"
|
android:name="action"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
|
1
app/src/main/res/raw/.gitignore
vendored
1
app/src/main/res/raw/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/util_functions.sh
|
|
@ -13,21 +13,35 @@ env_check() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fix_env() {
|
cp_readlink() {
|
||||||
cd $MAGISKBIN
|
if [ -z $2 ]; then
|
||||||
PATH=/system/bin /system/bin/sh update-binary -x
|
cd $1
|
||||||
./busybox rm -f update-binary magisk.apk
|
else
|
||||||
./busybox chmod -R 755 .
|
cp -af $1/. $2
|
||||||
./magiskinit -x magisk magisk
|
cd $2
|
||||||
|
fi
|
||||||
|
for file in *; do
|
||||||
|
if [ -L $file ]; then
|
||||||
|
local full=$(readlink -f $file)
|
||||||
|
rm $file
|
||||||
|
cp -af $full $file
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
chmod -R 755 .
|
||||||
cd /
|
cd /
|
||||||
}
|
}
|
||||||
|
|
||||||
direct_install() {
|
fix_env() {
|
||||||
rm -rf $MAGISKBIN/* 2>/dev/null
|
# Cleanup and make dirs
|
||||||
|
rm -rf $MAGISKBIN/*
|
||||||
mkdir -p $MAGISKBIN 2>/dev/null
|
mkdir -p $MAGISKBIN 2>/dev/null
|
||||||
chmod 700 $NVBASE
|
chmod 700 $NVBASE
|
||||||
cp -af $1/. $MAGISKBIN
|
cp -af $1/. $MAGISKBIN
|
||||||
rm -f $MAGISKBIN/new-boot.img
|
rm -rf $1
|
||||||
|
chown -R 0:0 $MAGISKBIN
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_install() {
|
||||||
echo "- Flashing new boot image"
|
echo "- Flashing new boot image"
|
||||||
flash_image $1/new-boot.img $2
|
flash_image $1/new-boot.img $2
|
||||||
case $? in
|
case $? in
|
||||||
@ -40,10 +54,20 @@ direct_install() {
|
|||||||
return 2
|
return 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
rm -rf $1
|
|
||||||
|
rm -f $1/new-boot.img
|
||||||
|
fix_env $1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_uninstaller() {
|
||||||
|
rm -rf /dev/tmp
|
||||||
|
mkdir -p /dev/tmp/install
|
||||||
|
unzip -o "$1" "assets/*" "lib/*" -d /dev/tmp/install
|
||||||
|
INSTALLER=/dev/tmp/install sh /dev/tmp/install/assets/magisk_uninstaller.sh dummy 1 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
restore_imgs() {
|
restore_imgs() {
|
||||||
[ -z $SHA1 ] && return 1
|
[ -z $SHA1 ] && return 1
|
||||||
local BACKUPDIR=/data/magisk_backup_$SHA1
|
local BACKUPDIR=/data/magisk_backup_$SHA1
|
||||||
|
@ -18,7 +18,7 @@ buildscript {
|
|||||||
extra["vNav"] = vNav
|
extra["vNav"] = vNav
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:4.1.1")
|
classpath("com.android.tools.build:gradle:4.1.2")
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21")
|
||||||
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${vNav}")
|
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${vNav}")
|
||||||
|
|
||||||
|
144
build.py
144
build.py
@ -222,7 +222,7 @@ def sign_zip(unsigned):
|
|||||||
msg = '* Signing APK'
|
msg = '* Signing APK'
|
||||||
apksigner = op.join(find_build_tools(), 'apksigner')
|
apksigner = op.join(find_build_tools(), 'apksigner')
|
||||||
|
|
||||||
execArgs = [apksigner, 'sign',
|
exec_args = [apksigner, 'sign',
|
||||||
'--ks', config['keyStore'],
|
'--ks', config['keyStore'],
|
||||||
'--ks-pass', f'pass:{config["keyStorePass"]}',
|
'--ks-pass', f'pass:{config["keyStorePass"]}',
|
||||||
'--ks-key-alias', config['keyAlias'],
|
'--ks-key-alias', config['keyAlias'],
|
||||||
@ -232,14 +232,14 @@ def sign_zip(unsigned):
|
|||||||
|
|
||||||
if unsigned.endswith('.zip'):
|
if unsigned.endswith('.zip'):
|
||||||
msg = '* Signing zip'
|
msg = '* Signing zip'
|
||||||
execArgs.extend(['--min-sdk-version', '17',
|
exec_args.extend(['--min-sdk-version', '17',
|
||||||
'--v2-signing-enabled', 'false',
|
'--v2-signing-enabled', 'false',
|
||||||
'--v3-signing-enabled', 'false'])
|
'--v3-signing-enabled', 'false'])
|
||||||
|
|
||||||
execArgs.append(unsigned)
|
exec_args.append(unsigned)
|
||||||
|
|
||||||
header(msg)
|
header(msg)
|
||||||
proc = execv(execArgs)
|
proc = execv(exec_args)
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
error('Signing failed!')
|
error('Signing failed!')
|
||||||
|
|
||||||
@ -254,29 +254,6 @@ def binary_dump(src, out, var_name):
|
|||||||
out.flush()
|
out.flush()
|
||||||
|
|
||||||
|
|
||||||
def gen_update_binary():
|
|
||||||
bs = 1024
|
|
||||||
update_bin = bytearray(bs)
|
|
||||||
file = op.join('native', 'out', 'x86', 'busybox')
|
|
||||||
with open(file, 'rb') as f:
|
|
||||||
x86_bb = f.read()
|
|
||||||
file = op.join('native', 'out', 'armeabi-v7a', 'busybox')
|
|
||||||
with open(file, 'rb') as f:
|
|
||||||
arm_bb = f.read()
|
|
||||||
file = op.join('scripts', 'update_binary.sh')
|
|
||||||
with open(file, 'rb') as f:
|
|
||||||
script = f.read()
|
|
||||||
# Align x86 busybox to bs
|
|
||||||
blk_cnt = (len(x86_bb) - 1) // bs + 1
|
|
||||||
script = script.replace(b'__X86_CNT__', b'%d' % blk_cnt)
|
|
||||||
update_bin[:len(script)] = script
|
|
||||||
update_bin.extend(x86_bb)
|
|
||||||
# Padding for alignment
|
|
||||||
update_bin.extend(b'\0' * (blk_cnt * bs - len(x86_bb)))
|
|
||||||
update_bin.extend(arm_bb)
|
|
||||||
return update_bin
|
|
||||||
|
|
||||||
|
|
||||||
def run_ndk_build(flags):
|
def run_ndk_build(flags):
|
||||||
os.chdir('native')
|
os.chdir('native')
|
||||||
proc = system(f'{ndk_build} {base_flags} {flags} -j{cpu_count}')
|
proc = system(f'{ndk_build} {base_flags} {flags} -j{cpu_count}')
|
||||||
@ -407,96 +384,20 @@ def build_snet(args):
|
|||||||
header('Output: ' + target)
|
header('Output: ' + target)
|
||||||
|
|
||||||
|
|
||||||
def zip_main(args):
|
|
||||||
header('* Packing Flashable Zip')
|
|
||||||
|
|
||||||
if config['prettyName']:
|
|
||||||
name = f'Magisk-v{config["version"]}.zip'
|
|
||||||
elif args.release:
|
|
||||||
name = 'magisk-release.zip'
|
|
||||||
else:
|
|
||||||
name = 'magisk-debug.zip'
|
|
||||||
|
|
||||||
output = op.join(config['outdir'], name)
|
|
||||||
|
|
||||||
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
|
||||||
# update-binary
|
|
||||||
target = op.join('META-INF', 'com', 'google',
|
|
||||||
'android', 'update-binary')
|
|
||||||
vprint('zip: ' + target)
|
|
||||||
zipf.writestr(target, gen_update_binary())
|
|
||||||
|
|
||||||
# updater-script
|
|
||||||
source = op.join('scripts', 'flash_script.sh')
|
|
||||||
target = op.join('META-INF', 'com', 'google',
|
|
||||||
'android', 'updater-script')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
|
|
||||||
# Binaries
|
|
||||||
for lib_dir, zip_dir in zip(archs, ('arm', 'x86')):
|
|
||||||
for binary in ['magiskinit', 'magiskboot', 'magisk']:
|
|
||||||
source = op.join('native', 'out', lib_dir, binary)
|
|
||||||
target = op.join(zip_dir, 'magisk32' if binary == 'magisk' else binary)
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
|
|
||||||
for lib_dir, zip_dir in zip(arch64, ('arm', 'x86')):
|
|
||||||
source = op.join('native', 'out', lib_dir, 'magisk')
|
|
||||||
target = op.join(zip_dir, 'magisk64')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
|
|
||||||
# APK
|
|
||||||
source = op.join(
|
|
||||||
config['outdir'], 'app-release.apk' if args.release else 'app-debug.apk')
|
|
||||||
target = op.join('common', 'magisk.apk')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
|
|
||||||
# boot_patch.sh
|
|
||||||
source = op.join('scripts', 'boot_patch.sh')
|
|
||||||
target = op.join('common', 'boot_patch.sh')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
# util_functions.sh
|
|
||||||
source = op.join('scripts', 'util_functions.sh')
|
|
||||||
with open(source, 'r') as script:
|
|
||||||
# Add version info util_functions.sh
|
|
||||||
util_func = script.read().replace(
|
|
||||||
'#MAGISK_VERSION_STUB',
|
|
||||||
f'MAGISK_VER="{config["version"]}"\nMAGISK_VER_CODE={config["versionCode"]}')
|
|
||||||
target = op.join('common', 'util_functions.sh')
|
|
||||||
vprint(f'zip: {source} -> {target}')
|
|
||||||
zipf.writestr(target, util_func)
|
|
||||||
# addon.d.sh
|
|
||||||
source = op.join('scripts', 'addon.d.sh')
|
|
||||||
target = op.join('common', 'addon.d.sh')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
|
|
||||||
# chromeos
|
|
||||||
for tool in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
|
||||||
if tool == 'futility':
|
|
||||||
source = op.join('tools', tool)
|
|
||||||
else:
|
|
||||||
source = op.join('tools', 'keys', tool)
|
|
||||||
target = op.join('chromeos', tool)
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
|
|
||||||
# End of zipping
|
|
||||||
|
|
||||||
sign_zip(output)
|
|
||||||
header('Output: ' + output)
|
|
||||||
|
|
||||||
|
|
||||||
def zip_uninstaller(args):
|
def zip_uninstaller(args):
|
||||||
header('* Packing Uninstaller Zip')
|
header('* Packing Uninstaller Zip')
|
||||||
|
|
||||||
datestr = datetime.datetime.now().strftime("%Y%m%d")
|
datestr = f'{datetime.datetime.now():%Y%m%d}'
|
||||||
name = f'Magisk-uninstaller-{datestr}.zip' if config['prettyName'] else 'magisk-uninstaller.zip'
|
name = f'Magisk-uninstaller-{datestr}.zip' if config['prettyName'] else 'magisk-uninstaller.zip'
|
||||||
output = op.join(config['outdir'], name)
|
output = op.join(config['outdir'], name)
|
||||||
|
|
||||||
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||||
# update-binary
|
# update-binary
|
||||||
|
source = op.join('scripts', 'update_binary.sh')
|
||||||
target = op.join('META-INF', 'com', 'google',
|
target = op.join('META-INF', 'com', 'google',
|
||||||
'android', 'update-binary')
|
'android', 'update-binary')
|
||||||
vprint('zip: ' + target)
|
zip_with_msg(zipf, source, target)
|
||||||
zipf.writestr(target, gen_update_binary())
|
|
||||||
# updater-script
|
# updater-script
|
||||||
source = op.join('scripts', 'magisk_uninstaller.sh')
|
source = op.join('scripts', 'magisk_uninstaller.sh')
|
||||||
target = op.join('META-INF', 'com', 'google',
|
target = op.join('META-INF', 'com', 'google',
|
||||||
@ -504,17 +405,23 @@ def zip_uninstaller(args):
|
|||||||
zip_with_msg(zipf, source, target)
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
# Binaries
|
# Binaries
|
||||||
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
|
for exe in ('busybox', 'magiskboot'):
|
||||||
source = op.join('native', 'out', lib_dir, 'magiskboot')
|
for arch in archs:
|
||||||
target = op.join(zip_dir, 'magiskboot')
|
source = op.join('native', 'out', arch, exe)
|
||||||
zip_with_msg(zipf, source, target)
|
target = op.join('lib', arch, f'lib{exe}.so')
|
||||||
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
# util_functions.sh
|
# util_functions.sh
|
||||||
source = op.join('scripts', 'util_functions.sh')
|
source = op.join('scripts', 'util_functions.sh')
|
||||||
with open(source, 'r') as script:
|
with open(source, 'r') as script:
|
||||||
target = op.join('util_functions.sh')
|
# Add version info util_functions.sh
|
||||||
|
util_func = script.read().replace(
|
||||||
|
'#MAGISK_VERSION_STUB',
|
||||||
|
f'MAGISK_VER="{config["version"]}"\n'
|
||||||
|
f'MAGISK_VER_CODE={config["versionCode"]}')
|
||||||
|
target = op.join('assets', 'util_functions.sh')
|
||||||
vprint(f'zip: {source} -> {target}')
|
vprint(f'zip: {source} -> {target}')
|
||||||
zipf.writestr(target, script.read())
|
zipf.writestr(target, util_func)
|
||||||
|
|
||||||
# chromeos
|
# chromeos
|
||||||
for tool in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
for tool in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||||
@ -522,7 +429,7 @@ def zip_uninstaller(args):
|
|||||||
source = op.join('tools', tool)
|
source = op.join('tools', tool)
|
||||||
else:
|
else:
|
||||||
source = op.join('tools', 'keys', tool)
|
source = op.join('tools', 'keys', tool)
|
||||||
target = op.join('chromeos', tool)
|
target = op.join('assets', 'chromeos', tool)
|
||||||
zip_with_msg(zipf, source, target)
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
# End of zipping
|
# End of zipping
|
||||||
@ -597,9 +504,8 @@ def setup_ndk(args):
|
|||||||
def build_all(args):
|
def build_all(args):
|
||||||
vars(args)['target'] = []
|
vars(args)['target'] = []
|
||||||
build_stub(args)
|
build_stub(args)
|
||||||
build_app(args)
|
|
||||||
build_binary(args)
|
build_binary(args)
|
||||||
zip_main(args)
|
build_app(args)
|
||||||
zip_uninstaller(args)
|
zip_uninstaller(args)
|
||||||
|
|
||||||
|
|
||||||
@ -614,7 +520,7 @@ parser.add_argument('-c', '--config', default='config.prop',
|
|||||||
subparsers = parser.add_subparsers(title='actions')
|
subparsers = parser.add_subparsers(title='actions')
|
||||||
|
|
||||||
all_parser = subparsers.add_parser(
|
all_parser = subparsers.add_parser(
|
||||||
'all', help='build binaries, apks, zips')
|
'all', help='build everything')
|
||||||
all_parser.set_defaults(func=build_all)
|
all_parser.set_defaults(func=build_all)
|
||||||
|
|
||||||
binary_parser = subparsers.add_parser('binary', help='build binaries')
|
binary_parser = subparsers.add_parser('binary', help='build binaries')
|
||||||
@ -636,10 +542,6 @@ snet_parser = subparsers.add_parser(
|
|||||||
'snet', help='build snet extension')
|
'snet', help='build snet extension')
|
||||||
snet_parser.set_defaults(func=build_snet)
|
snet_parser.set_defaults(func=build_snet)
|
||||||
|
|
||||||
zip_parser = subparsers.add_parser(
|
|
||||||
'zip', help='zip Magisk into a flashable zip')
|
|
||||||
zip_parser.set_defaults(func=zip_main)
|
|
||||||
|
|
||||||
un_parser = subparsers.add_parser(
|
un_parser = subparsers.add_parser(
|
||||||
'uninstaller', help='create flashable uninstaller')
|
'uninstaller', help='create flashable uninstaller')
|
||||||
un_parser.set_defaults(func=zip_uninstaller)
|
un_parser.set_defaults(func=zip_uninstaller)
|
||||||
|
@ -18,12 +18,13 @@ object Config {
|
|||||||
}
|
}
|
||||||
fun contains(key: String) = get(key) != null
|
fun contains(key: String) = get(key) != null
|
||||||
|
|
||||||
val appVersion: String get() = get("appVersion") ?: commitHash
|
val version: String = get("version") ?: commitHash
|
||||||
val appVersionCode: Int get() = commitCount
|
val versionCode: Int get() = get("magisk.versionCode")!!.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
class MagiskPlugin : Plugin<Project> {
|
class MagiskPlugin : Plugin<Project> {
|
||||||
override fun apply(project: Project) {
|
override fun apply(project: Project) {
|
||||||
|
project.rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
||||||
val configPath: String? by project
|
val configPath: String? by project
|
||||||
val config = configPath?.let { File(it) } ?: project.rootProject.file("config.prop")
|
val config = configPath?.let { File(it) } ?: project.rootProject.file("config.prop")
|
||||||
if (config.exists())
|
if (config.exists())
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
# The version name of Magisk. Default: git HEAD short SHA1
|
# The version name of Magisk. Default: git HEAD short SHA1
|
||||||
version=string
|
version=string
|
||||||
|
|
||||||
# The version name of Magisk Manager. Default: git HEAD short SHA1
|
|
||||||
appVersion=string
|
|
||||||
|
|
||||||
# Output path. Default: out
|
# Output path. Default: out
|
||||||
outdir=string
|
outdir=string
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
#!/system/bin/sh
|
#!/system/bin/sh
|
||||||
###########################################################################################
|
#######################################################################################
|
||||||
#
|
|
||||||
# Magisk Boot Image Patcher
|
# Magisk Boot Image Patcher
|
||||||
# by topjohnwu
|
#######################################################################################
|
||||||
#
|
#
|
||||||
# Usage: boot_patch.sh <bootimage>
|
# Usage: boot_patch.sh <bootimage>
|
||||||
#
|
#
|
||||||
@ -14,16 +13,17 @@
|
|||||||
# File name Type Description
|
# File name Type Description
|
||||||
#
|
#
|
||||||
# boot_patch.sh script A script to patch boot image for Magisk.
|
# boot_patch.sh script A script to patch boot image for Magisk.
|
||||||
# (this file) The script will use binaries and files in its same directory
|
# (this file) The script will use files in its same
|
||||||
# to complete the patching process
|
# directory to complete the patching process
|
||||||
# util_functions.sh script A script which hosts all functions required for this script
|
# util_functions.sh script A script which hosts all functions required
|
||||||
# to work properly
|
# for this script to work properly
|
||||||
# magiskinit binary The binary to replace /init; magisk binary embedded
|
# magiskinit binary The binary to replace /init
|
||||||
|
# magisk(32/64) binary The magisk binaries
|
||||||
# magiskboot binary A tool to manipulate boot images
|
# magiskboot binary A tool to manipulate boot images
|
||||||
# chromeos folder This folder includes all the utilities and keys to sign
|
# chromeos folder This folder includes the utility and keys to sign
|
||||||
# (optional) chromeos boot images. Currently only used for Pixel C
|
# (optional) chromeos boot images. Only used for Pixel C.
|
||||||
#
|
#
|
||||||
###########################################################################################
|
#######################################################################################
|
||||||
|
|
||||||
############
|
############
|
||||||
# Functions
|
# Functions
|
||||||
@ -50,7 +50,7 @@ getdir() {
|
|||||||
|
|
||||||
if [ -z $SOURCEDMODE ]; then
|
if [ -z $SOURCEDMODE ]; then
|
||||||
# Switch to the location of the script file
|
# Switch to the location of the script file
|
||||||
cd "`getdir "${BASH_SOURCE:-$0}"`"
|
cd "$(getdir "${BASH_SOURCE:-$0}")"
|
||||||
# Load utility functions
|
# Load utility functions
|
||||||
. ./util_functions.sh
|
. ./util_functions.sh
|
||||||
# Check if 64-bit
|
# Check if 64-bit
|
||||||
@ -106,14 +106,14 @@ fi
|
|||||||
case $((STATUS & 3)) in
|
case $((STATUS & 3)) in
|
||||||
0 ) # Stock boot
|
0 ) # Stock boot
|
||||||
ui_print "- Stock boot image detected"
|
ui_print "- Stock boot image detected"
|
||||||
SHA1=`./magiskboot sha1 "$BOOTIMAGE" 2>/dev/null`
|
SHA1=$(./magiskboot sha1 "$BOOTIMAGE" 2>/dev/null)
|
||||||
cat $BOOTIMAGE > stock_boot.img
|
cat $BOOTIMAGE > stock_boot.img
|
||||||
cp -af ramdisk.cpio ramdisk.cpio.orig 2>/dev/null
|
cp -af ramdisk.cpio ramdisk.cpio.orig 2>/dev/null
|
||||||
;;
|
;;
|
||||||
1 ) # Magisk patched
|
1 ) # Magisk patched
|
||||||
ui_print "- Magisk patched boot image detected"
|
ui_print "- Magisk patched boot image detected"
|
||||||
# Find SHA1 of stock boot image
|
# Find SHA1 of stock boot image
|
||||||
[ -z $SHA1 ] && SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null`
|
[ -z $SHA1 ] && SHA1=$(./magiskboot cpio ramdisk.cpio sha1 2>/dev/null)
|
||||||
./magiskboot cpio ramdisk.cpio restore
|
./magiskboot cpio ramdisk.cpio restore
|
||||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||||
;;
|
;;
|
||||||
|
@ -1,31 +1,26 @@
|
|||||||
#MAGISK
|
#MAGISK
|
||||||
############################################
|
############################################
|
||||||
#
|
|
||||||
# Magisk Flash Script (updater-script)
|
# Magisk Flash Script (updater-script)
|
||||||
# by topjohnwu
|
|
||||||
#
|
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# Preparation
|
# Preparation
|
||||||
##############
|
##############
|
||||||
|
|
||||||
COMMONDIR=$INSTALLER/common
|
|
||||||
APK=$COMMONDIR/magisk.apk
|
|
||||||
CHROMEDIR=$INSTALLER/chromeos
|
|
||||||
|
|
||||||
# Default permissions
|
# Default permissions
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
OUTFD=$2
|
OUTFD=$2
|
||||||
ZIP=$3
|
APK="$3"
|
||||||
|
COMMONDIR=$INSTALLER/assets
|
||||||
|
CHROMEDIR=$INSTALLER/assets/chromeos
|
||||||
|
|
||||||
if [ ! -f $COMMONDIR/util_functions.sh ]; then
|
if [ ! -f $COMMONDIR/util_functions.sh ]; then
|
||||||
echo "! Unable to extract zip file!"
|
echo "! Unable to extract zip file!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Load utility fuctions
|
# Load utility functions
|
||||||
. $COMMONDIR/util_functions.sh
|
. $COMMONDIR/util_functions.sh
|
||||||
|
|
||||||
setup_flashable
|
setup_flashable
|
||||||
@ -57,7 +52,11 @@ api_level_arch_detect
|
|||||||
|
|
||||||
ui_print "- Device platform: $ARCH"
|
ui_print "- Device platform: $ARCH"
|
||||||
|
|
||||||
BINDIR=$INSTALLER/$ARCH32
|
BINDIR=$INSTALLER/lib/$ARCH32
|
||||||
|
[ ! -d "$BINDIR" ] && BINDIR=$INSTALLER/lib/armeabi-v7a
|
||||||
|
cd $BINDIR
|
||||||
|
for file in lib*.so; do mv "$file" "${file:3:${#file}-6}"; done
|
||||||
|
cd /
|
||||||
chmod -R 755 $CHROMEDIR $BINDIR
|
chmod -R 755 $CHROMEDIR $BINDIR
|
||||||
|
|
||||||
# Check if system root is installed and remove
|
# Check if system root is installed and remove
|
||||||
@ -72,24 +71,22 @@ ui_print "- Constructing environment"
|
|||||||
# Copy required files
|
# Copy required files
|
||||||
rm -rf $MAGISKBIN/* 2>/dev/null
|
rm -rf $MAGISKBIN/* 2>/dev/null
|
||||||
mkdir -p $MAGISKBIN 2>/dev/null
|
mkdir -p $MAGISKBIN 2>/dev/null
|
||||||
cp -af $BINDIR/. $COMMONDIR/. $CHROMEDIR $BBBIN $MAGISKBIN
|
cp -af $BINDIR/. $COMMONDIR/. $BBBIN $MAGISKBIN
|
||||||
chmod -R 755 $MAGISKBIN
|
chmod -R 755 $MAGISKBIN
|
||||||
|
|
||||||
# addon.d
|
# addon.d
|
||||||
if [ -d /system/addon.d ]; then
|
if [ -d /system/addon.d ]; then
|
||||||
ui_print "- Adding addon.d survival script"
|
ui_print "- Adding addon.d survival script"
|
||||||
blockdev --setrw /dev/block/mapper/system$SLOT 2>/dev/null
|
blockdev --setrw /dev/block/mapper/system$SLOT 2>/dev/null
|
||||||
mount -o rw,remount /system
|
mount -o rw,remount /system || mount -o rw,remount /
|
||||||
ADDOND=/system/addon.d/99-magisk.sh
|
ADDOND=/system/addon.d/99-magisk.sh
|
||||||
cp -af $COMMONDIR/addon.d.sh $ADDOND
|
cp -af $COMMONDIR/addon.d.sh $ADDOND
|
||||||
chmod 755 $ADDOND
|
chmod 755 $ADDOND
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$BOOTMODE || recovery_actions
|
##################
|
||||||
|
# Image Patching
|
||||||
#####################
|
##################
|
||||||
# Boot/DTBO Patching
|
|
||||||
#####################
|
|
||||||
|
|
||||||
install_magisk
|
install_magisk
|
||||||
|
|
||||||
|
@ -1,72 +1,75 @@
|
|||||||
#MAGISK
|
#MAGISK
|
||||||
############################################
|
############################################
|
||||||
#
|
# Magisk Uninstaller (updater-script)
|
||||||
# Magisk Uninstaller
|
|
||||||
# by topjohnwu
|
|
||||||
#
|
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# Preparation
|
# Preparation
|
||||||
##############
|
##############
|
||||||
|
|
||||||
# This path should work in any cases
|
|
||||||
TMPDIR=/dev/tmp
|
|
||||||
|
|
||||||
INSTALLER=$TMPDIR/install
|
|
||||||
CHROMEDIR=$INSTALLER/chromeos
|
|
||||||
|
|
||||||
# Default permissions
|
# Default permissions
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
OUTFD=$2
|
OUTFD=$2
|
||||||
ZIP=$3
|
APK="$3"
|
||||||
|
COMMONDIR=$INSTALLER/assets
|
||||||
|
CHROMEDIR=$INSTALLER/assets/chromeos
|
||||||
|
|
||||||
if [ ! -f $INSTALLER/util_functions.sh ]; then
|
if [ ! -f $COMMONDIR/util_functions.sh ]; then
|
||||||
echo "! Unable to extract zip file!"
|
echo "! Unable to extract zip file!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Load utility functions
|
# Load utility functions
|
||||||
. $INSTALLER/util_functions.sh
|
. $COMMONDIR/util_functions.sh
|
||||||
|
|
||||||
setup_flashable
|
setup_flashable
|
||||||
|
|
||||||
print_title "Magisk Uninstaller"
|
############
|
||||||
|
# Detection
|
||||||
|
############
|
||||||
|
|
||||||
|
if echo $MAGISK_VER | grep -q '\.'; then
|
||||||
|
PRETTY_VER=$MAGISK_VER
|
||||||
|
else
|
||||||
|
PRETTY_VER="$MAGISK_VER($MAGISK_VER_CODE)"
|
||||||
|
fi
|
||||||
|
print_title "Magisk $PRETTY_VER Uninstaller"
|
||||||
|
|
||||||
is_mounted /data || mount /data || abort "! Unable to mount /data, please uninstall with Magisk Manager"
|
is_mounted /data || mount /data || abort "! Unable to mount /data, please uninstall with Magisk Manager"
|
||||||
|
mount_partitions
|
||||||
|
check_data
|
||||||
|
$DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager"
|
||||||
if ! $BOOTMODE; then
|
if ! $BOOTMODE; then
|
||||||
# Mounting stuffs in recovery (best effort)
|
# Mounting stuffs in recovery (best effort)
|
||||||
mount_name metadata /metadata
|
mount_name metadata /metadata
|
||||||
mount_name "cache cac" /cache
|
mount_name "cache cac" /cache
|
||||||
mount_name persist /persist
|
mount_name persist /persist
|
||||||
fi
|
fi
|
||||||
mount_partitions
|
get_flags
|
||||||
|
find_boot_image
|
||||||
|
|
||||||
|
[ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
|
||||||
|
ui_print "- Target image: $BOOTIMAGE"
|
||||||
|
|
||||||
|
# Detect version and architecture
|
||||||
api_level_arch_detect
|
api_level_arch_detect
|
||||||
|
|
||||||
ui_print "- Device platform: $ARCH"
|
ui_print "- Device platform: $ARCH"
|
||||||
MAGISKBIN=$INSTALLER/$ARCH32
|
|
||||||
mv $CHROMEDIR $MAGISKBIN
|
|
||||||
chmod -R 755 $MAGISKBIN
|
|
||||||
|
|
||||||
check_data
|
BINDIR=$INSTALLER/lib/$ARCH32
|
||||||
$DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager"
|
[ ! -d "$BINDIR" ] && BINDIR=$INSTALLER/lib/armeabi-v7a
|
||||||
$BOOTMODE || recovery_actions
|
cd $BINDIR
|
||||||
run_migrations
|
for file in lib*.so; do mv "$file" "${file:3:${#file}-6}"; done
|
||||||
|
cd /
|
||||||
|
chmod -R 755 $CHROMEDIR $BINDIR
|
||||||
|
cp -af $CHROMEDIR/. $BINDIR/chromeos
|
||||||
|
|
||||||
############
|
############
|
||||||
# Uninstall
|
# Uninstall
|
||||||
############
|
############
|
||||||
|
|
||||||
get_flags
|
cd $BINDIR
|
||||||
find_boot_image
|
|
||||||
|
|
||||||
[ -e $BOOTIMAGE ] || abort "! Unable to detect boot image"
|
|
||||||
ui_print "- Found target image: $BOOTIMAGE"
|
|
||||||
[ -z $DTBOIMAGE ] || ui_print "- Found dtbo image: $DTBOIMAGE"
|
|
||||||
|
|
||||||
cd $MAGISKBIN
|
|
||||||
|
|
||||||
CHROMEOS=false
|
CHROMEOS=false
|
||||||
|
|
||||||
@ -108,14 +111,14 @@ case $((STATUS & 3)) in
|
|||||||
1 ) # Magisk patched
|
1 ) # Magisk patched
|
||||||
ui_print "- Magisk patched image detected"
|
ui_print "- Magisk patched image detected"
|
||||||
# Find SHA1 of stock boot image
|
# Find SHA1 of stock boot image
|
||||||
SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null`
|
SHA1=$(./magiskboot cpio ramdisk.cpio sha1 2>/dev/null)
|
||||||
BACKUPDIR=/data/magisk_backup_$SHA1
|
BACKUPDIR=/data/magisk_backup_$SHA1
|
||||||
if [ -d $BACKUPDIR ]; then
|
if [ -d $BACKUPDIR ]; then
|
||||||
ui_print "- Restoring stock boot image"
|
ui_print "- Restoring stock boot image"
|
||||||
flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE
|
flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE
|
||||||
for name in dtb dtbo dtbs; do
|
for name in dtb dtbo dtbs; do
|
||||||
[ -f $BACKUPDIR/${name}.img.gz ] || continue
|
[ -f $BACKUPDIR/${name}.img.gz ] || continue
|
||||||
IMAGE=`find_block $name$SLOT`
|
IMAGE=$(find_block $name$SLOT)
|
||||||
[ -z $IMAGE ] && continue
|
[ -z $IMAGE ] && continue
|
||||||
ui_print "- Restoring stock $name image"
|
ui_print "- Restoring stock $name image"
|
||||||
flash_image $BACKUPDIR/${name}.img.gz $IMAGE
|
flash_image $BACKUPDIR/${name}.img.gz $IMAGE
|
||||||
@ -124,7 +127,7 @@ case $((STATUS & 3)) in
|
|||||||
ui_print "! Boot image backup unavailable"
|
ui_print "! Boot image backup unavailable"
|
||||||
ui_print "- Restoring ramdisk with internal backup"
|
ui_print "- Restoring ramdisk with internal backup"
|
||||||
./magiskboot cpio ramdisk.cpio restore
|
./magiskboot cpio ramdisk.cpio restore
|
||||||
if ! ./magiskboot cpio ramdisk.cpio "exists init.rc"; then
|
if ! ./magiskboot cpio ramdisk.cpio "exists init"; then
|
||||||
# A only system-as-root
|
# A only system-as-root
|
||||||
rm -f ramdisk.cpio
|
rm -f ramdisk.cpio
|
||||||
fi
|
fi
|
||||||
@ -148,10 +151,11 @@ rm -rf \
|
|||||||
/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* \
|
/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* \
|
||||||
/data/unencrypted/magisk /metadata/magisk /persist/magisk /mnt/vendor/persist/magisk
|
/data/unencrypted/magisk /metadata/magisk /persist/magisk /mnt/vendor/persist/magisk
|
||||||
|
|
||||||
if [ -f /system/addon.d/99-magisk.sh ]; then
|
ADDOND=/system/addon.d/99-magisk.sh
|
||||||
|
if [ -f $ADDOND ]; then
|
||||||
blockdev --setrw /dev/block/mapper/system$SLOT 2>/dev/null
|
blockdev --setrw /dev/block/mapper/system$SLOT 2>/dev/null
|
||||||
mount -o rw,remount /system || mount -o rw,remount /
|
mount -o rw,remount /system || mount -o rw,remount /
|
||||||
rm -f /system/addon.d/99-magisk.sh
|
rm -f $ADDOND
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd /
|
cd /
|
||||||
@ -163,7 +167,10 @@ if $BOOTMODE; then
|
|||||||
ui_print "********************************************"
|
ui_print "********************************************"
|
||||||
(sleep 8; /system/bin/reboot)&
|
(sleep 8; /system/bin/reboot)&
|
||||||
else
|
else
|
||||||
rm -rf /data/data/*magisk* /data/user*/*/*magisk* /data/app/*magisk* /data/app/*/*magisk*
|
ui_print "********************************************"
|
||||||
|
ui_print " Magisk Manager will not be uninstalled"
|
||||||
|
ui_print " Please uninstall it manually after reboot"
|
||||||
|
ui_print "********************************************"
|
||||||
recovery_cleanup
|
recovery_cleanup
|
||||||
ui_print "- Done"
|
ui_print "- Done"
|
||||||
fi
|
fi
|
||||||
|
@ -1,37 +1,18 @@
|
|||||||
#!/sbin/sh
|
#!/sbin/sh
|
||||||
X86_CNT=__X86_CNT__
|
|
||||||
extract_bb() {
|
TMPDIR=/dev/tmp
|
||||||
touch "$BBBIN"
|
rm -rf $TMPDIR
|
||||||
chmod 755 "$BBBIN"
|
mkdir -p $TMPDIR 2>/dev/null
|
||||||
dd if="$0" of="$BBBIN" bs=1024 skip=1 count=$X86_CNT
|
|
||||||
"$BBBIN" >/dev/null 2>&1 || dd if="$0" of="$BBBIN" bs=1024 skip=$(($X86_CNT + 1))
|
export BBBIN=$TMPDIR/busybox
|
||||||
}
|
unzip -o "$3" lib/x86/libbusybox.so lib/armeabi-v7a/libbusybox.so -d $TMPDIR >&2
|
||||||
setup_bb() {
|
chmod -R 755 $TMPDIR/lib
|
||||||
mkdir -p $TMPDIR 2>/dev/null
|
mv -f $TMPDIR/lib/x86/libbusybox.so $BBBIN
|
||||||
BBBIN=$TMPDIR/busybox
|
$BBBIN >/dev/null 2>&1 || mv -f $TMPDIR/lib/armeabi-v7a/libbusybox.so $BBBIN
|
||||||
extract_bb
|
$BBBIN rm -rf $TMPDIR/lib
|
||||||
}
|
|
||||||
export BBBIN
|
export INSTALLER=$TMPDIR/install
|
||||||
case "$1" in
|
$BBBIN mkdir -p $INSTALLER
|
||||||
"extract"|"-x")
|
$BBBIN unzip -o "$3" "assets/*" "lib/*" "META-INF/com/google/*" -x "lib/*/libbusybox.so" -d $INSTALLER >&2
|
||||||
BBBIN=./busybox
|
export ASH_STANDALONE=1
|
||||||
[ -z "$2" ] || BBBIN="$2"
|
exec $BBBIN sh "$INSTALLER/META-INF/com/google/android/updater-script" "$@"
|
||||||
extract_bb
|
|
||||||
;;
|
|
||||||
"sh")
|
|
||||||
TMPDIR=.
|
|
||||||
setup_bb
|
|
||||||
shift
|
|
||||||
exec ./busybox sh -o standalone "$@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
TMPDIR=/dev/tmp
|
|
||||||
rm -rf $TMPDIR 2>/dev/null
|
|
||||||
setup_bb
|
|
||||||
export INSTALLER=$TMPDIR/install
|
|
||||||
$BBBIN mkdir -p $INSTALLER
|
|
||||||
$BBBIN unzip -o "$3" -d $INSTALLER >&2
|
|
||||||
exec $BBBIN sh -o standalone $INSTALLER/META-INF/com/google/android/updater-script "$@"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
exit
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
############################################
|
############################################
|
||||||
#
|
|
||||||
# Magisk General Utility Functions
|
# Magisk General Utility Functions
|
||||||
# by topjohnwu
|
|
||||||
#
|
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
#MAGISK_VERSION_STUB
|
#MAGISK_VERSION_STUB
|
||||||
@ -33,7 +30,7 @@ grep_prop() {
|
|||||||
shift
|
shift
|
||||||
local FILES=$@
|
local FILES=$@
|
||||||
[ -z "$FILES" ] && FILES='/system/build.prop'
|
[ -z "$FILES" ] && FILES='/system/build.prop'
|
||||||
cat $FILES | dos2unix | sed -n "$REGEX" 2>/dev/null | head -n 1
|
cat $FILES 2>/dev/null | dos2unix | sed -n "$REGEX" | head -n 1
|
||||||
}
|
}
|
||||||
|
|
||||||
getvar() {
|
getvar() {
|
||||||
@ -42,11 +39,11 @@ getvar() {
|
|||||||
local PROPPATH='/data/.magisk /cache/.magisk'
|
local PROPPATH='/data/.magisk /cache/.magisk'
|
||||||
[ -n $MAGISKTMP ] && PROPPATH="$MAGISKTMP/config $PROPPATH"
|
[ -n $MAGISKTMP ] && PROPPATH="$MAGISKTMP/config $PROPPATH"
|
||||||
VALUE=$(grep_prop $VARNAME $PROPPATH)
|
VALUE=$(grep_prop $VARNAME $PROPPATH)
|
||||||
[ ! -z $VALUE ] && eval $VARNAME=\$VALUE
|
[ -n $VALUE ] && eval $VARNAME=\$VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
is_mounted() {
|
is_mounted() {
|
||||||
grep -q " `readlink -f $1` " /proc/mounts 2>/dev/null
|
grep -q " $(readlink -f $1) " /proc/mounts 2>/dev/null
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +93,7 @@ setup_flashable() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
recovery_actions
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_bb() {
|
ensure_bb() {
|
||||||
@ -309,51 +307,57 @@ mount_apex() {
|
|||||||
$BOOTMODE || [ ! -d /system/apex ] && return
|
$BOOTMODE || [ ! -d /system/apex ] && return
|
||||||
local APEX DEST
|
local APEX DEST
|
||||||
setup_mntpoint /apex
|
setup_mntpoint /apex
|
||||||
|
mount -t tmpfs tmpfs /apex -o mode=755
|
||||||
|
local PATTERN='s/.*"name":[^"]*"\([^"]*\).*/\1/p'
|
||||||
for APEX in /system/apex/*; do
|
for APEX in /system/apex/*; do
|
||||||
DEST=/apex/$(basename $APEX .apex)
|
|
||||||
[ "$DEST" == /apex/com.android.runtime.release ] && DEST=/apex/com.android.runtime
|
|
||||||
mkdir -p $DEST 2>/dev/null
|
|
||||||
if [ -f $APEX ]; then
|
if [ -f $APEX ]; then
|
||||||
# APEX APKs, extract and loop mount
|
# APEX APKs, extract and loop mount
|
||||||
unzip -qo $APEX apex_payload.img -d /apex
|
unzip -qo $APEX apex_payload.img -d /apex
|
||||||
loop_setup apex_payload.img
|
DEST=/apex/$(unzip -qp $APEX apex_manifest.pb | strings | head -n 1)
|
||||||
|
[ -z $DEST ] && DEST=/apex/$(unzip -qp $APEX apex_manifest.json | sed -n $PATTERN)
|
||||||
|
[ -z $DEST ] && continue
|
||||||
|
mkdir -p $DEST
|
||||||
|
loop_setup /apex/apex_payload.img
|
||||||
if [ ! -z $LOOPDEV ]; then
|
if [ ! -z $LOOPDEV ]; then
|
||||||
ui_print "- Mounting $DEST"
|
ui_print "- Mounting $DEST"
|
||||||
mount -t ext4 -o ro,noatime $LOOPDEV $DEST
|
mount -t ext4 -o ro,noatime $LOOPDEV $DEST
|
||||||
fi
|
fi
|
||||||
rm -f apex_payload.img
|
rm -f /apex/apex_payload.img
|
||||||
elif [ -d $APEX ]; then
|
elif [ -d $APEX ]; then
|
||||||
# APEX folders, bind mount directory
|
# APEX folders, bind mount directory
|
||||||
|
if [ -f $APEX/apex_manifest.json ]; then
|
||||||
|
DEST=/apex/$(sed -n $PATTERN $APEX/apex_manifest.json)
|
||||||
|
elif [ -f $APEX/apex_manifest.pb ]; then
|
||||||
|
DEST=/apex/$(strings apex_manifest.pb | head -n 1)
|
||||||
|
else
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
mkdir -p $DEST
|
||||||
ui_print "- Mounting $DEST"
|
ui_print "- Mounting $DEST"
|
||||||
mount -o bind $APEX $DEST
|
mount -o bind $APEX $DEST
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
export ANDROID_RUNTIME_ROOT=/apex/com.android.runtime
|
export ANDROID_RUNTIME_ROOT=/apex/com.android.runtime
|
||||||
export ANDROID_TZDATA_ROOT=/apex/com.android.tzdata
|
export ANDROID_TZDATA_ROOT=/apex/com.android.tzdata
|
||||||
local APEXRJPATH=/apex/com.android.runtime/javalib
|
export ANDROID_ART_ROOT=/apex/com.android.art
|
||||||
local SYSFRAME=/system/framework
|
export ANDROID_I18N_ROOT=/apex/com.android.i18n
|
||||||
export BOOTCLASSPATH=\
|
local APEXJARS=$(find /apex -name '*.jar' | sort | tr '\n' ':')
|
||||||
$APEXRJPATH/core-oj.jar:$APEXRJPATH/core-libart.jar:$APEXRJPATH/okhttp.jar:\
|
local FWK=/system/framework
|
||||||
$APEXRJPATH/bouncycastle.jar:$APEXRJPATH/apache-xml.jar:$SYSFRAME/framework.jar:\
|
export BOOTCLASSPATH=${APEXJARS}\
|
||||||
$SYSFRAME/ext.jar:$SYSFRAME/telephony-common.jar:$SYSFRAME/voip-common.jar:\
|
$FWK/framework.jar:$FWK/ext.jar:$FWK/telephony-common.jar:\
|
||||||
$SYSFRAME/ims-common.jar:$SYSFRAME/android.test.base.jar:$SYSFRAME/telephony-ext.jar:\
|
$FWK/voip-common.jar:$FWK/ims-common.jar:$FWK/telephony-ext.jar
|
||||||
/apex/com.android.conscrypt/javalib/conscrypt.jar:\
|
|
||||||
/apex/com.android.media/javalib/updatable-media.jar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
umount_apex() {
|
umount_apex() {
|
||||||
[ -d /apex ] || return
|
[ -d /apex ] || return
|
||||||
local DEST SRC
|
umount -l /apex
|
||||||
for DEST in /apex/*; do
|
for loop in /dev/block/loop*; do
|
||||||
[ "$DEST" = '/apex/*' ] && break
|
losetup -d $loop 2>/dev/null
|
||||||
SRC=$(grep $DEST /proc/mounts | awk '{ print $1 }')
|
|
||||||
umount -l $DEST
|
|
||||||
# Detach loop device just in case
|
|
||||||
losetup -d $SRC 2>/dev/null
|
|
||||||
done
|
done
|
||||||
rm -rf /apex
|
|
||||||
unset ANDROID_RUNTIME_ROOT
|
unset ANDROID_RUNTIME_ROOT
|
||||||
unset ANDROID_TZDATA_ROOT
|
unset ANDROID_TZDATA_ROOT
|
||||||
|
unset ANDROID_ART_ROOT
|
||||||
|
unset ANDROID_I18N_ROOT
|
||||||
unset BOOTCLASSPATH
|
unset BOOTCLASSPATH
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +393,7 @@ find_boot_image() {
|
|||||||
BOOTIMAGE=
|
BOOTIMAGE=
|
||||||
if $RECOVERYMODE; then
|
if $RECOVERYMODE; then
|
||||||
BOOTIMAGE=`find_block recovery_ramdisk$SLOT recovery$SLOT sos`
|
BOOTIMAGE=`find_block recovery_ramdisk$SLOT recovery$SLOT sos`
|
||||||
elif [ ! -z $SLOT ]; then
|
elif [ -n $SLOT ]; then
|
||||||
BOOTIMAGE=`find_block ramdisk$SLOT recovery_ramdisk$SLOT boot$SLOT`
|
BOOTIMAGE=`find_block ramdisk$SLOT recovery_ramdisk$SLOT boot$SLOT`
|
||||||
else
|
else
|
||||||
BOOTIMAGE=`find_block ramdisk recovery_ramdisk kern-a android_boot kernel bootimg boot lnx boot_a`
|
BOOTIMAGE=`find_block ramdisk recovery_ramdisk kern-a android_boot kernel bootimg boot lnx boot_a`
|
||||||
@ -402,7 +406,7 @@ find_boot_image() {
|
|||||||
|
|
||||||
flash_image() {
|
flash_image() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
*.gz) CMD1="$MAGISKBIN/magiskboot decompress '$1' - 2>/dev/null";;
|
*.gz) CMD1="gzip -d < '$1' 2>/dev/null";;
|
||||||
*) CMD1="cat '$1'";;
|
*) CMD1="cat '$1'";;
|
||||||
esac
|
esac
|
||||||
if $BOOTSIGNED; then
|
if $BOOTSIGNED; then
|
||||||
|
@ -3,12 +3,12 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
val canary = !Config.appVersion.contains(".")
|
val canary = !Config.version.contains(".")
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.topjohnwu.magisk"
|
applicationId = "com.topjohnwu.magisk"
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = Config.appVersion
|
versionName = Config.version
|
||||||
buildConfigField("int", "STUB_VERSION", "15")
|
buildConfigField("int", "STUB_VERSION", "15")
|
||||||
buildConfigField("String", "DEV_CHANNEL", Config["DEV_CHANNEL"] ?: "null")
|
buildConfigField("String", "DEV_CHANNEL", Config["DEV_CHANNEL"] ?: "null")
|
||||||
buildConfigField("boolean", "CANARY", if (canary) "true" else "false")
|
buildConfigField("boolean", "CANARY", if (canary) "true" else "false")
|
||||||
|
Loading…
Reference in New Issue
Block a user