Avoiding using shell I/O
This commit is contained in:
parent
6fb20b3ee5
commit
bec5edca84
@ -10,8 +10,9 @@ import androidx.multidex.MultiDex
|
|||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.DynAPK
|
||||||
|
import com.topjohnwu.magisk.core.utils.AppShellInit
|
||||||
|
import com.topjohnwu.magisk.core.utils.BusyBoxInit
|
||||||
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
|
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
|
||||||
import com.topjohnwu.magisk.core.utils.RootInit
|
|
||||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
import com.topjohnwu.magisk.core.utils.updateConfig
|
||||||
import com.topjohnwu.magisk.di.koinModules
|
import com.topjohnwu.magisk.di.koinModules
|
||||||
import com.topjohnwu.magisk.ktx.unwrap
|
import com.topjohnwu.magisk.ktx.unwrap
|
||||||
@ -32,7 +33,7 @@ open class App() : Application() {
|
|||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
Shell.setDefaultBuilder(Shell.Builder.create()
|
Shell.setDefaultBuilder(Shell.Builder.create()
|
||||||
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
||||||
.setInitializers(RootInit::class.java)
|
.setInitializers(BusyBoxInit::class.java, AppShellInit::class.java)
|
||||||
.setTimeout(2))
|
.setTimeout(2))
|
||||||
Shell.EXECUTOR = IODispatcherExecutor()
|
Shell.EXECUTOR = IODispatcherExecutor()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
import androidx.databinding.ObservableBoolean
|
import androidx.databinding.ObservableBoolean
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.DynAPK
|
||||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||||
@ -34,9 +35,10 @@ object Info {
|
|||||||
@JvmStatic val isFDE get() = crypto == "block"
|
@JvmStatic val isFDE get() = crypto == "block"
|
||||||
@JvmField var ramdisk = false
|
@JvmField var ramdisk = false
|
||||||
@JvmField var hasGMS = true
|
@JvmField var hasGMS = true
|
||||||
@JvmField var isPixel = false
|
@JvmField val isPixel = Build.BRAND == "google"
|
||||||
@JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1"
|
@JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1"
|
||||||
var crypto = ""
|
var crypto = ""
|
||||||
|
var noDataExec = false
|
||||||
|
|
||||||
val isConnected by lazy {
|
val isConnected by lazy {
|
||||||
ObservableBoolean(false).also { field ->
|
ObservableBoolean(false).also { field ->
|
||||||
|
@ -8,9 +8,9 @@ import androidx.core.os.postDelayed
|
|||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.DynAPK
|
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.core.utils.BusyBoxInit
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
import com.topjohnwu.magisk.core.utils.LightShellInit
|
||||||
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
|
||||||
@ -27,8 +27,6 @@ import com.topjohnwu.superuser.ShellUtils
|
|||||||
import com.topjohnwu.superuser.internal.NOPList
|
import com.topjohnwu.superuser.internal.NOPList
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import com.topjohnwu.superuser.io.SuFile
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
import com.topjohnwu.superuser.io.SuFileInputStream
|
|
||||||
import com.topjohnwu.superuser.io.SuFileOutputStream
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.jpountz.lz4.LZ4FrameInputStream
|
import net.jpountz.lz4.LZ4FrameInputStream
|
||||||
@ -53,7 +51,9 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
protected lateinit var installDir: File
|
protected lateinit var installDir: File
|
||||||
private lateinit var srcBoot: File
|
private lateinit var srcBoot: File
|
||||||
|
|
||||||
private var tarOut: TarOutputStream? = null
|
private lateinit var shell: Shell
|
||||||
|
private var closeShell = false
|
||||||
|
|
||||||
private val service: NetworkService by inject()
|
private val service: NetworkService by inject()
|
||||||
protected val context: Context by inject(Protected)
|
protected val context: Context by inject(Protected)
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findSecondaryImage(): Boolean {
|
private fun findSecondary(): Boolean {
|
||||||
val slot = "echo \$SLOT".fsh()
|
val slot = "echo \$SLOT".fsh()
|
||||||
val target = if (slot == "_a") "_b" else "_a"
|
val target = if (slot == "_a") "_b" else "_a"
|
||||||
console.add("- Target slot: $target")
|
console.add("- Target slot: $target")
|
||||||
@ -86,26 +86,13 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installDirFile(name: String): File {
|
|
||||||
return if (installDir is SuFile)
|
|
||||||
SuFile(installDir, name)
|
|
||||||
else
|
|
||||||
File(installDir, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractFiles(): Boolean {
|
private fun extractFiles(): Boolean {
|
||||||
console.add("- Device platform: ${Const.CPU_ABI}")
|
console.add("- Device platform: ${Const.CPU_ABI}")
|
||||||
console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||||
|
|
||||||
val binDir = File(context.filesDir.parent, "install")
|
installDir = File(context.filesDir.parent, "install")
|
||||||
binDir.deleteRecursively()
|
installDir.deleteRecursively()
|
||||||
binDir.mkdirs()
|
installDir.mkdirs()
|
||||||
|
|
||||||
installDir = if (Shell.rootAccess()) {
|
|
||||||
SuFile("${Const.TMPDIR}/install")
|
|
||||||
} else {
|
|
||||||
binDir
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Extract binaries
|
// Extract binaries
|
||||||
@ -116,7 +103,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}.forEach {
|
}.forEach {
|
||||||
val n = it.name.substring(it.name.lastIndexOf('/') + 1)
|
val n = it.name.substring(it.name.lastIndexOf('/') + 1)
|
||||||
val name = n.substring(3, n.length - 3)
|
val name = n.substring(3, n.length - 3)
|
||||||
val dest = File(binDir, name)
|
val dest = File(installDir, name)
|
||||||
zf.getInputStream(it).writeTo(dest)
|
zf.getInputStream(it).writeTo(dest)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -125,21 +112,21 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
} ?: emptyArray()
|
} ?: emptyArray()
|
||||||
for (lib in libs) {
|
for (lib in libs) {
|
||||||
val name = lib.name.substring(3, lib.name.length - 3)
|
val name = lib.name.substring(3, lib.name.length - 3)
|
||||||
val bin = File(binDir, name)
|
val bin = File(installDir, name)
|
||||||
symlink(lib.path, bin.path)
|
symlink(lib.path, bin.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract scripts
|
// Extract scripts
|
||||||
for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) {
|
for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) {
|
||||||
val dest = File(binDir, script)
|
val dest = File(installDir, script)
|
||||||
context.assets.open(script).writeTo(dest)
|
context.assets.open(script).writeTo(dest)
|
||||||
}
|
}
|
||||||
// Extract chromeos tools
|
// Extract chromeos tools
|
||||||
File(binDir, "chromeos").mkdir()
|
File(installDir, "chromeos").mkdir()
|
||||||
for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) {
|
for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) {
|
||||||
val name = "chromeos/$file"
|
val name = "chromeos/$file"
|
||||||
val dest = File(binDir, name)
|
val dest = File(installDir, name)
|
||||||
context.assets.open(name).writeTo(dest)
|
context.assets.open(name).writeTo(dest)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -148,19 +135,10 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (installDir !== binDir) {
|
|
||||||
arrayOf(
|
|
||||||
"rm -rf $installDir",
|
|
||||||
"mkdir -p $installDir",
|
|
||||||
"cp_readlink $binDir $installDir",
|
|
||||||
"rm -rf $binDir"
|
|
||||||
).sh()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newEntry(name: String, size: Long): TarEntry {
|
private fun newTarEntry(name: String, size: Long): TarEntry {
|
||||||
console.add("-- Writing: $name")
|
console.add("-- Writing: $name")
|
||||||
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
|
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
|
||||||
}
|
}
|
||||||
@ -172,8 +150,13 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
TarInputStream(input).use { tarIn ->
|
TarInputStream(input).use { tarIn ->
|
||||||
lateinit var entry: TarEntry
|
lateinit var entry: TarEntry
|
||||||
|
|
||||||
fun decompressedStream() =
|
fun decompressedStream(): InputStream {
|
||||||
if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
val src = if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
||||||
|
return object : FilterInputStream(src) {
|
||||||
|
override fun available() = 0 /* Workaround bug in LZ4FrameInputStream */
|
||||||
|
override fun close() { /* Never close src stream */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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") ||
|
||||||
@ -181,15 +164,10 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
val name = entry.name.replace(".lz4", "")
|
val name = entry.name.replace(".lz4", "")
|
||||||
console.add("-- Extracting: $name")
|
console.add("-- Extracting: $name")
|
||||||
|
|
||||||
val extract = installDirFile(name)
|
val extract = File(installDir, name)
|
||||||
SuFileOutputStream(extract).use { decompressedStream().copyTo(it) }
|
decompressedStream().writeTo(extract)
|
||||||
} else if (entry.name.contains("vbmeta.img")) {
|
} else if (entry.name.contains("vbmeta.img")) {
|
||||||
// DO NOT USE readBytes() DUE TO BUG IN LZ4FrameInputStream
|
val rawData = decompressedStream().readBytes()
|
||||||
val rawData = decompressedStream().run {
|
|
||||||
val buffer = ByteArrayOutputStream()
|
|
||||||
copyTo(buffer)
|
|
||||||
buffer.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
|
||||||
@ -198,7 +176,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
||||||
console.add("-- Patching: vbmeta.img")
|
console.add("-- Patching: vbmeta.img")
|
||||||
ByteBuffer.wrap(rawData).putInt(120, 3)
|
ByteBuffer.wrap(rawData).putInt(120, 3)
|
||||||
tarOut.putNextEntry(newEntry("vbmeta.img", rawData.size.toLong()))
|
tarOut.putNextEntry(newTarEntry("vbmeta.img", rawData.size.toLong()))
|
||||||
tarOut.write(rawData)
|
tarOut.write(rawData)
|
||||||
} else {
|
} else {
|
||||||
console.add("-- Copying: ${entry.name}")
|
console.add("-- Copying: ${entry.name}")
|
||||||
@ -207,19 +185,21 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val boot = installDirFile("boot.img")
|
val boot = File(installDir, "boot.img")
|
||||||
val recovery = installDirFile("recovery.img")
|
val recovery = File(installDir, "recovery.img")
|
||||||
if (Config.recovery && recovery.exists() && boot.exists()) {
|
if (Config.recovery && recovery.exists() && boot.exists()) {
|
||||||
// Install Magisk to recovery
|
// Install to recovery
|
||||||
srcBoot = recovery
|
srcBoot = recovery
|
||||||
// Repack boot image to prevent restore
|
// Repack boot image to prevent auto restore
|
||||||
arrayOf(
|
arrayOf(
|
||||||
|
"cd $installDir",
|
||||||
"./magiskboot unpack boot.img",
|
"./magiskboot unpack boot.img",
|
||||||
"./magiskboot repack boot.img",
|
"./magiskboot repack boot.img",
|
||||||
"./magiskboot cleanup",
|
"./magiskboot cleanup",
|
||||||
"mv new-boot.img boot.img").sh()
|
"mv new-boot.img boot.img",
|
||||||
SuFileInputStream(boot).use {
|
"cd /").sh()
|
||||||
tarOut.putNextEntry(newEntry("boot.img", boot.length()))
|
boot.inputStream().use {
|
||||||
|
tarOut.putNextEntry(newTarEntry("boot.img", boot.length()))
|
||||||
it.copyTo(tarOut)
|
it.copyTo(tarOut)
|
||||||
}
|
}
|
||||||
boot.delete()
|
boot.delete()
|
||||||
@ -263,9 +243,9 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
handleTar(src, outFile!!.uri.outputStream())
|
handleTar(src, outFile!!.uri.outputStream())
|
||||||
} else {
|
} else {
|
||||||
// Raw image
|
// Raw image
|
||||||
srcBoot = installDirFile("boot.img")
|
srcBoot = File(installDir, "boot.img")
|
||||||
console.add("- Copying image to cache")
|
console.add("- Copying image to cache")
|
||||||
SuFileOutputStream(srcBoot).use { src.copyTo(it) }
|
src.writeTo(srcBoot)
|
||||||
outFile = MediaStoreUtils.getFile("$filename.img", true)
|
outFile = MediaStoreUtils.getFile("$filename.img", true)
|
||||||
outFile!!.uri.outputStream()
|
outFile!!.uri.outputStream()
|
||||||
}
|
}
|
||||||
@ -285,12 +265,12 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
|
|
||||||
// Output file
|
// Output file
|
||||||
try {
|
try {
|
||||||
val patched = installDirFile("new-boot.img")
|
val patched = File(installDir, "new-boot.img")
|
||||||
if (outStream is TarOutputStream) {
|
if (outStream is TarOutputStream) {
|
||||||
val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img"
|
val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img"
|
||||||
outStream.putNextEntry(newEntry(name, patched.length()))
|
outStream.putNextEntry(newTarEntry(name, patched.length()))
|
||||||
}
|
}
|
||||||
withStreams(SuFileInputStream(patched), outStream) { src, out -> src.copyTo(out) }
|
withStreams(patched.inputStream(), outStream) { src, out -> src.copyTo(out) }
|
||||||
patched.delete()
|
patched.delete()
|
||||||
|
|
||||||
console.add("")
|
console.add("")
|
||||||
@ -306,22 +286,56 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fix up binaries
|
// Fix up binaries
|
||||||
if (installDir is SuFile) {
|
srcBoot.delete()
|
||||||
"fix_env $installDir".sh()
|
"cp_readlink $installDir".sh()
|
||||||
} else {
|
|
||||||
"cp_readlink $installDir".sh()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun rootInputStream(file: File): InputStream {
|
||||||
|
return if (file is SuFile)
|
||||||
|
object : BufferedInputStream(null) {
|
||||||
|
private val tmp =
|
||||||
|
File.createTempFile(file.name, null, context.cacheDir).also {
|
||||||
|
// Copy to tmp file and read from there
|
||||||
|
"cat $file > $it".sh()
|
||||||
|
`in` = it.inputStream()
|
||||||
|
}
|
||||||
|
override fun close() {
|
||||||
|
super.close()
|
||||||
|
tmp.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
file.inputStream().buffered()
|
||||||
|
}
|
||||||
|
|
||||||
private fun patchBoot(): Boolean {
|
private fun patchBoot(): Boolean {
|
||||||
"cd $installDir".sh()
|
val inRootDir = shell.isRoot && Info.noDataExec
|
||||||
|
|
||||||
|
val newBootImg: File
|
||||||
|
if (inRootDir) {
|
||||||
|
// Migrate everything to tmpfs to workaround Samsung bullshit
|
||||||
|
SuFile("${Const.TMPDIR}/install").also {
|
||||||
|
arrayOf(
|
||||||
|
"rm -rf $it",
|
||||||
|
"mkdir -p $it",
|
||||||
|
"cp_readlink $installDir $it",
|
||||||
|
"rm -rf $installDir"
|
||||||
|
).sh()
|
||||||
|
installDir = it
|
||||||
|
}
|
||||||
|
newBootImg = SuFile(installDir, "new-boot.img")
|
||||||
|
} else {
|
||||||
|
newBootImg = File(installDir, "new-boot.img")
|
||||||
|
// Create the output file before hand
|
||||||
|
newBootImg.createNewFile()
|
||||||
|
}
|
||||||
|
|
||||||
var isSigned = false
|
var isSigned = false
|
||||||
if (srcBoot.let { it !is SuFile || !it.isCharacter }) {
|
if (srcBoot.let { it !is SuFile || !it.isCharacter }) {
|
||||||
try {
|
try {
|
||||||
SuFileInputStream(srcBoot).use {
|
rootInputStream(srcBoot).use {
|
||||||
if (SignBoot.verifySignature(it, null)) {
|
if (SignBoot.verifySignature(it, null)) {
|
||||||
isSigned = true
|
isSigned = true
|
||||||
console.add("- Boot image is signed with AVB 1.0")
|
console.add("- Boot image is signed with AVB 1.0")
|
||||||
@ -333,90 +347,99 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val FLAGS =
|
val flags =
|
||||||
"KEEPFORCEENCRYPT=${Config.keepEnc} " +
|
"KEEPFORCEENCRYPT=${Config.keepEnc} " +
|
||||||
"KEEPVERITY=${Config.keepVerity} " +
|
"KEEPVERITY=${Config.keepVerity} " +
|
||||||
"RECOVERYMODE=${Config.recovery}"
|
"RECOVERYMODE=${Config.recovery}"
|
||||||
|
|
||||||
if (!"$FLAGS sh boot_patch.sh $srcBoot".sh().isSuccess)
|
if (!"cd $installDir; $flags sh boot_patch.sh $srcBoot".sh().isSuccess)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
val job = Shell.sh("./magiskboot cleanup", "cd /")
|
val job = shell.newJob().add("./magiskboot cleanup", "cd /")
|
||||||
|
|
||||||
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 = installDirFile("signed.img")
|
val signed = File.createTempFile("signed", ".img", context.cacheDir)
|
||||||
try {
|
try {
|
||||||
withStreams(SuFileInputStream(patched), SuFileOutputStream(signed)) {
|
withStreams(rootInputStream(newBootImg), signed.outputStream().buffered()) {
|
||||||
input, out -> SignBoot.doSignature(null, null, input, out, "/boot")
|
src, out -> SignBoot.doSignature(null, null, src, out, "/boot")
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
console.add("! Unable to sign image")
|
console.add("! Unable to sign image")
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (inRootDir) {
|
||||||
job.add("mv -f $signed $patched")
|
job.add("cp -f $signed $newBootImg", "rm -f $signed")
|
||||||
|
} else {
|
||||||
|
signed.copyTo(newBootImg, overwrite = true)
|
||||||
|
signed.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
job.exec()
|
job.exec()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flashBoot(): Boolean {
|
private fun flashBoot() = "direct_install $installDir $srcBoot".sh().isSuccess
|
||||||
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
|
|
||||||
return false
|
|
||||||
arrayOf("run_migrations", "copy_sepolicy_rules").sh()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun postOTA(): Boolean {
|
private suspend fun postOTA(): Boolean {
|
||||||
val bootctl = SuFile("/data/adb/bootctl")
|
|
||||||
try {
|
try {
|
||||||
withStreams(service.fetchBootctl().byteStream(), SuFileOutputStream(bootctl)) {
|
val bootctl = File.createTempFile("bootctl", null, context.cacheDir)
|
||||||
it, out -> it.copyTo(out)
|
service.fetchBootctl().byteStream().writeTo(bootctl)
|
||||||
}
|
"post_ota $bootctl".sh()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
console.add("! Unable to download bootctl")
|
console.add("! Unable to download bootctl")
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
"post_ota ${bootctl.parent}".sh()
|
|
||||||
|
|
||||||
console.add("***************************************")
|
console.add("***************************************")
|
||||||
console.add(" Next reboot will boot to second slot!")
|
console.add(" Next reboot will boot to second slot!")
|
||||||
console.add("***************************************")
|
console.add("***************************************")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun uninstall(): Boolean {
|
private fun setupShell(forceNonRoot: Boolean = false): Boolean {
|
||||||
val apk = if (isRunningAsStub) {
|
shell = Shell.getShell()
|
||||||
DynAPK.current(context).path
|
if (forceNonRoot && shell.isRoot) {
|
||||||
} else {
|
shell = Shell.Builder.create()
|
||||||
context.packageCodePath
|
.setFlags(Shell.FLAG_NON_ROOT_SHELL)
|
||||||
|
.setInitializers(BusyBoxInit::class.java, LightShellInit::class.java)
|
||||||
|
.build()
|
||||||
|
closeShell = true
|
||||||
}
|
}
|
||||||
return "run_uninstaller $apk".sh().isSuccess
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.sh() = Shell.sh(this).to(console, logs).exec()
|
private fun String.sh() = shell.newJob().add(this).to(console, logs).exec()
|
||||||
private fun Array<String>.sh() = Shell.sh(*this).to(console, logs).exec()
|
private fun Array<String>.sh() = shell.newJob().add(*this).to(console, logs).exec()
|
||||||
private fun String.fsh() = ShellUtils.fastCmd(this)
|
private fun String.fsh() = ShellUtils.fastCmd(shell, this)
|
||||||
private fun Array<String>.fsh() = ShellUtils.fastCmd(*this)
|
private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)
|
||||||
|
|
||||||
protected fun doPatchFile(patchFile: Uri) = extractFiles() && handleFile(patchFile)
|
protected fun doPatchFile(patchFile: Uri) =
|
||||||
|
setupShell(true) && extractFiles() && handleFile(patchFile)
|
||||||
|
|
||||||
protected fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot()
|
protected fun direct() =
|
||||||
|
setupShell() && findImage() && extractFiles() && patchBoot() && flashBoot()
|
||||||
|
|
||||||
protected suspend fun secondSlot() =
|
protected suspend fun secondSlot() =
|
||||||
findSecondaryImage() && extractFiles() && patchBoot() && flashBoot() && postOTA()
|
setupShell() && findSecondary() && extractFiles() && patchBoot() && flashBoot() && postOTA()
|
||||||
|
|
||||||
protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess
|
protected fun fixEnv() =
|
||||||
|
setupShell() && extractFiles() && "fix_env $installDir".sh().isSuccess
|
||||||
|
|
||||||
|
protected fun uninstall() =
|
||||||
|
setupShell() && "run_uninstaller ${AssetHack.apk}".sh().isSuccess
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
protected abstract suspend fun operations(): Boolean
|
protected abstract suspend fun operations(): Boolean
|
||||||
|
|
||||||
open suspend fun exec() = withContext(Dispatchers.IO) { operations() }
|
open suspend fun exec() = withContext(Dispatchers.IO) {
|
||||||
|
val result = operations()
|
||||||
|
if (closeShell)
|
||||||
|
shell.close()
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MagiskInstaller(
|
abstract class MagiskInstaller(
|
||||||
|
@ -14,20 +14,19 @@ import com.topjohnwu.superuser.ShellUtils
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.jar.JarFile
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
class RootInit : Shell.Initializer() {
|
abstract class BaseShellInit : Shell.Initializer() {
|
||||||
|
final override fun onInit(context: Context, shell: Shell): Boolean {
|
||||||
override fun onInit(context: Context, shell: Shell): Boolean {
|
|
||||||
return init(context.wrap(), shell)
|
return init(context.wrap(), shell)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun init(context: Context, shell: Shell): Boolean {
|
abstract 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()
|
|
||||||
|
|
||||||
|
class BusyBoxInit : BaseShellInit() {
|
||||||
|
|
||||||
|
override fun init(context: Context, shell: Shell): Boolean {
|
||||||
shell.newJob().apply {
|
shell.newJob().apply {
|
||||||
add("export SDK_INT=${Build.VERSION.SDK_INT}")
|
|
||||||
add("export ASH_STANDALONE=1")
|
add("export ASH_STANDALONE=1")
|
||||||
|
|
||||||
val localBB: File
|
val localBB: File
|
||||||
@ -41,27 +40,58 @@ class RootInit : Shell.Initializer() {
|
|||||||
localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so")
|
localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shell.isRoot) {
|
if (shell.isRoot) {
|
||||||
// Directly execute the file
|
add("export MAGISKTMP=\$(magisk --path)/.magisk")
|
||||||
add("exec $localBB sh")
|
// Test if we can properly execute stuff in /data
|
||||||
} else {
|
Info.noDataExec = !shell.newJob().add("$localBB true").exec().isSuccess
|
||||||
// Copy it out of /data to workaround Samsung BS
|
}
|
||||||
|
|
||||||
|
if (Info.noDataExec) {
|
||||||
|
// Copy it out of /data to workaround Samsung bullshit
|
||||||
add(
|
add(
|
||||||
"export MAGISKTMP=\$(magisk --path)/.magisk",
|
|
||||||
"if [ -x \$MAGISKTMP/busybox/busybox ]; then",
|
"if [ -x \$MAGISKTMP/busybox/busybox ]; then",
|
||||||
" cp -af $localBB \$MAGISKTMP/busybox/busybox",
|
" cp -af $localBB \$MAGISKTMP/busybox/busybox",
|
||||||
" exec \$MAGISKTMP/busybox/busybox sh",
|
" exec \$MAGISKTMP/busybox/busybox sh",
|
||||||
"else",
|
"else",
|
||||||
" exec $localBB sh",
|
" cp -af $localBB /dev/.busybox",
|
||||||
|
" exec /dev/.busybox sh",
|
||||||
"fi"
|
"fi"
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
// Directly execute the file
|
||||||
|
add("exec $localBB sh")
|
||||||
}
|
}
|
||||||
|
}.exec()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LightShellInit : BaseShellInit() {
|
||||||
|
|
||||||
|
override fun init(context: Context, shell: Shell): Boolean {
|
||||||
|
shell.newJob().apply {
|
||||||
|
add("export SDK_INT=${Build.VERSION.SDK_INT}")
|
||||||
|
add(context.rawResource(R.raw.manager))
|
||||||
|
}.exec()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppShellInit : BaseShellInit() {
|
||||||
|
|
||||||
|
override 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(context.rawResource(R.raw.manager))
|
add(context.rawResource(R.raw.manager))
|
||||||
if (shell.isRoot) {
|
if (shell.isRoot) {
|
||||||
add(context.assets.open("util_functions.sh"))
|
add(context.assets.open("util_functions.sh"))
|
||||||
}
|
}
|
||||||
add("mm_init")
|
add("app_init")
|
||||||
}.exec()
|
}.exec()
|
||||||
|
|
||||||
Const.MAGISKTMP = getVar("MAGISKTMP")
|
Const.MAGISKTMP = getVar("MAGISKTMP")
|
||||||
@ -69,7 +99,6 @@ class RootInit : Shell.Initializer() {
|
|||||||
Info.ramdisk = getBool("RAMDISKEXIST")
|
Info.ramdisk = getBool("RAMDISKEXIST")
|
||||||
Info.isAB = getBool("ISAB")
|
Info.isAB = getBool("ISAB")
|
||||||
Info.crypto = getVar("CRYPTOTYPE")
|
Info.crypto = getVar("CRYPTOTYPE")
|
||||||
Info.isPixel = fastCmd("getprop ro.product.brand") == "google"
|
|
||||||
|
|
||||||
// Default presets
|
// Default presets
|
||||||
Config.recovery = getBool("RECOVERYMODE")
|
Config.recovery = getBool("RECOVERYMODE")
|
@ -36,7 +36,7 @@ fix_env() {
|
|||||||
rm -rf $MAGISKBIN/*
|
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_readlink $1 $MAGISKBIN
|
||||||
rm -rf $1
|
rm -rf $1
|
||||||
chown -R 0:0 $MAGISKBIN
|
chown -R 0:0 $MAGISKBIN
|
||||||
}
|
}
|
||||||
@ -57,6 +57,8 @@ direct_install() {
|
|||||||
|
|
||||||
rm -f $1/new-boot.img
|
rm -f $1/new-boot.img
|
||||||
fix_env $1
|
fix_env $1
|
||||||
|
run_migrations
|
||||||
|
copy_sepolicy_rules
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -87,15 +89,17 @@ restore_imgs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post_ota() {
|
post_ota() {
|
||||||
cd $1
|
cd /data/adb
|
||||||
|
cp -f $1 bootctl
|
||||||
|
rm -f $1
|
||||||
chmod 755 bootctl
|
chmod 755 bootctl
|
||||||
./bootctl hal-info || return
|
./bootctl hal-info || return
|
||||||
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
|
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
|
||||||
./bootctl set-active-boot-slot $SLOT_NUM
|
./bootctl set-active-boot-slot $SLOT_NUM
|
||||||
cat << EOF > post-fs-data.d/post_ota.sh
|
cat << EOF > post-fs-data.d/post_ota.sh
|
||||||
${1}/bootctl mark-boot-successful
|
/data/adb/bootctl mark-boot-successful
|
||||||
rm -f ${1}/bootctl
|
rm -f /data/adb/bootctl
|
||||||
rm -f ${1}/post-fs-data.d/post_ota.sh
|
rm -f /data/adb/post-fs-data.d/post_ota.sh
|
||||||
EOF
|
EOF
|
||||||
chmod 755 post-fs-data.d/post_ota.sh
|
chmod 755 post-fs-data.d/post_ota.sh
|
||||||
cd /
|
cd /
|
||||||
@ -196,8 +200,7 @@ grep_prop() { return; }
|
|||||||
# Initialize
|
# Initialize
|
||||||
#############
|
#############
|
||||||
|
|
||||||
mm_init() {
|
app_init() {
|
||||||
export BOOTMODE=true
|
|
||||||
mount_partitions
|
mount_partitions
|
||||||
get_flags
|
get_flags
|
||||||
run_migrations
|
run_migrations
|
||||||
@ -207,3 +210,5 @@ mm_init() {
|
|||||||
# Make sure RECOVERYMODE has value
|
# Make sure RECOVERYMODE has value
|
||||||
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
|
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export BOOTMODE=true
|
||||||
|
@ -28,6 +28,6 @@ kapt.incremental.apt=true
|
|||||||
|
|
||||||
# Magisk
|
# Magisk
|
||||||
magisk.stubVersion=17
|
magisk.stubVersion=17
|
||||||
magisk.versionCode=21405
|
magisk.versionCode=21406
|
||||||
magisk.ndkVersion=21d
|
magisk.ndkVersion=21d
|
||||||
magisk.fullNdkVersion=21.3.6528147
|
magisk.fullNdkVersion=21.3.6528147
|
||||||
|
Loading…
Reference in New Issue
Block a user