Files
gitlab-instance-0a899031_re…/run_redroid.py
2026-04-03 23:22:27 -05:00

325 lines
10 KiB
Python

#!/usr/bin/env python3
import argparse
import os
import re
import shlex
import subprocess
from tools.helper import bcolors, print_color
COMMON_BRIDGE_PROPS = [
"ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi",
"ro.product.cpu.abilist64=x86_64,arm64-v8a",
"ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi",
"ro.dalvik.vm.isa.arm=x86",
"ro.dalvik.vm.isa.arm64=x86_64",
"ro.enable.native.bridge.exec=1",
"ro.enable.native.bridge.exec64=1",
"ro.vendor.enable.native.bridge.exec=1",
"ro.vendor.enable.native.bridge.exec64=1",
]
DEVICE_PROFILES = {
"pixel-3-xl": [
"ro.product.model=Pixel 3 XL",
"ro.product.brand=google",
"ro.product.name=crosshatch",
"ro.product.device=crosshatch",
"ro.product.manufacturer=Google",
"ro.product.system.model=Pixel 3 XL",
"ro.product.system.brand=google",
"ro.product.system.name=crosshatch",
"ro.product.system.device=crosshatch",
"ro.product.system.manufacturer=Google",
"ro.product.vendor.model=Pixel 3 XL",
"ro.product.vendor.brand=google",
"ro.product.vendor.name=crosshatch",
"ro.product.vendor.device=crosshatch",
"ro.product.vendor.manufacturer=Google",
"ro.product.product.model=Pixel 3 XL",
"ro.product.product.brand=google",
"ro.product.product.name=crosshatch",
"ro.product.product.device=crosshatch",
"ro.product.product.manufacturer=Google",
"ro.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
"ro.system.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
"ro.vendor.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
"ro.product.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
"ro.build.type=user",
"ro.build.tags=release-keys",
"ro.secure=1",
"ro.debuggable=0",
"ro.boot.verifiedbootstate=green",
"ro.boot.vbmeta.device_state=locked",
"ro.boot.flash.locked=1",
"ro.boot.veritymode=enforcing",
],
"pixel-7-pro": [
"ro.product.model=Pixel 7 Pro",
"ro.product.brand=google",
"ro.product.name=cheetah",
"ro.product.device=cheetah",
"ro.product.manufacturer=Google",
"ro.product.system.model=Pixel 7 Pro",
"ro.product.system.brand=google",
"ro.product.system.name=cheetah",
"ro.product.system.device=cheetah",
"ro.product.system.manufacturer=Google",
"ro.product.vendor.model=Pixel 7 Pro",
"ro.product.vendor.brand=google",
"ro.product.vendor.name=cheetah",
"ro.product.vendor.device=cheetah",
"ro.product.vendor.manufacturer=Google",
"ro.product.product.model=Pixel 7 Pro",
"ro.product.product.brand=google",
"ro.product.product.name=cheetah",
"ro.product.product.device=cheetah",
"ro.product.product.manufacturer=Google",
"ro.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
"ro.system.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
"ro.vendor.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
"ro.product.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
"ro.build.type=user",
"ro.build.tags=release-keys",
"ro.secure=1",
"ro.debuggable=0",
"ro.boot.verifiedbootstate=green",
"ro.boot.vbmeta.device_state=locked",
"ro.boot.flash.locked=1",
"ro.boot.veritymode=enforcing",
]
}
LAUNCH_PRESETS = {
"redroid-pro": {
"props": [
"androidboot.redroid_width=720",
"androidboot.redroid_height=1280",
"androidboot.redroid_dpi=320",
"androidboot.redroid_fps=30",
"androidboot.redroid_vm_heap=256m",
"ro.config.low_ram=true",
"config.disable_consumerir=true",
],
"device_profile": "pixel-3-xl",
"memory": "4g",
}
}
def infer_bridge(image, requested):
if requested != "auto":
return requested
if "houdini" in image:
return "libhoudini"
if "ndk" in image:
return "libndk"
return "none"
def bridge_props(mode):
if mode == "none":
return []
props = list(COMMON_BRIDGE_PROPS)
if mode == "libhoudini":
props.append("ro.dalvik.vm.native.bridge=libhoudini.so")
elif mode == "libnb":
props.append("ro.dalvik.vm.native.bridge=libnb.so")
elif mode == "libndk":
props.append("ro.dalvik.vm.native.bridge=libndk_translation.so")
props.append("ro.ndk_translation.version=0.2.3")
else:
raise ValueError("Unsupported bridge mode: {}".format(mode))
return props
def device_profile_props(profile):
if profile == "none":
return []
try:
return list(DEVICE_PROFILES[profile])
except KeyError as exc:
raise ValueError("Unsupported device profile: {}".format(profile)) from exc
def preset_props(preset):
if preset == "none":
return []
try:
return list(LAUNCH_PRESETS[preset]["props"])
except KeyError as exc:
raise ValueError("Unsupported preset: {}".format(preset)) from exc
def resolve_device_profile(preset, requested):
if requested != "none":
return requested
if preset == "none":
return "none"
return LAUNCH_PRESETS[preset].get("device_profile", "none")
def resolve_memory(preset, requested):
if requested:
return requested
if preset == "none":
return None
return LAUNCH_PRESETS[preset].get("memory")
def default_name(image):
name = image.split("/")[-1].replace(":", "_")
name = re.sub(r"[^a-zA-Z0-9_.-]+", "-", name).strip("-")
return name or "redroid"
def quote_cmd(args):
return " ".join(shlex.quote(part) for part in args)
def main():
parser = argparse.ArgumentParser(
description="Launch a redroid container with a tested native bridge configuration."
)
parser.add_argument(
"--image",
required=True,
help="Image name, for example redroid/redroid:13.0.0_mindthegapps_houdini_magisk",
)
parser.add_argument(
"-c",
"--container",
default="docker",
choices=["docker", "podman"],
help="Container runtime",
)
parser.add_argument(
"--name",
help="Container name. Defaults to a slug derived from the image tag.",
)
parser.add_argument(
"--data-dir",
default="~/data-redroid",
help="Host directory mapped to /data",
)
parser.add_argument(
"--port",
type=int,
default=5555,
help="Host TCP port mapped to guest ADB 5555",
)
parser.add_argument(
"--bridge",
choices=["auto", "none", "libhoudini", "libnb", "libndk"],
default="auto",
help="Native bridge runtime property to inject",
)
parser.add_argument(
"--preset",
choices=["none"] + sorted(LAUNCH_PRESETS.keys()),
default="none",
help="Inject a tested launch preset",
)
parser.add_argument(
"--gpu-mode",
default="guest",
help="Value for androidboot.redroid_gpu_mode",
)
parser.add_argument(
"--device-profile",
choices=["none"] + sorted(DEVICE_PROFILES.keys()),
default="none",
help="Inject a tested device identity profile",
)
parser.add_argument(
"--memory",
help="Optional container memory limit, for example 4g",
)
parser.add_argument(
"--replace",
action="store_true",
help="Remove an existing container with the same name before running",
)
parser.add_argument(
"--keep-container",
action="store_true",
help="Do not pass --rm to the runtime",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print the generated command without executing it",
)
parser.add_argument(
"--extra-prop",
action="append",
default=[],
help="Additional redroid property, repeatable",
)
args = parser.parse_args()
image = args.image
name = args.name or default_name(image)
data_dir = os.path.abspath(os.path.expanduser(args.data_dir))
bridge = infer_bridge(image, args.bridge)
preset = args.preset
device_profile = resolve_device_profile(preset, args.device_profile)
memory = resolve_memory(preset, args.memory)
os.makedirs(data_dir, exist_ok=True)
cmd = [
args.container,
"run",
"-d",
"--privileged",
"--name",
name,
"-v",
"{}:/data".format(data_dir),
"-p",
"{}:5555".format(args.port),
]
if memory:
cmd.extend(["--memory", memory])
if not args.keep_container:
cmd.append("--rm")
cmd.extend(
[
image,
"androidboot.redroid_gpu_mode={}".format(args.gpu_mode),
]
)
cmd.extend(bridge_props(bridge))
cmd.extend(preset_props(preset))
cmd.extend(device_profile_props(device_profile))
cmd.extend(args.extra_prop)
if args.replace and not args.dry_run:
subprocess.run(
[args.container, "rm", "-f", name],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
print_color("Selected native bridge: {}".format(bridge), bcolors.GREEN)
if preset != "none":
print_color("Selected launch preset: {}".format(preset), bcolors.GREEN)
if device_profile != "none":
print_color("Selected device profile: {}".format(device_profile), bcolors.GREEN)
print("Command:\n{}\n".format(quote_cmd(cmd)))
if args.dry_run:
return
subprocess.run(cmd, check=True)
print_color("Container started as {}".format(name), bcolors.GREEN)
print("ADB: adb connect 127.0.0.1:{}".format(args.port))
if __name__ == "__main__":
main()