Compare commits

..

2 Commits

Author SHA1 Message Date
Wang Han
30cd19ec9c Fix formatting and update comments in bootimg.cpp 2026-01-17 16:35:41 +08:00
Wang Han
1f1eb01a7d magiskboot: Fix tail offset calculation 2026-01-17 14:38:51 +08:00
20 changed files with 244 additions and 276 deletions

2
app/.gitignore vendored
View File

@@ -3,5 +3,5 @@
# Gradle
.gradle
.kotlin
build
/local.properties
/build

1
app/apk/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -1,7 +1,8 @@
plugins {
id("com.android.application")
kotlin("android")
kotlin("plugin.parcelize")
id("com.android.legacy-kapt")
kotlin("kapt")
id("androidx.navigation.safeargs.kotlin")
}
@@ -25,10 +26,6 @@ android {
isCoreLibraryDesugaringEnabled = true
}
defaultConfig {
proguardFile("proguard-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = true

View File

@@ -1,3 +0,0 @@
# Excessive obfuscation
-flattenpackagehierarchy
-allowaccessmodification

1
app/buildSrc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins {
`kotlin-dsl`
}
@@ -19,7 +21,6 @@ gradlePlugin {
dependencies {
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
implementation(libs.android.gradle.plugin)
implementation(libs.android.kapt.plugin)
implementation(libs.ksp.plugin)
implementation(libs.navigation.safe.args.plugin)
implementation(libs.lsparanoid.plugin)

View File

@@ -46,25 +46,11 @@ class MagiskPlugin : Plugin<Project> {
private fun Project.applyPlugin() {
initRandom(rootProject.file("dict.txt"))
props.clear()
// Get gradle properties relevant to Magisk
props.putAll(properties.filter { (key, _) -> key.startsWith("magisk.") })
// Load config.prop
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
val configPath: String? by this
val configFile = rootFile(configPath ?: "config.prop")
if (configFile.exists()) {
configFile.inputStream().use {
val config = Properties()
config.load(it)
// Remove properties that should be passed by commandline
config.remove("abiList")
props.putAll(config)
}
}
// Commandline override
findProperty("abiList")?.let { props.put("abiList", it) }
val config = rootFile(configPath ?: "config.prop")
if (config.exists())
config.inputStream().use { props.load(it) }
val repo = FileRepository(rootFile(".git"))
val refId = repo.refDatabase.exactRef("HEAD").objectId

View File

@@ -1,68 +1,72 @@
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import org.apache.tools.ant.filters.FixCrLfFilter
import org.gradle.api.Action
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.StopExecutionException
import org.gradle.api.tasks.Sync
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.exclude
import org.gradle.kotlin.dsl.filter
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.ByteArrayOutputStream
import java.io.File
import java.net.URI
import java.security.MessageDigest
import java.util.HexFormat
import java.util.zip.Deflater
import java.util.zip.DeflaterOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
private fun Project.android(configure: Action<CommonExtension>) =
private fun Project.androidBase(configure: Action<BaseExtension>) =
extensions.configure("android", configure)
private fun Project.androidApp(configure: Action<ApplicationExtension>) =
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
extensions.configure("android", configure)
internal val Project.androidApp: ApplicationExtension
get() = extensions["android"] as ApplicationExtension
internal val Project.androidApp: BaseAppModuleExtension
get() = extensions["android"] as BaseAppModuleExtension
private fun Project.androidComponents(configure: Action<AndroidComponentsExtension<*, *, *>>) =
extensions.configure(AndroidComponentsExtension::class.java, configure)
private val Project.androidLib: LibraryExtension
get() = extensions["android"] as LibraryExtension
private val Project.androidComponents: AndroidComponentsExtension<*, *, *>
get() = extensions["androidComponents"] as AndroidComponentsExtension<*, *, *>
internal fun Project.androidAppComponents(configure: Action<ApplicationAndroidComponentsExtension>) =
extensions.configure(ApplicationAndroidComponentsExtension::class.java, configure)
internal val Project.androidComponents
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
fun Project.setupCommon() {
android {
compileSdk {
version = release(36)
}
androidBase {
compileSdkVersion(36)
buildToolsVersion = "36.0.0"
ndkPath = "${androidComponents.sdkComponents.sdkDirectory.get().asFile}/ndk/magisk"
ndkPath = "$sdkDirectory/ndk/magisk"
ndkVersion = "29.0.14206865"
defaultConfig.apply {
defaultConfig {
minSdk = 23
}
compileOptions.apply {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
packaging.apply {
packagingOptions {
resources {
excludes += arrayOf(
"/META-INF/*",
@@ -119,108 +123,93 @@ const val BUSYBOX_DOWNLOAD_URL =
const val BUSYBOX_ZIP_CHECKSUM =
"b4d0551feabaf314e53c79316c980e8f66432e9fb91a69dbbf10a93564b40951"
private abstract class SyncWithDir : Sync() {
@get:OutputDirectory
abstract val outputFolder: DirectoryProperty
}
fun Project.setupCoreLib() {
setupCommon()
val abiList = Config.abiList
androidLib.libraryVariants.all {
val variant = name
val variantCapped = name.replaceFirstChar { it.uppercase() }
val abiList = Config.abiList
androidComponents {
onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val syncLibs = tasks.register("sync${variantCapped}JniLibs", SyncWithDir::class) {
outputFolder.set(layout.buildDirectory.dir("$variantName/jniLibs"))
into(outputFolder)
for (abi in abiList) {
into(abi) {
from(rootFile("native/out/$abi")) {
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
rename { if (it.endsWith(".so")) it else "lib$it.so" }
}
}
}
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
include(abiList.map { "$it/libbusybox.so" })
onlyIf {
if (inputs.sourceFiles.files.size != abiList.size * 6)
throw StopExecutionException("Please build binaries first! (./build.py binary)")
true
}
}
variant.sources.jniLibs?.let {
it.addGeneratedSourceDirectory(syncLibs, SyncWithDir::outputFolder)
}
val syncResources = tasks.register("sync${variantCapped}Resources", SyncWithDir::class) {
outputFolder.set(layout.buildDirectory.dir("$variantName/resources"))
into(outputFolder)
into("META-INF/com/google/android") {
from(rootFile("scripts/update_binary.sh")) {
rename { "update-binary" }
}
from(rootFile("scripts/flash_script.sh")) {
rename { "updater-script" }
val syncLibs = tasks.register("sync${variantCapped}JniLibs", Sync::class) {
into("src/$variant/jniLibs")
for (abi in abiList) {
into(abi) {
from(rootFile("native/out/$abi")) {
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
rename { if (it.endsWith(".so")) it else "lib$it.so" }
}
}
}
variant.sources.resources?.let {
it.addGeneratedSourceDirectory(syncResources, SyncWithDir::outputFolder)
}
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
val syncAssets = tasks.register("sync${variantCapped}Assets", SyncWithDir::class) {
outputFolder.set(layout.buildDirectory.dir("$variantName/assets"))
into(outputFolder)
inputs.property("version", Config.version)
inputs.property("versionCode", Config.versionCode)
from(rootFile("scripts")) {
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
}
from(rootFile("tools/bootctl"))
into("chromeos") {
from(rootFile("tools/futility"))
from(rootFile("tools/keys")) {
include("kernel_data_key.vbprivk", "kernel.keyblock")
}
}
from(stubTask) {
include { it.name.endsWith(".apk") }
rename { "stub.apk" }
}
filesMatching("**/util_functions.sh") {
filter {
it.replace(
"#MAGISK_VERSION_STUB",
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
)
}
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
}
variant.sources.assets?.let {
it.addGeneratedSourceDirectory(syncAssets, SyncWithDir::outputFolder)
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
include(abiList.map { "$it/libbusybox.so" })
onlyIf {
if (inputs.sourceFiles.files.size != abiList.size * 6)
throw StopExecutionException("Please build binaries first! (./build.py binary)")
true
}
}
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(syncLibs)
val syncResources = tasks.register("sync${variantCapped}Resources", Sync::class) {
into("src/$variant/resources/META-INF/com/google/android")
from(rootFile("scripts/update_binary.sh")) {
rename { "update-binary" }
}
from(rootFile("scripts/flash_script.sh")) {
rename { "updater-script" }
}
}
processJavaResourcesProvider.configure { dependsOn(syncResources) }
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
val stubApk = stubTask.outputs.files.asFileTree.filter {
it.name.endsWith(".apk")
}
val syncAssets = tasks.register("sync${variantCapped}Assets", Sync::class) {
dependsOn(stubTask)
inputs.property("version", Config.version)
inputs.property("versionCode", Config.versionCode)
into("src/$variant/assets")
from(rootFile("scripts")) {
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
}
from(rootFile("tools/bootctl"))
into("chromeos") {
from(rootFile("tools/futility"))
from(rootFile("tools/keys")) {
include("kernel_data_key.vbprivk", "kernel.keyblock")
}
}
from(stubApk) {
rename { "stub.apk" }
}
filesMatching("**/util_functions.sh") {
filter {
it.replace(
"#MAGISK_VERSION_STUB",
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
)
}
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
}
mergeAssetsProvider.configure { dependsOn(syncAssets) }
}
tasks.named<Delete>("clean") {
delete.addAll(listOf("src/main/jniLibs", "src/main/resources", "src/debug", "src/release"))
}
}
fun Project.setupAppCommon() {
setupCommon()
androidApp {
android {
signingConfigs {
Config["keyStore"]?.also {
create("config") {
@@ -265,25 +254,22 @@ fun Project.setupAppCommon() {
}
}
androidAppComponents {
onVariants { variant ->
val commentTask = tasks.register(
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
AddCommentTask::class.java
)
val transformationRequest = variant.artifacts.use(commentTask)
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
.toTransformMany(SingleArtifact.APK)
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
commentTask.configure {
this.transformationRequest = transformationRequest
this.signingConfig = signingConfig
this.comment = "version=${Config.version}\n" +
"versionCode=${Config.versionCode}\n" +
"stubVersion=${Config.stubVersion}\n"
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
}
androidComponents.onVariants { variant ->
val commentTask = tasks.register(
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
AddCommentTask::class.java
)
val transformationRequest = variant.artifacts.use(commentTask)
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
.toTransformMany(SingleArtifact.APK)
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
commentTask.configure {
this.transformationRequest = transformationRequest
this.signingConfig = signingConfig
this.comment = "version=${Config.version}\n" +
"versionCode=${Config.versionCode}\n" +
"stubVersion=${Config.stubVersion}\n"
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
}
}
}
@@ -291,7 +277,7 @@ fun Project.setupAppCommon() {
fun Project.setupMainApk() {
setupAppCommon()
androidApp {
android {
namespace = "com.topjohnwu.magisk"
defaultConfig {
@@ -304,10 +290,8 @@ fun Project.setupMainApk() {
debugSymbolLevel = "FULL"
}
}
}
androidComponents {
onVariants { variant ->
androidComponents.onVariants { variant ->
variant.instrumentation.apply {
setAsmFramesComputationMode(COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
transformClassesWith(
@@ -330,26 +314,17 @@ const val SHAMIKO_CHECKSUM =
fun Project.setupTestApk() {
setupAppCommon()
androidComponents {
onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val dlTask = tasks.register("download${variantCapped}Lsposed", SyncWithDir::class) {
outputFolder.set(layout.buildDirectory.dir("$variantName/lsposed"))
into(outputFolder)
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
rename { "lsposed.zip" }
}
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
rename { "shamiko.zip" }
}
androidApp.applicationVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() }
val dlTask by tasks.register("download${variantCapped}Lsposed", Sync::class) {
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
rename { "lsposed.zip" }
}
variant.sources.assets?.let {
it.addGeneratedSourceDirectory(dlTask, SyncWithDir::outputFolder)
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
rename { "shamiko.zip" }
}
into("src/${this@all.name}/assets")
}
mergeAssetsProvider.configure { dependsOn(dlTask) }
}
}

View File

@@ -8,14 +8,13 @@ import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
@@ -83,16 +82,18 @@ private abstract class ManifestUpdater: DefaultTask() {
@get:Input
abstract val applicationId: Property<String>
@get:Input
abstract val factoryClass: Property<String>
@get:Input
abstract val appClass: Property<String>
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val mergedManifest: RegularFileProperty
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val factoryClassDir: DirectoryProperty
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val appClassDir: DirectoryProperty
@get:OutputFile
abstract val outputManifest: RegularFileProperty
@@ -169,18 +170,25 @@ private abstract class ManifestUpdater: DefaultTask() {
// Shuffle the order of the components
cmpList.shuffle(RANDOM)
val (factoryPkg, factoryClass) = factoryClassDir.asFileTree.firstNotNullOf {
it.parentFile!!.name to it.name.removeSuffix(".java")
}
val (appPkg, appClass) = appClassDir.asFileTree.firstNotNullOf {
it.parentFile!!.name to it.name.removeSuffix(".java")
}
val components = cmpList.joinToString("\n\n")
.replace("\${applicationId}", applicationId.get())
val manifest = mergedManifest.asFile.get().readText().replace(Regex(".*\\<application"), """
|<application
| android:appComponentFactory="${factoryClass.get()}"
| android:name="${appClass.get()}"""".ind(1)
| android:appComponentFactory="$factoryPkg.$factoryClass"
| android:name="$appPkg.$appClass"""".ind(1)
).replace(Regex(".*\\<\\/application"), "$components\n </application")
outputManifest.get().asFile.writeText(manifest)
}
}
private fun genStubClasses(outDir: File): Pair<String, String> {
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
val classNameGenerator = sequence {
fun notJavaKeyword(name: String) = when (name) {
"do", "if", "for", "int", "new", "try" -> false
@@ -209,7 +217,7 @@ private fun genStubClasses(outDir: File): Pair<String, String> {
}
}.distinct().iterator()
fun genClass(type: String, outDir: File): String {
fun genClass(type: String, outDir: File) {
val clzName = classNameGenerator.next()
val (pkg, name) = clzName.split('.')
val pkgDir = File(outDir, pkg)
@@ -218,12 +226,10 @@ private fun genStubClasses(outDir: File): Pair<String, String> {
it.println("package $pkg;")
it.println("public class $name extends com.topjohnwu.magisk.$type {}")
}
return clzName
}
val factory = genClass("DelegateComponentFactory", outDir)
val app = genClass("StubApplication", outDir)
return Pair(factory, app)
genClass("DelegateComponentFactory", factoryOutDir)
genClass("StubApplication", appOutDir)
}
private fun genEncryptedResources(res: ByteArray, outDir: File) {
@@ -258,76 +264,74 @@ private fun genEncryptedResources(res: ByteArray, outDir: File) {
}
}
private abstract class TaskWithDir : DefaultTask() {
@get:OutputDirectory
abstract val outputFolder: DirectoryProperty
}
fun Project.setupStubApk() {
setupAppCommon()
androidAppComponents {
onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val variantLowered = variantName.lowercase()
val componentJavaOutDir = layout.buildDirectory
.dir("generated/${variantLowered}/components").get().asFile
val (factory, app) = genStubClasses(componentJavaOutDir)
val manifestUpdater =
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
applicationId = variant.applicationId
factoryClass.set(factory)
appClass.set(app)
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
val aapt = sdkComponents.aapt2.get().executable.get().asFile
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"${variantLowered}/process${variantCapped}Resources/" +
"linked-resources-binary-format-${variantLowered}.ap_").get().asFile
val genResourcesTask = tasks.register("generate${variantCapped}BundledResources", TaskWithDir::class) {
dependsOn("process${variantCapped}Resources")
outputFolder.set(layout.buildDirectory.dir("generated/${variantLowered}/resources"))
doLast {
val apkTmp = File("${apk}.tmp")
providers.exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}.result.get()
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->
ZipOutputStream(apk.outputStream()).use {
it.setLevel(Deflater.BEST_COMPRESSION)
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
it.closeEntry()
}
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
}
}
apkTmp.delete()
genEncryptedResources(bos.toByteArray(), outputFolder.get().asFile)
}
androidComponents.onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val manifestUpdater =
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
dependsOn("generate${variantCapped}ObfuscatedClass")
applicationId = variant.applicationId
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
}
variant.sources.java?.let {
it.addStaticSourceDirectory(componentJavaOutDir.path)
it.addGeneratedSourceDirectory(genResourcesTask, TaskWithDir::outputFolder)
}
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
androidApp.applicationVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() }
val variantLowered = name.lowercase()
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
inputs.property("seed", RAND_SEED)
outputs.dirs(outFactoryClassDir, outAppClassDir)
doLast {
outFactoryClassDir.mkdirs()
outAppClassDir.mkdirs()
genStubClasses(outFactoryClassDir, outAppClassDir)
}
}
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
outputs.dir(outResDir)
doLast {
val apkTmp = File("${apk}.tmp")
providers.exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}.result.get()
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->
ZipOutputStream(apk.outputStream()).use {
it.setLevel(Deflater.BEST_COMPRESSION)
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
it.closeEntry()
}
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
}
}
apkTmp.delete()
genEncryptedResources(bos.toByteArray(), outResDir)
}
}
registerJavaGeneratingTask(processResourcesTask, outResDir)
}
// Override optimizeReleaseResources task
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile

3
app/core/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/build
src/debug
src/release

View File

@@ -1,5 +1,6 @@
plugins {
id("com.android.library")
kotlin("android")
kotlin("plugin.parcelize")
id("dev.zacsweers.moshix")
id("com.google.devtools.ksp")

View File

@@ -33,5 +33,9 @@
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
# Excessive obfuscation
-flattenpackagehierarchy
-allowaccessmodification
-dontwarn org.junit.**
-dontwarn org.apache.**

View File

@@ -24,7 +24,9 @@ org.gradle.caching=true
kapt.use.k2=true
# Android
android.useAndroidX=true
android.injected.testOnly=false
android.nonFinalResIds=false
# Magisk
magisk.stubVersion=40

View File

@@ -1,7 +1,7 @@
[versions]
kotlin = "2.3.0"
android = "9.0.0"
ksp = "2.3.4"
kotlin = "2.2.21"
android = "8.13.2"
ksp = "2.3.3"
rikka = "1.3.0"
navigation = "2.9.6"
libsu = "6.0.0"
@@ -23,7 +23,7 @@ timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "7.1.0.202411261347-r" }
# AndroidX
activity = { module = "androidx.activity:activity", version = "1.12.2" }
activity = { module = "androidx.activity:activity", version = "1.12.1" }
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" }
core-ktx = { module = "androidx.core:core-ktx", version = "1.17.0" }
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.2.0" }
@@ -37,7 +37,7 @@ room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.2.0" }
transition = { module = "androidx.transition:transition", version = "1.7.0" }
transition = { module = "androidx.transition:transition", version = "1.6.0" }
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.5.0" }
material = { module = "com.google.android.material:material", version = "1.13.0" }
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.5" }
@@ -59,10 +59,9 @@ rikka-insets = { module = "dev.rikka.rikkax.insets:insets", version.ref = "rikka
# Build plugins
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "android" }
android-kapt-plugin = { module = "com.android.legacy-kapt:com.android.legacy-kapt.gradle.plugin", version.ref = "android" }
ksp-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
navigation-safe-args-plugin = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "navigation" }
lsparanoid-plugin = { module = "org.lsposed.lsparanoid:gradle-plugin", version = "0.6.0" }
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.34.2" }
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.34.1" }
[plugins]

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

1
app/shared/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -6,5 +6,4 @@ setupCommon()
android {
namespace = "com.topjohnwu.shared"
enableKotlin = false
}

View File

@@ -1,5 +1,6 @@
plugins {
id("com.android.application")
kotlin("android")
}
android {

View File

@@ -406,7 +406,6 @@ def build_apk(module: str):
gradlew,
f"{module}:assemble{build_type}",
f"-PconfigPath={props}",
f"-PabiList={','.join(build_abis.keys())}",
],
env=env,
)

View File

@@ -292,7 +292,7 @@ static int find_dtb_offset(const uint8_t *buf, unsigned sz) {
auto fdt_hdr = reinterpret_cast<const fdt_header *>(curr);
// Check that fdt_header.totalsize does not overflow kernel image size or is empty dtb
// https://github.com/torvalds/linux/commit/7b937cc243e5b1df8780a0aa743ce800df6c68d1
// https://github.com/torvalds/linux/commit/7b937cc243e5b1df8780a0aa743ce800df6c68d1
uint32_t totalsize = fdt_hdr->totalsize;
if (totalsize > end - curr || totalsize <= 0x48)
continue;
@@ -611,9 +611,7 @@ bool boot_img::parse_image(const uint8_t *addr, FileFormat type) {
int split_image_dtb(Utf8CStr filename, bool skip_decomp) {
mmap_data img(filename.c_str());
if (int offset = find_dtb_offset(img.data(), img.size()); offset > 0) {
size_t off = (size_t) offset;
if (size_t off = find_dtb_offset(img.data(), img.size()); off > 0) {
FileFormat fmt = check_fmt_lg(img.data(), img.size());
if (!skip_decomp && fmt_compressed(fmt)) {
int fd = creat(KERNEL_FILE, 0644);
@@ -915,8 +913,6 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
file_align();
}
off.tail = lseek(fd, 0, SEEK_CUR);
// Proprietary stuffs
if (boot.flags[SEANDROID_FLAG]) {
xwrite(fd, SEANDROID_MAGIC, 16);
@@ -927,6 +923,7 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
xwrite(fd, LG_BUMP_MAGIC, 16);
}
off.tail = lseek(fd, 0, SEEK_CUR);
file_align();
// vbmeta