From 8e224ac193aac6f66df34a522d3a4041db3a7764 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 9 Apr 2026 03:29:37 +0000 Subject: [PATCH] Fix-Magisk-bootstrap-compatibility --- bootstrap_redroid.py | 88 +++++++++++++++++++++++++++++++++++--------- redroid.py | 1 + 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/bootstrap_redroid.py b/bootstrap_redroid.py index ae97ce4..238f7d1 100644 --- a/bootstrap_redroid.py +++ b/bootstrap_redroid.py @@ -43,20 +43,47 @@ class Backend: if self.mode == "adb" and ":" in self.serial: run(["adb", "connect", self.serial], check=False) + def _root_probe_args(self): + return [ + ["shell", "su", "root", "id"], + ["shell", "su", "0", "id"], + ] + + def _root_shell_args(self, command): + return [ + ["shell", "su", "root", "sh", "-c", command], + ["shell", "su", "0", "sh", "-c", command], + ] + def has_root(self): if self.mode == "container": return True - if not self._root_checked: - result = adb( - self.serial, - ["shell", "su", "-mm", "-c", "id"], - check=False, - capture_output=True, - ) - self._root_available = result.returncode == 0 and "uid=0(" in result.stdout - self._root_checked = True + if not self._root_checked or not self._root_available: + for args in self._root_probe_args(): + result = adb( + self.serial, + args, + check=False, + capture_output=True, + ) + if result.returncode == 0 and "uid=0(" in result.stdout: + self._root_available = True + self._root_checked = True + return True + self._root_available = False + self._root_checked = False return self._root_available + def wait_for_root(self, timeout): + if self.mode == "container": + return True + deadline = time.time() + timeout + while time.time() < deadline: + if self.has_root(): + return True + time.sleep(2) + return False + def exec_shell(self, command, check=True, capture_output=False, root=False): if self.mode == "container": return run( @@ -72,12 +99,20 @@ class Backend: capture_output=capture_output, ) if root and self.has_root(): - return adb( - self.serial, - ["shell", "su", "-mm", "-c", command], - check=check, - capture_output=capture_output, - ) + last_result = None + for args in self._root_shell_args(command): + result = adb( + self.serial, + args, + check=False, + capture_output=capture_output, + ) + last_result = result + if result.returncode == 0: + return result + if check and last_result is not None: + last_result.check_returncode() + return last_result return adb( self.serial, ["shell", command], @@ -160,6 +195,8 @@ def shell_quote(value): def apply_device_profile(backend, profile): if profile == "none": return "skipped" + if backend.mode == "adb" and not backend.has_root(): + return "skipped (root unavailable)" props = DEVICE_PROFILES[profile] resetprop = backend.exec_shell( "if [ -x /debug_ramdisk/resetprop ]; then echo /debug_ramdisk/resetprop; " @@ -263,6 +300,12 @@ def main(): default="none", help="Apply a runtime device profile through resetprop when available", ) + parser.add_argument( + "--root-timeout", + type=int, + default=120, + help="Seconds to wait for Magisk root in ADB mode before root-only tweaks", + ) parser.add_argument( "--install-extreme-script", action="store_true", @@ -278,14 +321,25 @@ def main(): backend.connect() backend.wait_for_boot(args.timeout) + if backend.mode == "adb" and (args.device_profile != "none" or args.install_extreme_script): + root_ready = backend.wait_for_root(min(args.root_timeout, args.timeout)) + if not root_ready: + print("Root: unavailable after waiting; continuing without root-only tweaks") + put_setting(backend, "global", "device_provisioned", "1") put_setting(backend, "secure", "user_setup_complete", "1") put_setting(backend, "secure", "location_mode", "3") put_setting(backend, "global", "package_verifier_enable", "0") put_setting(backend, "global", "verifier_verify_adb_installs", "0") - try_shell(backend, "pm disable-user --user 0 com.android.setupwizard") - try_shell(backend, "pm disable-user --user 0 com.google.android.setupwizard") + for package in ("com.android.setupwizard", "com.google.android.setupwizard"): + packages = backend.exec_shell( + "pm list packages --user 0", + check=False, + capture_output=True, + ).stdout + if package in packages: + try_shell(backend, "pm disable-user --user 0 {}".format(shell_quote(package))) try_shell(backend, "appops set com.android.shell android:mock_location allow") proxy_state = configure_proxy(backend, args.proxy_host, args.proxy_port) diff --git a/redroid.py b/redroid.py index 3efe343..ecbb4ea 100644 --- a/redroid.py +++ b/redroid.py @@ -89,6 +89,7 @@ def build_layers(args): tags.append("houdini") if args.magisk: + os.environ.setdefault("REDROID_MAGISK_BOOT_COMPLETE", "1") Magisk().install() dockerfile += "COPY magisk /\n" tags.append("magisk")