Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(patch): add direct boot patch install method (only when updating) #34

Merged
merged 3 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text eol=lf

# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
# *.c text
# *.h text

# Declare files that will always have CRLF line endings on checkout.
*.cmd text eol=crlf
*.bat text eol=crlf

# Denote all files that are truly binary and should not be modified.
tools/** binary
*.jar binary
*.exe binary
*.apk binary
*.png binary
*.jpg binary
*.ttf binary
*.so binary

# Help GitHub detect languages
native/jni/external/** linguist-vendored
native/jni/systemproperties/** linguist-language=C++
25 changes: 18 additions & 7 deletions app/src/main/assets/boot_patch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# File name Type Description
#
# boot_patch.sh script A script to patch boot image for APatch.
# (this file) The script will use files in its same
# (this file) The script will use files in its same
# directory to complete the patching process.
# bootimg binary The target boot image
# kpimg binary KernelPatch core Image
Expand Down Expand Up @@ -38,30 +38,43 @@ getdir() {
# Switch to the location of the script file
cd "$(getdir "${BASH_SOURCE:-$0}")"

# Load utility functions
. ./util_functions.sh

echo "APatch Boot Image Patcher"

# Check if 64-bit
if [ $(uname -m) = "aarch64" ]; then
echo "System is arm64"
else
echo "Not is arm64"
exit
echo "System is not arm64"
exit 1
fi


SUPERKEY=$1
BOOTIMAGE=$2

if [ -z "$BOOTIMAGE" ]; then
find_boot_image
fi

[ -z "$SUPERKEY" ] && { echo "SuperKey empty!"; exit 1; }
[ -e "$BOOTIMAGE" ] || { echo "$BOOTIMAGE does not exist!"; exit 1; }

echo "- Target image: $BOOTIMAGE"

# Check for dependencies
command -v ./magiskboot >/dev/null 2>&1 || { echo "magiskboot not found!"; exit 1; }
command -v ./kptools >/dev/null 2>&1 || { echo "kptools not found!"; exit 1; }

echo "- Unpacking boot image"
./magiskboot unpack "$BOOTIMAGE" >/dev/null 2>&1

if [ $? -ne 0 ]; then
echo "Unpack error: $?"
exit $?
fi

mv kernel kernel.ori

echo "- Patching kernel"
Expand All @@ -75,7 +88,5 @@ fi
echo "- Repacking boot image"
./magiskboot repack "$BOOTIMAGE" >/dev/null 2>&1 || exit $?

ls -l "new-boot.img" | echo

# Reset any error code
true
true
60 changes: 60 additions & 0 deletions app/src/main/assets/util_functions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/system/bin/sh
#######################################################################################
# Helper Functions (credits to topjohnwu)
#######################################################################################

toupper() {
echo "$@" | tr '[:lower:]' '[:upper:]'
}

grep_prop() {
local REGEX="s/^$1=//p"
shift
local FILES=$@
[ -z "$FILES" ] && FILES='/system/build.prop'
cat $FILES 2>/dev/null | dos2unix | sed -n "$REGEX" | head -n 1
}

find_block() {
local BLOCK DEV DEVICE DEVNAME PARTNAME UEVENT
for BLOCK in "$@"; do
DEVICE=$(find /dev/block \( -type b -o -type c -o -type l \) -iname $BLOCK | head -n 1) 2>/dev/null
if [ ! -z $DEVICE ]; then
readlink -f $DEVICE
return 0
fi
done
# Fallback by parsing sysfs uevents
for UEVENT in /sys/dev/block/*/uevent; do
DEVNAME=$(grep_prop DEVNAME $UEVENT)
PARTNAME=$(grep_prop PARTNAME $UEVENT)
for BLOCK in "$@"; do
if [ "$(toupper $BLOCK)" = "$(toupper $PARTNAME)" ]; then
echo /dev/block/$DEVNAME
return 0
fi
done
done
# Look just in /dev in case we're dealing with MTD/NAND without /dev/block devices/links
for DEV in "$@"; do
DEVICE=$(find /dev \( -type b -o -type c -o -type l \) -maxdepth 1 -iname $DEV | head -n 1) 2>/dev/null
if [ ! -z $DEVICE ]; then
readlink -f $DEVICE
return 0
fi
done
return 1
}

find_boot_image() {
SLOT=$(getprop ro.boot.slot_suffix)
if [ ! -z $SLOT ]; then
BOOTIMAGE=$(find_block "ramdisk$SLOT" "recovery_ramdisk$SLOT" "init_boot$SLOT" "boot$SLOT")
else
BOOTIMAGE=$(find_block ramdisk recovery_ramdisk kern-a android_boot kernel bootimg init_boot boot lnx boot_a)
fi
if [ -z $BOOTIMAGE ]; then
# Lets see what fstabs tells me
BOOTIMAGE=$(grep -v '#' /etc/*fstab* | grep -E '/boot(img)?[^a-zA-Z]' | grep -oE '/dev/[a-zA-Z0-9_./-]*' | head -n 1)
fi
}
4 changes: 4 additions & 0 deletions app/src/main/java/me/bmax/apatch/APatchApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ class APApplication : Application() {
}
}

fun getSuperKey(): String {
return superKey
}

fun updateSuperKey(password: String) {
superKey = password
}
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/me/bmax/apatch/ui/screen/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
) {
WarningCard()
KStatusCard(state)
AStatusCard(state)
AStatusCard(state, navigator)
InfoCard()
LearnMoreCard()
Spacer(Modifier)
Expand Down Expand Up @@ -397,7 +397,7 @@ private fun KStatusCard(state: APApplication.State) {
}

@Composable
private fun AStatusCard(state: APApplication.State) {
private fun AStatusCard(state: APApplication.State, navigator: DestinationsNavigator) {
ElevatedCard(
colors = CardDefaults.elevatedCardColors(containerColor = run {
MaterialTheme.colorScheme.secondaryContainer
Expand Down Expand Up @@ -470,6 +470,9 @@ private fun AStatusCard(state: APApplication.State) {
state.equals(APApplication.State.KERNELPATCH_READY)
|| state.equals(APApplication.State.ANDROIDPATCH_NEED_UPDATE) -> {
APApplication.install()
if (state.equals(APApplication.State.ANDROIDPATCH_NEED_UPDATE)) {
navigator.navigate(PatchScreenDestination(null, apApp.getSuperKey()))
}
}
state.equals(APApplication.State.ANDROIDPATCH_INSTALLED) -> {
APApplication.uninstall()
Expand Down
91 changes: 53 additions & 38 deletions app/src/main/java/me/bmax/apatch/ui/screen/Patch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import java.util.*

@Composable
@Destination
fun PatchScreen(navigator: DestinationsNavigator, uri: Uri, superKey: String) {
fun PatchScreen(navigator: DestinationsNavigator, uri: Uri?, superKey: String) {
var text by remember { mutableStateOf("") }
var showFloatAction by remember { mutableStateOf(false) }
val scrollState = rememberScrollState()
Expand Down Expand Up @@ -153,23 +153,27 @@ private fun String.fsh() = ShellUtils.fastCmd(shell, this)
private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)


fun patchBootimg(uri: Uri, superKey: String, logs: MutableList<String>): Boolean {
fun patchBootimg(uri: Uri?, superKey: String, logs: MutableList<String>): Boolean {
var outPath: File? = null
var srcBoot: ExtendedFile? = null
var patchDir: ExtendedFile = FileSystemManager.getLocal().getFile(apApp.filesDir.parent, "patch")
patchDir.deleteRecursively()
patchDir.mkdirs()

val srcBoot = patchDir.getChildFile("boot.img")
if (uri != null) {
srcBoot = patchDir.getChildFile("boot.img")

// Process input file
try {
uri.inputStream().buffered().use { src ->
srcBoot.also {
src.copyAndCloseOut(it.newOutputStream())
// Process input file
try {
uri.inputStream().buffered().use { src ->
srcBoot.also {
src.copyAndCloseOut(it.newOutputStream())
}
}
} catch (e: IOException) {
logs.add("Copy boot image error: " + e)
return false
}
} catch (e: IOException) {
logs.add("Copy boot image error: " + e)
return false
}

val execs = listOf("libkptools.so", "libmagiskboot.so", "libbusybox.so")
Expand All @@ -185,49 +189,60 @@ fun patchBootimg(uri: Uri, superKey: String, logs: MutableList<String>): Boolean
}

// Extract scripts
for (script in listOf("boot_patch.sh", "kpimg")) {
for (script in listOf("boot_patch.sh", "util_functions.sh", "kpimg")) {
val dest = File(patchDir, script)
apApp.assets.open(script).writeTo(dest)
}

val apVer = getManagerVersion().second
val rand = (1..4).map { ('a'..'z').random() }.joinToString("")
val outFilename = "apatch_${apVer}_${rand}_boot.img"
val patchCommand = if (uri != null) "sh boot_patch.sh $superKey ${srcBoot?.path}" else "sh boot_patch.sh $superKey"

val cmds = arrayOf(
"cd $patchDir",
"sh boot_patch.sh $superKey ${srcBoot.path}",
patchCommand,
)
shell.newJob().add(*cmds).to(logs, logs).exec()
val isSuccess = shell.newJob().add(*cmds).to(logs, logs).exec().isSuccess
logs.add("****************************")

val newBootFile = patchDir.getChildFile("new-boot.img")
if(!newBootFile.exists()) {
logs.add(" Patch failed, no new-boot.img generated")
return false
}

val outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
if(!outDir.exists()) outDir.mkdirs()
val outPath = File(outDir, outFilename)

val inputUri = UriUtils.getUriForFile(newBootFile)

var succ = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val outUri = MediaStoreUtils.createDownloadUri(outFilename)
succ = MediaStoreUtils.insertDownload(outUri, inputUri)
} else {
newBootFile.inputStream().copyAndClose(outPath.outputStream())
}

if(succ) {
logs.add(" Output file is written to ")
logs.add(" ${outPath.path}")
if (uri == null) {
if (isSuccess) {
logs.add(" Boot patch was successful")
} else {
succ = false
logs.add(" Boot patch failed")
}
} else {
logs.add(" Write patched boot.img failed ")
val newBootFile = patchDir.getChildFile("new-boot.img")
if (newBootFile.exists()) {
val outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
if(!outDir.exists()) outDir.mkdirs()
outPath = File(outDir, outFilename)

val inputUri = UriUtils.getUriForFile(newBootFile)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val outUri = MediaStoreUtils.createDownloadUri(outFilename)
succ = MediaStoreUtils.insertDownload(outUri, inputUri)
} else {
newBootFile.inputStream().copyAndClose(outPath.outputStream())
}

if (succ) {
logs.add(" Write patched boot.img was successful")
logs.add(" Output file is written to ")
logs.add(" ${outPath?.path}")
} else {
logs.add(" Write patched boot.img failed")
}
} else {
succ = false
logs.add(" Patch failed, no new-boot.img generated")
}
}
logs.add("****************************")

return true
return succ
}
Loading