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:
topjohnwu
2021-01-22 02:28:53 -08:00
parent 61d52991f1
commit ec8fffe61c
37 changed files with 784 additions and 1025 deletions
@@ -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<FlashViewModel, FragmentFlashMd2Binding>() {
override val layoutRes = R.layout.fragment_flash_md2
override val viewModel by viewModel<FlashViewModel> {
parametersOf(args.fromBundle(requireArguments()))
parametersOf(FlashFragmentArgs.fromBundle(requireArguments()))
}
private var defaultOrientation = -1
@@ -78,7 +77,7 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
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<FlashViewModel, FragmentFlashMd2Binding>()
/* 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) }
}
@@ -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()