diff --git a/app/.gitignore b/app/.gitignore index 88ab538f6..50b291029 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -6,6 +6,7 @@ app/release *.hprof .externalNativeBuild/ -public.certificate.x509.pem -private.key.pk8 *.apk +src/main/assets +src/main/jniLibs +src/main/resources diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7024c0746..186bfe1e0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,4 @@ +import org.apache.tools.ant.filters.FixCrLfFilter import java.io.PrintStream plugins { @@ -22,8 +23,8 @@ android { applicationId = "com.topjohnwu.magisk" vectorDrawables.useSupportLibrary = true multiDexEnabled = true - versionName = Config.appVersion - versionCode = Config.appVersionCode + versionName = Config.version + versionCode = Config.versionCode javaCompileOptions.annotationProcessorOptions.arguments( mapOf("room.incremental" to "true") @@ -51,13 +52,14 @@ android { } packagingOptions { - exclude("/META-INF/**") + exclude("/META-INF/*") exclude("/org/bouncycastle/**") exclude("/kotlin/**") exclude("/kotlinx/**") exclude("/okhttp3/**") exclude("/*.txt") exclude("/*.bin") + doNotStrip("**/*.so") } kotlinOptions { @@ -65,10 +67,82 @@ android { } } -tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) { - from(rootProject.file("scripts/util_functions.sh")) - into("src/main/res/raw") -}) +val syncLibs by tasks.registering(Sync::class) { + into("src/main/jniLibs") + 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("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 { val keysDir = rootProject.file("tools/keys") @@ -178,6 +252,5 @@ dependencies { implementation("androidx.transition:transition:1.3.1") implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.core:core-ktx:1.3.2") - implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0") implementation("com.google.android.material:material:1.2.1") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 73c3175b0..28a97b807 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,8 @@ android:icon="@drawable/ic_launcher" android:name="a.e" android:allowBackup="false" + android:multiArch="true" + android:extractNativeLibs="true" tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"> diff --git a/app/src/main/java/com/topjohnwu/magisk/core/App.kt b/app/src/main/java/com/topjohnwu/magisk/core/App.kt index fed406a5a..988640ce4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/App.kt @@ -19,6 +19,7 @@ import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin import timber.log.Timber +import java.io.File import kotlin.system.exitProcess open class App() : Application() { @@ -61,6 +62,12 @@ open class App() : Application() { val wrapped = impl.wrap() 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 startKoin { androidContext(wrapped) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Const.kt b/app/src/main/java/com/topjohnwu/magisk/core/Const.kt index b5e91f1b4..0298cfded 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Const.kt @@ -1,13 +1,30 @@ package com.topjohnwu.magisk.core +import android.os.Build import android.os.Process +import java.io.File +@Suppress("DEPRECATION") 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 lateinit var MAGISKTMP: String + lateinit var NATIVE_LIB_DIR: File 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" // Versions @@ -19,11 +36,9 @@ object Const { val USER_ID = Process.myUid() / 100000 object Version { - const val MIN_VERSION = "v19.0" - const val MIN_VERCODE = 19000 + const val MIN_VERSION = "v20.4" + 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_2() = Info.env.magiskVersionCode >= 21200 || isCanary() fun isCanary() = Info.env.magiskVersionCode % 100 != 0 diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/Action.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/Action.kt deleted file mode 100644 index ae3b9cc0b..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/Action.kt +++ /dev/null @@ -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() - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt index 7e20d0bef..4f7500051 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/BaseDownloader.kt @@ -8,7 +8,6 @@ import androidx.lifecycle.MutableLiveData import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.ForegroundTracker import com.topjohnwu.magisk.core.base.BaseService -import com.topjohnwu.magisk.core.utils.MediaStoreUtils.checkSum import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.core.utils.ProgressInputStream import com.topjohnwu.magisk.data.repository.NetworkService @@ -69,17 +68,14 @@ abstract class BaseDownloader : BaseService(), KoinComponent { // -- Download logic private suspend fun Subject.startDownload() { - val skip = this is Subject.Magisk && file.checkSum("MD5", magisk.md5) - if (!skip) { - val stream = service.fetchFile(url).toProgressStream(this) - when (this) { - is Subject.Module -> // Download and process on-the-fly - stream.toModule(file, service.fetchInstaller().byteStream()) - else -> { - withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) } - if (this is Subject.Manager) - handleAPK(this) - } + val stream = service.fetchFile(url).toProgressStream(this) + when (this) { + is Subject.Module -> // Download and process on-the-fly + stream.toModule(file, service.fetchInstaller().byteStream()) + else -> { + withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) } + if (this is Subject.Manager) + handleAPK(this) } } val newId = notifyFinish(this) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt index 06f9e9b54..7f7b0755a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt @@ -7,11 +7,10 @@ import android.content.Context import android.content.Intent import android.os.Build import androidx.core.net.toFile -import com.topjohnwu.magisk.core.download.Action.* -import com.topjohnwu.magisk.core.download.Action.Flash.Secondary -import com.topjohnwu.magisk.core.download.Subject.* +import com.topjohnwu.magisk.core.download.Action.Flash +import com.topjohnwu.magisk.core.download.Subject.Manager +import com.topjohnwu.magisk.core.download.Subject.Module import com.topjohnwu.magisk.core.intent -import com.topjohnwu.magisk.core.tasks.EnvFixTask import com.topjohnwu.magisk.ui.flash.FlashFragment import com.topjohnwu.magisk.utils.APKInstall import kotlin.random.Random.Default.nextInt @@ -22,25 +21,12 @@ open class DownloadService : BaseDownloader() { private val context get() = this override suspend fun onFinish(subject: Subject, id: Int) = when (subject) { - is Magisk -> subject.onFinish(id) is Module -> 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) { - is Flash -> FlashFragment.install(file, id) + Flash -> FlashFragment.install(file, id) else -> Unit } @@ -53,22 +39,13 @@ open class DownloadService : BaseDownloader() { override fun Notification.Builder.setIntent(subject: Subject) = when (subject) { - is Magisk -> setIntent(subject) is Module -> 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) = when (subject.action) { - is Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file)) + Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file)) else -> setContentIntent(Intent()) } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt index 4e03eb038..ad88d8913 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.os.Parcelable import androidx.core.net.toUri 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.StubJson import com.topjohnwu.magisk.core.model.module.OnlineModule @@ -53,57 +52,12 @@ sealed class Subject : Parcelable { } } - - abstract class Magisk : Subject() { - - val magisk: MagiskJson = Info.remote.magisk - - @Parcelize - private class Internal( - override val action: 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) - } - } - - } - +} + +sealed class Action : Parcelable { + @Parcelize + object Flash : Action() + + @Parcelize + object Download : Action() } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/tasks/FlashZip.kt b/app/src/main/java/com/topjohnwu/magisk/core/tasks/FlashZip.kt index e054f7c1b..d76c05200 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/tasks/FlashZip.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/tasks/FlashZip.kt @@ -2,14 +2,13 @@ package com.topjohnwu.magisk.core.tasks import android.content.Context 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.utils.MediaStoreUtils.displayName -import com.topjohnwu.magisk.core.utils.unzip import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream +import com.topjohnwu.magisk.core.utils.unzip import com.topjohnwu.magisk.ktx.writeTo import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.internal.UiThreadHandler import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.koin.core.KoinComponent @@ -25,61 +24,50 @@ open class FlashZip( private val logs: MutableList ): KoinComponent { - val context: Context by inject() - private val installFolder = File(context.cacheDir, "flash").apply { - if (!exists()) mkdirs() - } - 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 - } + private val context: Context by inject() + private val installDir = File(context.cacheDir, "flash") + private lateinit var zipFile: File @Throws(IOException::class) private fun flash(): Boolean { - console.add("- Copying zip to temp directory") + installDir.deleteRecursively() + installDir.mkdirs() - runCatching { - mUri.inputStream().writeTo(tmpFile) - }.getOrElse { - when (it) { - is FileNotFoundException -> console.add("! Invalid Uri") - is IOException -> console.add("! Cannot copy to cache") + zipFile = if (mUri.scheme == "file") { + mUri.toFile() + } else { + File(installDir, "install.zip").also { + console.add("- Copying zip to temp directory") + 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 { - unzipAndCheck() + val isValid = runCatching { + zipFile.unzip(installDir, "META-INF/com/google/android", true) + val script = File(installDir, "updater-script") + script.readText().contains("#MAGISK") }.getOrElse { console.add("! Unzip error") throw it } - if (!isMagiskModule) { - console.add("! This zip is not a Magisk Module!") + if (!isValid) { + console.add("! This zip is not a Magisk module!") return false } console.add("- Installing ${mUri.displayName}") - val parentFile = tmpFile.parent ?: return false - - return Shell - .su( - "cd $parentFile", - "BOOTMODE=true sh update-binary dummy 1 $tmpFile" - ) - .to(console, logs) - .exec().isSuccess + return Shell.su("sh $installDir/update-binary dummy 1 $zipFile") + .to(console, logs).exec().isSuccess } open suspend fun exec() = withContext(Dispatchers.IO) { @@ -94,25 +82,7 @@ open class FlashZip( Timber.e(e) false } 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, - log: MutableList - ) : 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 - } - } - } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt b/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt index f221714fb..a89cb375d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt @@ -91,8 +91,7 @@ object HideAPK { } private suspend fun patchAndHide(context: Context, label: String): Boolean { - val dlStub = !isRunningAsStub && SDK_INT >= 28 && Const.Version.atLeast_20_2() - val src = if (dlStub) { + val src = if (!isRunningAsStub && SDK_INT >= 28) { val stub = File(context.cacheDir, "stub.apk") try { svc.fetchFile(Info.remote.stub.link).byteStream().use { diff --git a/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt index eebb6ddf9..e8f8a7cd3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt @@ -1,25 +1,25 @@ package com.topjohnwu.magisk.core.tasks import android.content.Context -import android.content.Intent import android.net.Uri -import android.os.Build import android.widget.Toast import androidx.annotation.WorkerThread import androidx.core.os.postDelayed -import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.topjohnwu.magisk.BuildConfig +import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.R 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.inputStream import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.data.repository.NetworkService import com.topjohnwu.magisk.di.Protected -import com.topjohnwu.magisk.events.dialog.EnvFixDialog import com.topjohnwu.magisk.ktx.reboot +import com.topjohnwu.magisk.ktx.symlink import com.topjohnwu.magisk.ktx.withStreams +import com.topjohnwu.magisk.ktx.writeTo import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.signing.SignBoot import com.topjohnwu.superuser.Shell @@ -37,7 +37,6 @@ import org.kamranzafar.jtar.TarHeader import org.kamranzafar.jtar.TarInputStream import org.kamranzafar.jtar.TarOutputStream import org.koin.core.KoinComponent -import org.koin.core.get import org.koin.core.inject import timber.log.Timber import java.io.* @@ -46,10 +45,8 @@ import java.security.SecureRandom import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream -import kotlin.collections.set abstract class MagiskInstallImpl protected constructor( - private var zipUri: Uri, protected val console: MutableList = NOPList.getInstance(), private val logs: MutableList = NOPList.getInstance() ) : KoinComponent { @@ -59,17 +56,7 @@ abstract class MagiskInstallImpl protected constructor( private var tarOut: TarOutputStream? = null private val service: NetworkService by inject() - protected val context: Context by inject() - - companion object { - private val ABI_MAP = TreeMap() - init { - ABI_MAP["armeabi-v7a"] = "arm" - ABI_MAP["arm64-v8a"] = "arm64" - ABI_MAP["x86"] = "x86" - ABI_MAP["x86_64"] = "x64" - } - } + protected val context: Context by inject(Protected) private fun findImage(): Boolean { srcBoot = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh() @@ -98,69 +85,85 @@ abstract class MagiskInstallImpl protected constructor( return true } - @Suppress("DEPRECATION") - private fun extractZip(): Boolean { - val arch: String - val arch32: String - if (Build.VERSION.SDK_INT >= 21) { - arch = ABI_MAP[Build.SUPPORTED_ABIS[0]]!! - arch32 = ABI_MAP[Build.SUPPORTED_32_BIT_ABIS[0]]!! + private fun installDirFile(name: String): File { + return if (installDir is SuFile) + SuFile(installDir, name) + else + File(installDir, name) + } + + 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 { - arch = ABI_MAP[Build.CPU_ABI]!! - 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) + binDir } try { - ZipInputStream(zipUri.inputStream().buffered()).use { zi -> - lateinit var ze: ZipEntry - while (zi.nextEntry?.let { ze = it } != null) { - if (ze.isDirectory) - continue + // Extract binaries + if (isRunningAsStub) { + ZipInputStream(DynAPK.current(context).inputStream().buffered()).use { zi -> + lateinit var ze: ZipEntry + while (zi.nextEntry?.let { ze = it } != null) { + if (ze.isDirectory) + continue - var name: String? = null - - if (ze.name.startsWith("chromeos/")) { - name = ze.name - } else { - 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 name = if (ze.name.startsWith("lib/${Const.CPU_ABI_32}/")) { + val n = ze.name.substring(ze.name.lastIndexOf('/') + 1) + n.substring(3, n.length - 3) + } else { + continue } + + val dest = File(binDir, name) + dest.outputStream().use { zi.copyTo(it) } } - - name ?: continue - - val dest = newFile(name) - dest.parentFile!!.mkdirs() - SuFileOutputStream(dest).use { s -> zi.copyTo(s) } + } + } else { + val libs = Const.NATIVE_LIB_DIR.listFiles { _, name -> + name.startsWith("lib") && name.endsWith(".so") + } ?: emptyArray() + 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) return false } - val init64 = newFile("magiskinit64") - if (init64.exists() && arch != arch32) { - init64.renameTo(newFile("magiskinit")) - } else { - init64.delete() + if (installDir !== binDir) { + arrayOf( + "rm -rf $installDir", + "mkdir -p $installDir", + "cp_readlink $binDir $installDir", + "rm -rf $binDir" + ).sh() } - "cd $installDir; chmod 755 *".sh() + return true } @@ -177,7 +180,7 @@ abstract class MagiskInstallImpl protected constructor( lateinit var entry: TarEntry 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) { if (entry.name.contains("boot.img") || @@ -186,12 +189,9 @@ abstract class MagiskInstallImpl protected constructor( console.add("-- Extracting: $name") val extract = File(installDir, name) - FileOutputStream(extract).use { decompressedStream().copyTo(it) } + decompressedStream().writeTo(extract) } else if (entry.name.contains("vbmeta.img")) { - val rawData = ByteArrayOutputStream().let { - decompressedStream().copyTo(it) - it.toByteArray() - } + val rawData = decompressedStream().readBytes() // Valid vbmeta.img should be at least 256 bytes if (rawData.size < 256) continue @@ -208,8 +208,8 @@ abstract class MagiskInstallImpl protected constructor( tarIn.copyTo(tarOut, bufferSize = 1024 * 1024) } } - val boot = SuFile.open(installDir, "boot.img") - val recovery = SuFile.open(installDir, "recovery.img") + val boot = installDirFile("boot.img") + val recovery = installDirFile("recovery.img") if (Config.recovery && recovery.exists() && boot.exists()) { // Install Magisk to recovery srcBoot = recovery.path @@ -306,10 +306,19 @@ abstract class MagiskInstallImpl protected constructor( return false } + // Fix up binaries + if (installDir is SuFile) { + "fix_env $installDir".sh() + } else { + "cp_readlink $installDir".sh() + } + return true } private fun patchBoot(): Boolean { + "cd $installDir".sh() + var srcNand = "" if ("[ -c $srcBoot ] && nanddump -f boot.img $srcBoot".sh().isSuccess) { srcNand = srcBoot @@ -334,26 +343,20 @@ abstract class MagiskInstallImpl protected constructor( "KEEPVERITY=${Config.keepVerity} " + "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 - } - if (srcNand.isNotEmpty()) { + if (srcNand.isNotEmpty()) srcBoot = srcNand - } - val job = Shell.sh( - "./magiskboot cleanup", - "mv bin/busybox busybox", - "rm -rf magisk.apk bin boot.img update-binary", - "cd /") + val job = Shell.sh("./magiskboot cleanup", "cd /") - val patched = File(installDir, "new-boot.img") + val patched = installDirFile("new-boot.img") if (isSigned) { console.add("- Signing boot image with verity keys") - val signed = File(installDir, "signed.img") + val signed = installDirFile("signed.img") try { - withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) { + withStreams(SuFileInputStream(patched), SuFileOutputStream(signed)) { input, out -> SignBoot.doSignature(null, null, input, out, "/boot") } } catch (e: IOException) { @@ -368,18 +371,10 @@ abstract class MagiskInstallImpl protected constructor( 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 { if (!"direct_install $installDir $srcBoot".sh().isSuccess) return false - "run_migrations".sh() + arrayOf("run_migrations", "copy_sepolicy_rules").sh() return true } @@ -403,24 +398,28 @@ abstract class MagiskInstallImpl protected constructor( 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.sh() = Shell.sh(*this).to(console, logs).exec() private fun String.fsh() = ShellUtils.fastCmd(this) private fun Array.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() && - copySepolicyRules() && flashBoot() + protected fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot() - protected suspend fun secondSlot() = findSecondaryImage() && extractZip() && - patchBoot() && copySepolicyRules() && flashBoot() && postOTA() + protected suspend fun secondSlot() = + findSecondaryImage() && extractFiles() && patchBoot() && flashBoot() && postOTA() - protected fun fixEnv(): Boolean { - installDir = SuFile("/data/adb/magisk") - Shell.su("rm -rf /data/adb/magisk/*").exec() - return extractZip() && Shell.su("fix_env").exec().isSuccess - } + protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess @WorkerThread protected abstract suspend fun operations(): Boolean @@ -429,85 +428,84 @@ abstract class MagiskInstallImpl protected constructor( } abstract class MagiskInstaller( - zip: Uri, console: MutableList, logs: MutableList -) : MagiskInstallImpl(zip, console, logs) { - - init { - installDir = File(get(Protected).filesDir.parent, "install") - "rm -rf $installDir".sh() - installDir.mkdirs() - } +) : MagiskInstallImpl(console, logs) { override suspend fun exec(): Boolean { val success = super.exec() if (success) { console.add("- All done!") } 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") } return success } class Patch( - zip: Uri, private val uri: Uri, console: MutableList, logs: MutableList - ) : MagiskInstaller(zip, console, logs) { + ) : MagiskInstaller(console, logs) { override suspend fun operations() = doPatchFile(uri) } class SecondSlot( - zip: Uri, console: MutableList, logs: MutableList - ) : MagiskInstaller(zip, console, logs) { + ) : MagiskInstaller(console, logs) { override suspend fun operations() = secondSlot() } class Direct( - zip: Uri, console: MutableList, logs: MutableList - ) : MagiskInstaller(zip, console, logs) { + ) : MagiskInstaller(console, logs) { override suspend fun operations() = direct() } class Emulator( - zip: Uri, console: MutableList, logs: MutableList - ) : MagiskInstallImpl(zip, console, logs) { + ) : MagiskInstaller(console, logs) { override suspend fun operations() = fixEnv() + } + + class Uninstall( + console: MutableList, + logs: MutableList + ) : MagiskInstallImpl(console, logs) { + override suspend fun operations() = uninstall() override suspend fun exec(): Boolean { val success = super.exec() if (success) { - console.add("- All done!") - } else { - console.add("! Installation failed") + UiThreadHandler.handler.postDelayed(3000) { + Shell.su("pm uninstall ${context.packageName}").exec() + } } 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() - LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(EnvFixDialog.DISMISS)) - Utils.toast( - if (success) R.string.reboot_delay_toast else R.string.setup_fail, - Toast.LENGTH_LONG - ) - if (success) - UiThreadHandler.handler.postDelayed(5000) { reboot() } - return success + override suspend fun exec(): Boolean { + val success = super.exec() + callback() + Utils.toast( + if (success) R.string.reboot_delay_toast else R.string.setup_fail, + Toast.LENGTH_LONG + ) + if (success) + UiThreadHandler.handler.postDelayed(5000) { reboot() } + return success + } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/RootInit.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/RootInit.kt index 20db3339a..ac1a8f3b7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/RootInit.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/RootInit.kt @@ -2,14 +2,17 @@ package com.topjohnwu.magisk.core.utils import android.content.Context import android.os.Build +import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.Config -import com.topjohnwu.magisk.core.Const -import com.topjohnwu.magisk.core.Info -import com.topjohnwu.magisk.core.wrap +import com.topjohnwu.magisk.core.* +import com.topjohnwu.magisk.ktx.cachedFile +import com.topjohnwu.magisk.ktx.deviceProtectedContext import com.topjohnwu.magisk.ktx.rawResource +import com.topjohnwu.magisk.ktx.writeTo import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils +import java.io.File +import java.util.jar.JarFile class RootInit : Shell.Initializer() { @@ -17,31 +20,50 @@ class RootInit : Shell.Initializer() { return init(context.wrap(), shell) } - 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() + private fun init(context: Context, shell: Shell): Boolean { fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd) fun getVar(name: String) = fastCmd("echo \$$name") 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") Info.isSAR = getBool("SYSTEM_ROOT") Info.ramdisk = getBool("RAMDISKEXIST") diff --git a/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt b/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt index b72a1c6ad..2a6165cd3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt @@ -110,7 +110,7 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor { activity.startActivityForResult(intent, Const.ID.FETCH_ZIP) { code, intent -> if (code == Activity.RESULT_OK && intent != null) { intent.data?.also { - MainDirections.actionFlashFragment(it, Const.Value.FLASH_ZIP).navigate() + MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate() } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/events/dialog/DialogEvent.kt b/app/src/main/java/com/topjohnwu/magisk/events/dialog/DialogEvent.kt index 293c71e41..700326abb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/events/dialog/DialogEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/events/dialog/DialogEvent.kt @@ -10,7 +10,9 @@ abstract class DialogEvent : ViewEvent(), ActivityExecutor { protected lateinit var dialog: MagiskDialog 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) diff --git a/app/src/main/java/com/topjohnwu/magisk/events/dialog/EnvFixDialog.kt b/app/src/main/java/com/topjohnwu/magisk/events/dialog/EnvFixDialog.kt index 375f9d915..89c27ac94 100644 --- a/app/src/main/java/com/topjohnwu/magisk/events/dialog/EnvFixDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/events/dialog/EnvFixDialog.kt @@ -1,15 +1,11 @@ package com.topjohnwu.magisk.events.dialog -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.lifecycle.lifecycleScope import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.download.Action.EnvFix -import com.topjohnwu.magisk.core.download.DownloadService -import com.topjohnwu.magisk.core.download.Subject.Magisk +import com.topjohnwu.magisk.core.base.BaseActivity +import com.topjohnwu.magisk.core.tasks.MagiskInstaller import com.topjohnwu.magisk.view.MagiskDialog +import kotlinx.coroutines.launch class EnvFixDialog : DialogEvent() { @@ -24,20 +20,17 @@ class EnvFixDialog : DialogEvent() { .applyMessage(R.string.setup_msg) .resetButtons() .cancellable(false) - val lbm = LocalBroadcastManager.getInstance(dialog.context) - lbm.registerReceiver(object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent?) { + (dialog.ownerActivity as BaseActivity).lifecycleScope.launch { + MagiskInstaller.FixEnv { dialog.dismiss() - lbm.unregisterReceiver(this) - } - }, IntentFilter(DISMISS)) - DownloadService.start(dialog.context, Magisk(EnvFix)) + }.exec() + } } } .applyButton(MagiskDialog.ButtonType.NEGATIVE) { titleRes = android.R.string.cancel } - .let { Unit } + .let { } companion object { const val DISMISS = "com.topjohnwu.magisk.ENV_DONE" diff --git a/app/src/main/java/com/topjohnwu/magisk/events/dialog/ModuleInstallDialog.kt b/app/src/main/java/com/topjohnwu/magisk/events/dialog/ModuleInstallDialog.kt index ea7b1e069..0e0711940 100644 --- a/app/src/main/java/com/topjohnwu/magisk/events/dialog/ModuleInstallDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/events/dialog/ModuleInstallDialog.kt @@ -14,7 +14,7 @@ class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() { with(dialog) { 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) DownloadService.start(context, subject) } diff --git a/app/src/main/java/com/topjohnwu/magisk/events/dialog/UninstallDialog.kt b/app/src/main/java/com/topjohnwu/magisk/events/dialog/UninstallDialog.kt index 7ca5c42de..7eeb01a69 100644 --- a/app/src/main/java/com/topjohnwu/magisk/events/dialog/UninstallDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/events/dialog/UninstallDialog.kt @@ -4,9 +4,7 @@ import android.app.ProgressDialog import android.widget.Toast import com.topjohnwu.magisk.R 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.ui.flash.FlashFragment import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.superuser.Shell @@ -46,7 +44,7 @@ class UninstallDialog : DialogEvent() { } private fun completeUninstall() { - DownloadService.start(dialog.context, Subject.Magisk(Action.Uninstall)) + FlashFragment.uninstall() } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt index cc874e754..274803dcd 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt @@ -23,6 +23,7 @@ import android.graphics.drawable.LayerDrawable import android.net.Uri import android.os.Build import android.os.Build.VERSION.SDK_INT +import android.system.Os import android.text.PrecomputedText import android.view.View import android.view.ViewGroup @@ -59,6 +60,20 @@ import java.lang.reflect.Array as JArray val packageName: String get() = get().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 @get:SuppressLint("InlinedApi") @@ -83,6 +98,11 @@ fun Context.getBitmap(id: Int): 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.startActivityWithRoot() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt index d57608b4c..8b4e22d72 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt @@ -17,13 +17,12 @@ import com.topjohnwu.magisk.ui.MainActivity import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import com.topjohnwu.magisk.MainDirections.Companion.actionFlashFragment as toFlash -import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs as args class FlashFragment : BaseUIFragment() { override val layoutRes = R.layout.fragment_flash_md2 override val viewModel by viewModel { - parametersOf(args.fromBundle(requireArguments())) + parametersOf(FlashFragmentArgs.fromBundle(requireArguments())) } private var defaultOrientation = -1 @@ -78,7 +77,7 @@ class FlashFragment : BaseUIFragment() companion object { - private fun createIntent(context: Context, args: args) = + private fun createIntent(context: Context, args: FlashFragmentArgs) = NavDeepLinkBuilder(context) .setGraph(R.navigation.main) .setComponentName(MainActivity::class.java.cmp(context.packageName)) @@ -91,59 +90,34 @@ class FlashFragment : BaseUIFragment() /* Flashing is understood as installing / flashing magisk itself */ - fun flashIntent(context: Context, file: Uri, isSecondSlot: Boolean, id: Int = -1) = args( - installer = file, - 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 + fun flash(isSecondSlot: Boolean) = toFlash( + action = flashType(isSecondSlot) ).let { BaseUIActivity.postDirections(it) } /* Patching is understood as injecting img files with magisk */ - fun patchIntent(context: Context, file: Uri, uri: Uri, id: Int = -1) = args( - installer = file, + fun patch(uri: Uri) = toFlash( action = Const.Value.PATCH_FILE, - 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 + additionalData = uri ).let { BaseUIActivity.postDirections(it) } /* Uninstalling is understood as removing magisk entirely */ - fun uninstallIntent(context: Context, file: Uri, id: Int = -1) = args( - installer = file, - 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 + fun uninstall() = toFlash( + action = Const.Value.UNINSTALL ).let { BaseUIActivity.postDirections(it) } /* Installing is understood as flashing modules / zips */ - fun installIntent(context: Context, file: Uri, id: Int = -1) = args( - installer = file, + fun installIntent(context: Context, file: Uri, id: Int = -1) = FlashFragmentArgs( action = Const.Value.FLASH_ZIP, + additionalData = file, dismissId = id ).let { createIntent(context, it) } fun install(file: Uri, id: Int) = toFlash( - installer = file, action = Const.Value.FLASH_ZIP, + additionalData = file, dismissId = id ).let { BaseUIActivity.postDirections(it) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index ca97e0be5..796a8f506 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -27,9 +27,7 @@ import com.topjohnwu.superuser.Shell import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -class FlashViewModel( - args: FlashFragmentArgs -) : BaseViewModel() { +class FlashViewModel(args: FlashFragmentArgs) : BaseViewModel() { @get:Bindable var showReboot = Shell.rootAccess() @@ -52,36 +50,35 @@ class FlashViewModel( } init { - args.dismissId.takeIf { it != -1 }?.also { - Notifications.mgr.cancel(it) - } - val (installer, action, uri) = args - startFlashing(installer, uri, action) + val (action, uri, id) = args + if (id != -1) + Notifications.mgr.cancel(id) + startFlashing(action, uri) } - private fun startFlashing(installer: Uri, uri: Uri?, action: String) { + private fun startFlashing(action: String, uri: Uri?) { viewModelScope.launch { val result = when (action) { Const.Value.FLASH_ZIP -> { - FlashZip(installer, outItems, logItems).exec() + FlashZip(uri!!, outItems, logItems).exec() } Const.Value.UNINSTALL -> { showReboot = false - FlashZip.Uninstall(installer, outItems, logItems).exec() + MagiskInstaller.Uninstall(outItems, logItems).exec() } Const.Value.FLASH_MAGISK -> { if (Info.isEmulator) - MagiskInstaller.Emulator(installer, outItems, logItems).exec() + MagiskInstaller.Emulator(outItems, logItems).exec() else - MagiskInstaller.Direct(installer, outItems, logItems).exec() + MagiskInstaller.Direct(outItems, logItems).exec() } Const.Value.FLASH_INACTIVE_SLOT -> { - MagiskInstaller.SecondSlot(installer, outItems, logItems).exec() + MagiskInstaller.SecondSlot(outItems, logItems).exec() } Const.Value.PATCH_FILE -> { uri ?: return@launch showReboot = false - MagiskInstaller.Patch(installer, uri, outItems, logItems).exec() + MagiskInstaller.Patch(uri, outItems, logItems).exec() } else -> { back() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt index 53cc4407e..5958aad16 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.lifecycle.viewModelScope import com.topjohnwu.magisk.R import com.topjohnwu.magisk.arch.BaseUIFragment -import com.topjohnwu.magisk.core.download.BaseDownloader import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding import com.topjohnwu.magisk.ktx.coroutineScope import org.koin.androidx.viewmodel.ext.android.viewModel @@ -23,7 +22,6 @@ class InstallFragment : BaseUIFragment= 100) { - state = State.LOADED - } else if (this.progress < -150) { - state = State.LOADING_FAILED - } - } - fun step(nextStep: Int) { step = nextStep } 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 } - - // --- - - 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") - } } diff --git a/app/src/main/res/layout/fragment_install_md2.xml b/app/src/main/res/layout/fragment_install_md2.xml index 1555928a6..5fc64ea12 100644 --- a/app/src/main/res/layout/fragment_install_md2.xml +++ b/app/src/main/res/layout/fragment_install_md2.xml @@ -25,306 +25,217 @@ app:fitsSystemWindowsInsets="top|bottom" tools:paddingTop="24dp"> - + android:layout_height="wrap_content" + android:clipToPadding="false" + android:orientation="vertical" + android:paddingTop="@dimen/l_50"> - - - - - - - - - - - - -