diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 019534a39716..8f425a01506c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -44,6 +44,7 @@ android { packagingOptions { resources { excludes += "/META-INF/*" + excludes += "/META-INF/versions/**" excludes += "/org/bouncycastle/**" excludes += "/kotlin/**" excludes += "/kotlinx/**" @@ -72,9 +73,9 @@ dependencies { implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7") implementation("com.github.topjohnwu:lz4-java:1.7.1") implementation("com.jakewharton.timber:timber:5.0.1") - implementation("org.bouncycastle:bcpkix-jdk18on:1.71") + implementation("org.bouncycastle:bcpkix-jdk18on:1.72") implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0") - implementation("dev.rikka.rikkax.insets:insets:1.2.0") + implementation("dev.rikka.rikkax.insets:insets:1.3.0") implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1") implementation("io.noties.markwon:core:4.6.2") @@ -93,11 +94,11 @@ dependencies { implementation("com.squareup.okhttp3:logging-interceptor:${vOkHttp}") implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:${vOkHttp}") - val vMoshi = "1.13.0" + val vMoshi = "1.14.0" implementation("com.squareup.moshi:moshi:${vMoshi}") kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}") - val vRoom = "2.5.0-alpha03" + val vRoom = "2.5.0-rc01" implementation("androidx.room:room-runtime:${vRoom}") implementation("androidx.room:room-ktx:${vRoom}") kapt("androidx.room:room-compiler:${vRoom}") @@ -111,7 +112,7 @@ dependencies { implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.appcompat:appcompat:1.5.1") implementation("androidx.recyclerview:recyclerview:1.2.1") - implementation("androidx.fragment:fragment-ktx:1.5.4") + implementation("androidx.fragment:fragment-ktx:1.5.5") implementation("androidx.transition:transition:1.4.1") implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.core:core-splashscreen:1.0.0") diff --git a/app/shared/src/main/AndroidManifest.xml b/app/shared/src/main/AndroidManifest.xml index 9d351b52fc04..0edea0fff5f6 100644 --- a/app/shared/src/main/AndroidManifest.xml +++ b/app/shared/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a7b4c0276661..0242a1d5bd82 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,13 +2,22 @@ + + + + + tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" + tools:remove="android:appComponentFactory"> = Build.VERSION_CODES.M) { + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + } else { + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + } + manager.registerNetworkCallback(builder.build(), networkCallback) } @Suppress("DEPRECATION") diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt index 8f6eebab1668..62b5baf3b3e8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.topjohnwu.magisk.core.utils.net import android.annotation.TargetApi @@ -32,7 +30,7 @@ class MarshmallowNetworkObserver( override fun getCurrentState() { callback(manager.getNetworkCapabilities(manager.activeNetwork) - ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false) + ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false) } private inner class IdleBroadcastReceiver: BroadcastReceiver() { diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml new file mode 100644 index 000000000000..528440c1b8ba --- /dev/null +++ b/app/src/main/res/values-sw/strings.xml @@ -0,0 +1,238 @@ + + + + Moduli + Mtumiaji mkuu + Kumbukumbu + Mipangilio + Sakinisha + Nyumbanik + Mandhari + DenyList + + + Hakuna muunganisho unaopatikana + Kumbukumbu + Loading… + Sasisha + Haipatikani + Ficha + Kifurushi + Programu + + Download Magisk ONLY from the official GitHub page. Files from unknown sources can be malicious! + Support Us + Source + Magisk is, and always will be, free, and open source. You can however show us that you care by making a donation. + Installed + Latest + Invalid Update Channel + Uninstall Magisk + All modules will be disabled/removed!\nRoot will be removed!\nAny internal storage unencrypted through the use of Magisk will be re-encrypted! + + + Preserve force encryption + Preserve AVB 2.0/dm-verity + Patch vbmeta in boot image + Recovery Mode + Options + Method + Next + Let\'s go + Press to download and install + Direct Install (Recommended) + Install to Inactive Slot (After OTA) + Your device will be FORCED to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue? + Additional Setup + Select and Patch a File + Select a raw image (*.img) or an ODIN tarfile (*.tar) + Rebooting in 5 seconds… + Installation + + + Superuser Request + Because an app is obscuring a Superuser request, Magisk can\'t verify your response + Deny + Prompt + Grant + Grants full access to your device.\nDeny if you\'re not sure! + Forever + Once + 10 mins + 20 mins + 30 mins + 60 mins + %1$s was granted Superuser rights + %1$s was denied Superuser rights + Superuser rights of %1$s are granted + Superuser rights of %1$s are denied + Notifications of %1$s are enabled + Notifications of %1$s are disabled + Logging of %1$s is enabled + Logging of %1$s is disabled + Revoke? + Confirm to revoke %1$s Superuser rights + Toast + None + + Notifications + Revoke + No apps have asked for Superuser permission yet. + + + You\'re log-free, try using your root apps more + Magisk logs are empty, that\'s weird + Save log + Clear log now + Log successfully cleared + PID: %1$d + Target UID: %1$d + + + + + Show system apps + Show OS apps + Filter by name + Search + + + (No info provided) + Soft reboot + Reboot to Recovery + Reboot to Bootloader + Reboot to Download + Reboot to EDL + %1$s by %2$s + Remove + Restore + Install from storage + Update Available + Module suspended because %1$s is enabled + Module suspended because %1$s is not enabled + Zygisk module not loaded due to incompatibility + + + Theme Mode + Select mode which best suits your style! + Always Light + Follow System + Always Dark + Download path + Files will be saved to %1$s + Hide the Magisk app + Install a proxy app with a random package ID and custom app label + Restore the Magisk app + Unhide the app and restore the original APK + Language + (System Default) + Check Updates + Periodically check for updates in the background + Update Channel + Stable + Beta + Custom + Insert a custom channel URL + Run parts of Magisk in the zygote daemon + Enforce DenyList + Processes on the denylist will have all Magisk modifications reverted + This feature requires %1$s to be enabled + Configure DenyList + Select the processes to be included on the denylist + Systemless hosts + Systemless hosts support for ad blocking apps + Added systemless hosts module + New name + App will be repackaged with this name + Invalid format + Apps and ADB + Apps only + ADB only + Disabled + 10 seconds + 15 seconds + 20 seconds + 30 seconds + 45 seconds + 60 seconds + Superuser Access + Automatic Response + Request Timeout + Superuser Notification + Reauthenticate after upgrade + Ask for Superuser permissions again after upgrading apps + Tapjacking Protection + The Superuser prompt dialog will not respond to input while obscured by any other window or overlay + Biometric Authentication + Use biometric authentication to allow Superuser requests + Unsupported device or no biometric settings are enabled + Customization + Add a pretty shortcut to the home screen in case the name and icon are difficult to recognize after hiding the app + DNS over HTTPS + Workaround DNS poisoning in some nations + + Multiuser Mode + Device Owner Only + Device Owner Managed + User-Independent + Only owner has root access + Only owner can manage root access and receive request prompts + Each user has their own separate root rules + + Mount Namespace Mode + Global Namespace + Inherit Namespace + Isolated Namespace + All root sessions use the global mount namespace + Root sessions will inherit their requester\'s namespace + Each root session will have its own isolated namespace + + + Magisk Updates + Progress Notifications + Update Complete + Download complete + Error downloading file + Magisk Update Available! + Magisk Updated + Tap to open app + + + Yes + No + Install %1$s %2$s(%3$d) + Download + Reboot + Release notes + Flashing… + Done! + Failed! + Hiding the Magisk app… + No app found to open the link + Complete Uninstall + Restore Images + Restoring… + Restoration done! + Stock backup does not exist! + Setup failed + Requires Additional Setup + Your device needs additional setup for Magisk to work properly. Do you want to proceed and reboot? + Running environment setup… + Authenticate + Unsupported Magisk Version + This version of the app does not support Magisk versions lower than %1$s.\n\nThe app will behave as if no Magisk is installed, please upgrade Magisk as soon as possible. + Abnormal State + Running this app as a system app is not supported. Please revert the app to a user app. + A \"su\" binary not from Magisk has been detected. Please remove any competing root solution and/or reinstall Magisk. + Magisk is installed to external storage. Please move the app to internal storage. + The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK. + @string/settings_restore_app_title + Grant storage permission to enable this functionality + Allow "install unknown apps" to enable this functionality + Add shortcut to home screen + After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen? + No app found to handle this action + Reboot to apply changes + This will restore the hidden app back to the original app. Do you really want to do this? + + diff --git a/build.py b/build.py index bc174d71160e..d88b11834590 100755 --- a/build.py +++ b/build.py @@ -48,8 +48,8 @@ def vprint(str): no_color = True # Environment checks -if not sys.version_info >= (3, 6): - error('Requires Python 3.6+') +if not sys.version_info >= (3, 8): + error('Requires Python 3.8+') if 'ANDROID_SDK_ROOT' not in os.environ: error('Please add Android SDK path to ANDROID_SDK_ROOT environment variable!') @@ -309,13 +309,7 @@ def binary_dump(src, var_name, compressor=xz): def dump_bin_header(args): - stub = op.join(config['outdir'], f'stub-{"release" if args.release else "debug"}.apk') - if not op.exists(stub): - error('Build stub APK before building "magiskinit"') mkdir_p(native_gen_path) - with open(stub, 'rb') as src: - text = binary_dump(src, 'manager_xz') - write_if_diff(op.join(native_gen_path, 'binaries.h'), text) for arch in archs: preload = op.join('native', 'out', arch, 'libinit-ld.so') with open(preload, 'rb') as src: @@ -431,6 +425,14 @@ def build_app(args): header('* Building the Magisk app') build_apk(args, 'app') + # Stub building is directly integrated into the main app + # build process. Copy the stub APK into output directory. + build_type = 'release' if args.release else 'debug' + apk = f'stub-{build_type}.apk' + source = op.join('app', 'src', 'main', 'assets', 'stub.apk') + target = op.join(config['outdir'], apk) + cp(source, target) + def build_stub(args): header('* Building the stub app') @@ -455,6 +457,7 @@ def cleanup(args): if 'java' in args.target: header('* Cleaning java') execv([gradlew, 'app:clean', 'app:shared:clean', 'stub:clean']) + rm_rf(op.join('app', 'src', 'main', 'assets')) def setup_ndk(args): @@ -554,7 +557,6 @@ def patch_avd_ramdisk(args): def build_all(args): - build_stub(args) build_binary(args) build_app(args) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a2ec46cbc682..6a46db98b725 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,9 +24,9 @@ tasks.withType { } dependencies { - implementation(kotlin("gradle-plugin", "1.7.10")) + implementation(kotlin("gradle-plugin", "1.7.21")) implementation("com.android.tools.build:gradle:7.3.1") implementation("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3") implementation("io.michaelrocks:paranoid-gradle-plugin:0.3.7") - implementation("org.eclipse.jgit:org.eclipse.jgit:6.2.0.202206071550-r") + implementation("org.eclipse.jgit:org.eclipse.jgit:6.4.0.202211300538-r") } diff --git a/buildSrc/src/main/java/Setup.kt b/buildSrc/src/main/java/Setup.kt index c579e57bfd3d..4eb49ffe6824 100644 --- a/buildSrc/src/main/java/Setup.kt +++ b/buildSrc/src/main/java/Setup.kt @@ -15,8 +15,7 @@ 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.filter -import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.* import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -40,12 +39,12 @@ private fun BaseExtension.kotlinOptions(configure: Action) = } private val Project.android: BaseAppModuleExtension - get() = extensions.getByName("android") as BaseAppModuleExtension + get() = extensions["android"] as BaseAppModuleExtension fun Project.setupCommon() { androidBase { compileSdkVersion(33) - buildToolsVersion = "32.0.0" + buildToolsVersion = "33.0.1" ndkPath = "$sdkDirectory/ndk/magisk" defaultConfig { @@ -112,14 +111,14 @@ private fun Project.setupAppCommon() { } buildTypes { - signingConfigs.getByName("config").also { - getByName("debug") { + signingConfigs["config"].also { + debug { signingConfig = if (it.storeFile?.exists() == true) it - else signingConfigs.getByName("debug") + else signingConfigs["debug"] } - getByName("release") { + release { signingConfig = if (it.storeFile?.exists() == true) it - else signingConfigs.getByName("debug") + else signingConfigs["debug"] } } } @@ -147,7 +146,7 @@ private fun Project.setupAppCommon() { fun Project.setupApp() { setupAppCommon() - val syncLibs = tasks.register("syncLibs", Sync::class.java) { + val syncLibs by tasks.registering(Sync::class) { into("src/main/jniLibs") into("armeabi-v7a") { from(rootProject.file("native/out/armeabi-v7a")) { @@ -180,35 +179,8 @@ fun Project.setupApp() { } } - val syncAssets = tasks.register("syncAssets", Sync::class.java) { + val syncResources by tasks.registering(Sync::class) { dependsOn(syncLibs) - 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", "addon.d.sh") - include("uninstaller.sh", "module_installer.sh") - } - from(rootProject.file("tools/bootctl")) - 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}'\nMAGISK_VER_CODE=${Config.versionCode}" - ) - } - filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) - } - } - - val syncResources = tasks.register("syncResources", Sync::class.java) { - dependsOn(syncAssets) into("src/main/resources/META-INF/com/google/android") from(rootProject.file("scripts/update_binary.sh")) { rename { "update-binary" } @@ -220,22 +192,43 @@ fun Project.setupApp() { android.applicationVariants.all { val variantCapped = name.capitalize(Locale.ROOT) - val variantLowered = name.toLowerCase(Locale.ROOT) - val copyStub = tasks.register("copy${variantCapped}StubApk", Copy::class.java) { - dependsOn(syncResources) + val stubTask = tasks.getByPath(":stub:package$variantCapped") + val stubApk = stubTask.outputs.files.asFileTree.filter { + it.name.endsWith(".apk") + } + + val syncAssets = tasks.register("sync${variantCapped}Assets", Sync::class) { + dependsOn(syncResources, stubTask) + inputs.property("version", Config.version) + inputs.property("versionCode", Config.versionCode) into("src/main/assets") - from(rootProject.file("out/stub-${variantLowered}.apk")) { + from(rootProject.file("scripts")) { + include("util_functions.sh", "boot_patch.sh", "addon.d.sh") + include("uninstaller.sh", "module_installer.sh") + } + from(rootProject.file("tools/bootctl")) + into("chromeos") { + from(rootProject.file("tools/futility")) + from(rootProject.file("tools/keys")) { + include("kernel_data_key.vbprivk", "kernel.keyblock") + } + } + from(stubApk) { rename { "stub.apk" } } - onlyIf { - if (inputs.sourceFiles.files.size != 1) - throw StopExecutionException("Please build stub first! (./build.py stub)") - true + filesMatching("**/util_functions.sh") { + filter { + it.replace( + "#MAGISK_VERSION_STUB", + "MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}" + ) + } + filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) } } - preBuildProvider.get().dependsOn(copyStub) + preBuildProvider.get().dependsOn(syncAssets) val keysDir = rootProject.file("tools/keys") val outSrcDir = File(buildDir, "generated/source/keydata/$name") diff --git a/docs/guides.md b/docs/guides.md index d814f84b2c40..b13134a849e1 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -257,7 +257,7 @@ Since `/` is read-only on system-as-root devices, Magisk provides an overlay sys Overlay files shall be placed in the `overlay.d` folder in boot image ramdisk, and they follow these rules: -1. All `*.rc` files in `overlay.d` will be read and concatenated **AFTER** `init.rc` +1. Each `*.rc` file (except for `init.rc`) in `overlay.d` will be read and concatenated **AFTER** `init.rc` if it does not exist in the root directory, otherwise it will **REPLACE** the existing one. 2. Existing files can be replaced by files located at the same relative path 3. Files that correspond to a non-existing file will be ignored diff --git a/gradle.properties b/gradle.properties index 99d48d9c0fd5..36be6597e2b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,5 +28,5 @@ android.nonTransitiveRClass=true # Magisk magisk.stubVersion=34 -magisk.versionCode=25205 +magisk.versionCode=25206 magisk.ondkVersion=r25.2 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832f090..943f0cbfa754 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8fad3f5a98bf..2b22d057a071 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6c206..65dcd68d65c8 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac diff --git a/gradlew.bat b/gradlew.bat index f127cfd49d40..93e3f59f135d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/native/src/base/include/embed.hpp b/native/src/base/include/embed.hpp index 41250240e1e9..c34394a5572a 100644 --- a/native/src/base/include/embed.hpp +++ b/native/src/base/include/embed.hpp @@ -1,5 +1,3 @@ -#include - #if defined(__arm__) #include #elif defined(__aarch64__) diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index 8908a0750f5c..3a391aebe68d 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -17,7 +17,17 @@ using namespace std; -static bool safe_mode = false; +// Boot stage state +enum : int { + FLAG_NONE = 0, + FLAG_POST_FS_DATA_DONE = (1 << 0), + FLAG_LATE_START_DONE = (1 << 1), + FLAG_BOOT_COMPLETE = (1 << 2), + FLAG_SAFE_MODE = (1 << 3), +}; + +static int boot_state = FLAG_NONE; + bool zygisk_enabled = false; /********* @@ -272,21 +282,15 @@ static bool check_key_combo() { * Boot Stage Handlers * ***********************/ -static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER; extern int disable_deny(); -void post_fs_data(int client) { - close(client); - - mutex_guard lock(stage_lock); - +static void post_fs_data() { if (getenv("REMOUNT_ROOT")) xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr); if (!check_data()) - goto unblock_init; + return; - DAEMON_STATE = STATE_POST_FS_DATA; setup_logfile(true); LOGI("** post-fs-data mode running\n"); @@ -313,8 +317,9 @@ void post_fs_data(int client) { goto early_abort; } - if (getprop("persist.sys.safemode", true) == "1" || check_key_combo()) { - safe_mode = true; + if (getprop("persist.sys.safemode", true) == "1" || + getprop("ro.sys.safemode") == "1" || check_key_combo()) { + boot_state |= FLAG_SAFE_MODE; // Disable all modules and denylist so next boot will be clean disable_modules(); disable_deny(); @@ -330,40 +335,26 @@ void post_fs_data(int client) { early_abort: // We still do magic mount because root itself might need it magic_mount(); - DAEMON_STATE = STATE_POST_FS_DATA_DONE; - -unblock_init: - close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0)); + boot_state |= FLAG_POST_FS_DATA_DONE; } -void late_start(int client) { - close(client); - - mutex_guard lock(stage_lock); - run_finally fin([]{ DAEMON_STATE = STATE_LATE_START_DONE; }); +static void late_start() { setup_logfile(false); LOGI("** late_start service mode running\n"); - if (DAEMON_STATE < STATE_POST_FS_DATA_DONE || safe_mode) - return; - exec_common_scripts("service"); exec_module_scripts("service"); -} -void boot_complete(int client) { - close(client); + boot_state |= FLAG_LATE_START_DONE; +} - mutex_guard lock(stage_lock); - DAEMON_STATE = STATE_BOOT_COMPLETE; +static void boot_complete() { + boot_state |= FLAG_BOOT_COMPLETE; setup_logfile(false); LOGI("** boot-complete triggered\n"); - if (safe_mode) - return; - // At this point it's safe to create the folder if (access(SECURE_DIR, F_OK) != 0) xmkdir(SECURE_DIR, 0700); @@ -373,10 +364,26 @@ void boot_complete(int client) { get_manager(0, nullptr, true); } -void zygote_restart(int client) { - close(client); +void boot_stage_handler(int code) { + // Make sure boot stage execution is always serialized + static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER; + mutex_guard lock(stage_lock); - LOGI("** zygote restarted\n"); - pkg_xml_ino = 0; - prune_su_access(); + switch (code) { + case MainRequest::POST_FS_DATA: + if ((boot_state & FLAG_POST_FS_DATA_DONE) == 0) + post_fs_data(); + close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0)); + break; + case MainRequest::LATE_START: + if ((boot_state & FLAG_POST_FS_DATA_DONE) && (boot_state & FLAG_SAFE_MODE) == 0) + late_start(); + break; + case MainRequest::BOOT_COMPLETE: + if ((boot_state & FLAG_SAFE_MODE) == 0) + boot_complete(); + break; + default: + __builtin_unreachable(); + } } diff --git a/native/src/core/core.hpp b/native/src/core/core.hpp index 598d953398eb..efa7c9ee11f9 100644 --- a/native/src/core/core.hpp +++ b/native/src/core/core.hpp @@ -4,18 +4,8 @@ #include extern bool RECOVERY_MODE; -extern int DAEMON_STATE; extern std::atomic pkg_xml_ino; -// Daemon state -enum : int { - STATE_NONE, - STATE_POST_FS_DATA, - STATE_POST_FS_DATA_DONE, - STATE_LATE_START_DONE, - STATE_BOOT_COMPLETE -}; - void unlock_blocks(); void reboot(); void start_log_daemon(); diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index b30a73650805..f5626a9b7f7f 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -21,7 +21,6 @@ int SDK_INT = -1; string MAGISKTMP; bool RECOVERY_MODE = false; -int DAEMON_STATE = STATE_NONE; static struct stat self_st; @@ -143,17 +142,11 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { case MainRequest::SUPERUSER: su_daemon_handler(client, &cred); break; - case MainRequest::POST_FS_DATA: - post_fs_data(client); - break; - case MainRequest::LATE_START: - late_start(client); - break; - case MainRequest::BOOT_COMPLETE: - boot_complete(client); - break; case MainRequest::ZYGOTE_RESTART: - zygote_restart(client); + close(client); + LOGI("** zygote restarted\n"); + pkg_xml_ino = 0; + prune_su_access(); break; case MainRequest::SQLITE_CMD: exec_sql(client); @@ -232,7 +225,9 @@ static void handle_request(pollfd *pfd) { } code = read_int(client); - if (code < 0 || code >= MainRequest::END || code == MainRequest::_SYNC_BARRIER_) { + if (code < 0 || code >= MainRequest::END || + code == MainRequest::_SYNC_BARRIER_ || + code == MainRequest::_STAGE_BARRIER_) { // Unknown request code goto done; } @@ -274,10 +269,12 @@ static void handle_request(pollfd *pfd) { if (code < MainRequest::_SYNC_BARRIER_) { handle_request_sync(client, code); goto done; + } else if (code < MainRequest::_STAGE_BARRIER_) { + exec_task([=] { handle_request_async(client, code, cred); }); + } else { + close(client); + exec_task([=] { boot_stage_handler(code); }); } - - // Handle async requests in another thread - exec_task([=] { handle_request_async(client, code, cred); }); return; done: diff --git a/native/src/core/module.cpp b/native/src/core/module.cpp index 6967cf51f702..330b26fa8a23 100644 --- a/native/src/core/module.cpp +++ b/native/src/core/module.cpp @@ -117,7 +117,7 @@ class dir_node : public node_entry { // Return false to indicate need to upgrade to module bool collect_files(const char *module, int dfd); - // Return false to indicate need to upgrade to skeleton + // Return true to indicate need to upgrade to skeleton bool prepare(); // Default directory mount logic @@ -422,14 +422,14 @@ bool dir_node::prepare() { } } } - if (auto dn = dyn_cast(it->second); dn && dn->is_dir() && !dn->prepare()) { + if (auto dn = dyn_cast(it->second); dn && dn->is_dir() && dn->prepare()) { // Upgrade child to tmpfs it = upgrade(it); } next_node: ++it; } - return !to_tmpfs; + return to_tmpfs; } bool dir_node::collect_files(const char *module, int dfd) { diff --git a/native/src/include/daemon.hpp b/native/src/include/daemon.hpp index 6c5b436e3617..b2d812714204 100644 --- a/native/src/include/daemon.hpp +++ b/native/src/include/daemon.hpp @@ -31,15 +31,19 @@ enum : int { _SYNC_BARRIER_, SUPERUSER, - POST_FS_DATA, - LATE_START, - BOOT_COMPLETE, ZYGOTE_RESTART, DENYLIST, SQLITE_CMD, REMOVE_MODULES, ZYGISK, ZYGISK_PASSTHROUGH, + + _STAGE_BARRIER_, + + POST_FS_DATA, + LATE_START, + BOOT_COMPLETE, + END, }; } @@ -84,10 +88,7 @@ extern std::atomic logd_fd; extern "C" void magisk_log_write(int prio, const char *msg, int len); // Daemon handlers -void post_fs_data(int client); -void late_start(int client); -void boot_complete(int client); -void zygote_restart(int client); +void boot_stage_handler(int code); void denylist_handler(int client, const sock_cred *cred); void su_daemon_handler(int client, const sock_cred *cred); void zygisk_handler(int client, const sock_cred *cred); diff --git a/native/src/init/init.cpp b/native/src/init/init.cpp index 33f59bcec540..8417bd67e847 100644 --- a/native/src/init/init.cpp +++ b/native/src/init/init.cpp @@ -61,10 +61,6 @@ void restore_ramdisk_init() { } } -int dump_manager(const char *path, mode_t mode) { - return dump_bin(manager_xz, sizeof(manager_xz), path, mode); -} - int dump_preload(const char *path, mode_t mode) { return dump_bin(init_ld_xz, sizeof(init_ld_xz), path, mode); } @@ -87,12 +83,6 @@ int main(int argc, char *argv[]) { if (name == "magisk"sv) return magisk_proxy_main(argc, argv); - if (argc > 1 && argv[1] == "-x"sv) { - if (argc > 2 && argv[2] == "manager"sv) - return dump_manager(argv[3], 0644); - return 1; - } - if (getpid() != 1) return 1; diff --git a/native/src/init/init.hpp b/native/src/init/init.hpp index 1e1125488093..3d9c95bc0917 100644 --- a/native/src/init/init.hpp +++ b/native/src/init/init.hpp @@ -36,7 +36,6 @@ bool check_two_stage(); void setup_klog(); const char *backup_init(); void restore_ramdisk_init(); -int dump_manager(const char *path, mode_t mode); int dump_preload(const char *path, mode_t mode); /*************** diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index 65e3e02011b3..bb495777c419 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -68,8 +68,18 @@ static void load_overlay_rc(const char *overlay) { int dfd = dirfd(dir.get()); // Do not allow overwrite init.rc unlinkat(dfd, "init.rc", 0); + + // '/' + name + '\0' + char buf[NAME_MAX + 2]; + buf[0] = '/'; for (dirent *entry; (entry = xreaddir(dir.get()));) { - if (str_ends(entry->d_name, ".rc")) { + if (!str_ends(entry->d_name, ".rc")) { + continue; + } + strscpy(buf + 1, entry->d_name, sizeof(buf) - 1); + if (access(buf, F_OK) == 0) { + LOGD("Replace rc script [%s]\n", entry->d_name); + } else { LOGD("Found rc script [%s]\n", entry->d_name); int rc = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC); rc_list.push_back(full_read(rc)); @@ -141,6 +151,7 @@ static void patch_socket_name(const char *path) { static void extract_files(bool sbin) { const char *m32 = sbin ? "/sbin/magisk32.xz" : "magisk32.xz"; const char *m64 = sbin ? "/sbin/magisk64.xz" : "magisk64.xz"; + const char *stub_xz = sbin ? "/sbin/stub.xz" : "stub.xz"; if (access(m32, F_OK) == 0) { auto magisk = mmap_data(m32); @@ -161,8 +172,13 @@ static void extract_files(bool sbin) { } else { xsymlink("./magisk32", "magisk"); } - - dump_manager("stub.apk", 0); + if (access(stub_xz, F_OK) == 0) { + auto stub = mmap_data(stub_xz); + unlink(stub_xz); + int fd = xopen("stub.apk", O_WRONLY | O_CREAT, 0); + unxz(fd, stub.buf, stub.sz); + close(fd); + } } #define ROOTMIR MIRRDIR "/system_root" diff --git a/native/src/init/twostage.cpp b/native/src/init/twostage.cpp index 64a6c28cee10..e7fd1ee3123c 100644 --- a/native/src/init/twostage.cpp +++ b/native/src/init/twostage.cpp @@ -26,6 +26,7 @@ void LegacySARInit::first_stage_prep() { write(dest, init.buf, init.sz); fclone_attr(src, dest); close(dest); + close(src); } xmount("/data/init", "/init", nullptr, MS_BIND, nullptr); } diff --git a/scripts/avd_magisk.sh b/scripts/avd_magisk.sh index d8b65471ae02..9ea7f86313cf 100755 --- a/scripts/avd_magisk.sh +++ b/scripts/avd_magisk.sh @@ -48,7 +48,7 @@ fi pm install -r $(pwd)/magisk.apk # Extract files from APK -unzip -oj magisk.apk 'assets/util_functions.sh' +unzip -oj magisk.apk 'assets/util_functions.sh' 'assets/stub.apk' . ./util_functions.sh api_level_arch_detect @@ -124,7 +124,7 @@ mkdir $NVBASE/modules 2>/dev/null mkdir $POSTFSDATAD 2>/dev/null mkdir $SERVICED 2>/dev/null -for file in magisk32 magisk64 magiskpolicy; do +for file in magisk32 magisk64 magiskpolicy stub.apk; do chmod 755 ./$file cp -af ./$file $MAGISKTMP/$file cp -af ./$file $MAGISKBIN/$file @@ -143,8 +143,6 @@ ln -s ./magisk $MAGISKTMP/resetprop ln -s ./magisk $MAGISKTMP/magiskhide ln -s ./magiskpolicy $MAGISKTMP/supolicy -./magiskinit -x manager $MAGISKTMP/stub.apk - mkdir -p $MAGISKTMP/.magisk/mirror mkdir $MAGISKTMP/.magisk/block touch $MAGISKTMP/.magisk/config diff --git a/scripts/avd_patch.sh b/scripts/avd_patch.sh index e787953ca0f8..268b4decb5f9 100644 --- a/scripts/avd_patch.sh +++ b/scripts/avd_patch.sh @@ -43,7 +43,7 @@ if [ -z "$FIRST_STAGE" ]; then fi # Extract files from APK -unzip -oj magisk.apk 'assets/util_functions.sh' +unzip -oj magisk.apk 'assets/util_functions.sh' 'assets/stub.apk' . ./util_functions.sh api_level_arch_detect @@ -65,6 +65,7 @@ touch config ./magiskboot compress=xz magisk32 magisk32.xz ./magiskboot compress=xz magisk64 magisk64.xz +./magiskboot compress=xz stub.apk stub.xz export KEEPVERITY=false export KEEPFORCEENCRYPT=true @@ -75,10 +76,11 @@ export KEEPFORCEENCRYPT=true "mkdir 0750 overlay.d/sbin" \ "add 0644 overlay.d/sbin/magisk32.xz magisk32.xz" \ "add 0644 overlay.d/sbin/magisk64.xz magisk64.xz" \ +"add 0644 overlay.d/sbin/stub.xz stub.xz" \ "patch" \ "backup ramdisk.cpio.orig" \ "mkdir 000 .backup" \ "add 000 .backup/.magisk config" -rm -f ramdisk.cpio.orig config magisk*.xz +rm -f ramdisk.cpio.orig config magisk*.xz stub.xz ./magiskboot compress=gzip ramdisk.cpio ramdisk.cpio.gz diff --git a/scripts/boot_patch.sh b/scripts/boot_patch.sh index c334e8d84dc6..068097c38e45 100644 --- a/scripts/boot_patch.sh +++ b/scripts/boot_patch.sh @@ -6,7 +6,7 @@ # Usage: boot_patch.sh # # The following flags can be set in environment variables: -# KEEPVERITY, KEEPFORCEENCRYPT, RECOVERYMODE +# KEEPVERITY, KEEPFORCEENCRYPT, PATCHVBMETAFLAG, RECOVERYMODE # # This script should be placed in a directory with the following files: # @@ -14,12 +14,13 @@ # # boot_patch.sh script A script to patch boot image for Magisk. # (this file) The script will use files in its same -# directory to complete the patching process +# directory to complete the patching process. # util_functions.sh script A script which hosts all functions required -# for this script to work properly -# magiskinit binary The binary to replace /init -# magisk(32/64) binary The magisk binaries -# magiskboot binary A tool to manipulate boot images +# for this script to work properly. +# magiskinit binary The binary to replace /init. +# magisk(32/64) binary The magisk binaries. +# magiskboot binary A tool to manipulate boot images. +# stub.apk binary The stub Magisk app to embed into ramdisk. # chromeos folder This folder includes the utility and keys to sign # (optional) chromeos boot images. Only used for Pixel C. # @@ -164,6 +165,7 @@ if [ -f magisk64 ]; then ./magiskboot compress=xz magisk64 magisk64.xz unset SKIP64 fi +./magiskboot compress=xz stub.apk stub.xz ./magiskboot cpio ramdisk.cpio \ "add 0750 $INIT magiskinit" \ @@ -171,12 +173,13 @@ fi "mkdir 0750 overlay.d/sbin" \ "$SKIP32 add 0644 overlay.d/sbin/magisk32.xz magisk32.xz" \ "$SKIP64 add 0644 overlay.d/sbin/magisk64.xz magisk64.xz" \ +"add 0644 overlay.d/sbin/stub.xz stub.xz" \ "patch" \ "backup ramdisk.cpio.orig" \ "mkdir 000 .backup" \ "add 000 .backup/.magisk config" -rm -f ramdisk.cpio.orig config magisk*.xz +rm -f ramdisk.cpio.orig config magisk*.xz stub.xz ################# # Binary Patches diff --git a/scripts/flash_script.sh b/scripts/flash_script.sh index b6e8275ef32a..7e449cc973df 100644 --- a/scripts/flash_script.sh +++ b/scripts/flash_script.sh @@ -57,8 +57,6 @@ cd $BINDIR for file in lib*.so; do mv "$file" "${file:3:${#file}-6}"; done cd / cp -af $INSTALLER/lib/$ABI32/libmagisk32.so $BINDIR/magisk32 2>/dev/null -cp -af $CHROMEDIR/. $BINDIR/chromeos -chmod -R 755 $BINDIR # Check if system root is installed and remove $BOOTMODE || remove_system_su @@ -73,6 +71,11 @@ ui_print "- Constructing environment" rm -rf $MAGISKBIN/* 2>/dev/null mkdir -p $MAGISKBIN 2>/dev/null cp -af $BINDIR/. $COMMONDIR/. $BBBIN $MAGISKBIN + +# Remove files only used by the Magisk app +rm -f $MAGISKBIN/bootctl $MAGISKBIN/main.jar \ + $MAGISKBIN/module_installer.sh $MAGISKBIN/uninstaller.sh + chmod -R 755 $MAGISKBIN # addon.d diff --git a/stub/src/main/res/values-sw/strings.xml b/stub/src/main/res/values-sw/strings.xml new file mode 100644 index 000000000000..542cebcb308d --- /dev/null +++ b/stub/src/main/res/values-sw/strings.xml @@ -0,0 +1,7 @@ + + + Pata toleo jipya la Magisk kamili ili kumaliza usanidi. Pakua na usakinishe? + Tafadhali unganisha kwenye Mtandao! Kusasisha hadi Magisk kamili inahitajika. + Inapakua + Tafadhali zindua upya programu wewe mwenyewe +