's'
This commit is contained in:
50
README.md
50
README.md
@@ -17,6 +17,7 @@ It keeps only the parts that were actually used:
|
|||||||
- `run_redroid.py`
|
- `run_redroid.py`
|
||||||
- `bootstrap_redroid.py`
|
- `bootstrap_redroid.py`
|
||||||
- `verify_offline_bundle.py`
|
- `verify_offline_bundle.py`
|
||||||
|
- `tools/compat.py` (host / GPU / runtime checks)
|
||||||
- `offline/README.md`
|
- `offline/README.md`
|
||||||
- `offline/manifest.json`
|
- `offline/manifest.json`
|
||||||
- `stuff/mindthegapps.py`
|
- `stuff/mindthegapps.py`
|
||||||
@@ -63,6 +64,28 @@ python verify_offline_bundle.py
|
|||||||
python -m pip install -r requirements.txt
|
python -m pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Compatibility checks
|
||||||
|
|
||||||
|
The project probes the **host** before critical steps (no separate manual checklist required):
|
||||||
|
|
||||||
|
| Step | What it checks | Opt-out |
|
||||||
|
|------|----------------|---------|
|
||||||
|
| `redroid.py` | Container runtime (`docker`/`podman`) responds; OS is plausible for redroid | `--no-compat-check` |
|
||||||
|
| `run_redroid.py` | Same + Linux **`/dev/dri`** for GPU; **`binder_linux`** module hint on Linux | `--no-compat-check` |
|
||||||
|
|
||||||
|
**GPU mode (`run_redroid.py`):**
|
||||||
|
|
||||||
|
- **`--gpu-mode auto` (default):** uses **`host`** when DRI devices exist and automatically adds `docker run --device …` for each `/dev/dri/card*` / `renderD*` node; otherwise **`guest`** (software GLES).
|
||||||
|
- **`--gpu-mode host`:** pass-through when DRI exists; otherwise **falls back to `guest`** with a warning.
|
||||||
|
- **`--gpu-mode guest`:** force software rendering (common black-screen case on headless servers).
|
||||||
|
|
||||||
|
Quick probe only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tools/compat.py
|
||||||
|
python tools/compat.py -c podman --gpu-mode auto
|
||||||
|
```
|
||||||
|
|
||||||
## Build the 5570 image
|
## Build the 5570 image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -112,6 +135,33 @@ If you still see `unauthorized` after an upgrade, stop the container, remove per
|
|||||||
rm -rf ~/data-redroid13/misc/adb
|
rm -rf ~/data-redroid13/misc/adb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Black screen or flickering
|
||||||
|
|
||||||
|
With **`--gpu-mode guest`** (or **`auto`** when no `/dev/dri` exists), the display uses **software GLES** (SwiftShader). On many **headless servers**, SurfaceFlinger may show a **black screen**, **flicker**, or very low FPS. That is a **display stack** issue, not ADB. The default **`auto`** mode uses **host** GPU when DRI nodes are visible and injects **`--device`** flags automatically.
|
||||||
|
|
||||||
|
**What to try**
|
||||||
|
|
||||||
|
1. **GPU on the host:** ensure **`--gpu-mode auto`** (default) so DRI is detected, or set **`--gpu-mode host`** explicitly. If you must pass devices manually (unusual when `auto` works), for example:
|
||||||
|
```bash
|
||||||
|
docker run ... --device /dev/dri ...
|
||||||
|
```
|
||||||
|
The exact device path varies (Intel/AMD/NVIDIA, `nvidia-docker`, etc.); see [redroid-doc](https://github.com/remote-android/redroid-doc) GPU notes.
|
||||||
|
|
||||||
|
2. **Stay on software rendering** but tune the virtual panel (sometimes reduces glitches):
|
||||||
|
```bash
|
||||||
|
python run_redroid.py ... --adb-insecure \
|
||||||
|
--extra-prop androidboot.redroid_width=720 \
|
||||||
|
--extra-prop androidboot.redroid_height=1280 \
|
||||||
|
--extra-prop androidboot.redroid_fps=15
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **First boot / SetupWizard** can sit on a dark screen for a while. Run **`bootstrap_redroid.py`** once `adb` shows `device` so provisioning and wizard are skipped as intended.
|
||||||
|
|
||||||
|
4. **Debug with logcat** (on the host, after `adb connect`):
|
||||||
|
```bash
|
||||||
|
adb -s 127.0.0.1:5570 logcat -d | grep -iE 'SurfaceFlinger|EGL|GLES|redroid'
|
||||||
|
```
|
||||||
|
|
||||||
## Apply the 5570 hardening
|
## Apply the 5570 hardening
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
13
redroid.py
13
redroid.py
@@ -3,11 +3,13 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
from stuff.houdini import Houdini
|
from stuff.houdini import Houdini
|
||||||
from stuff.houdini_hack import Houdini_Hack
|
from stuff.houdini_hack import Houdini_Hack
|
||||||
from stuff.magisk import Magisk
|
from stuff.magisk import Magisk
|
||||||
from stuff.mindthegapps import MindTheGapps
|
from stuff.mindthegapps import MindTheGapps
|
||||||
|
import tools.compat as compat
|
||||||
import tools.helper as helper
|
import tools.helper as helper
|
||||||
|
|
||||||
|
|
||||||
@@ -163,10 +165,21 @@ def main():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Do not pull images or download archives; require offline/downloads and offline/images",
|
help="Do not pull images or download archives; require offline/downloads and offline/images",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-compat-check",
|
||||||
|
dest="no_compat_check",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip container-runtime / OS compatibility checks before build",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.offline:
|
if args.offline:
|
||||||
os.environ["REDROID_OFFLINE"] = "1"
|
os.environ["REDROID_OFFLINE"] = "1"
|
||||||
|
if not args.no_compat_check:
|
||||||
|
build_report = compat.analyze_for_build(args.container)
|
||||||
|
compat.emit_compat_report(build_report)
|
||||||
|
if build_report.errors:
|
||||||
|
sys.exit(1)
|
||||||
base_image_source = ensure_base_image(args.container, args.android)
|
base_image_source = ensure_base_image(args.container, args.android)
|
||||||
dockerfile, tags = build_layers(args)
|
dockerfile, tags = build_layers(args)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import tools.compat as compat
|
||||||
from tools.helper import bcolors, print_color
|
from tools.helper import bcolors, print_color
|
||||||
|
|
||||||
|
|
||||||
@@ -224,8 +226,11 @@ def main():
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--gpu-mode",
|
"--gpu-mode",
|
||||||
default="guest",
|
default="auto",
|
||||||
help="Value for androidboot.redroid_gpu_mode",
|
help=(
|
||||||
|
"androidboot.redroid_gpu_mode: auto=host if /dev/dri exists else guest (default); "
|
||||||
|
"guest=software GLES; host=GPU (adds --device for each DRI node when present)"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--device-profile",
|
"--device-profile",
|
||||||
@@ -266,6 +271,11 @@ def main():
|
|||||||
"(before device profile; see redroid-doc README / #579). Overrides user-build secure props."
|
"(before device profile; see redroid-doc README / #579). Overrides user-build secure props."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-compat-check",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip compatibility messages and docker/podman probe (still resolves --gpu-mode auto from DRI)",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
image = args.image
|
image = args.image
|
||||||
@@ -278,6 +288,16 @@ def main():
|
|||||||
|
|
||||||
os.makedirs(data_dir, exist_ok=True)
|
os.makedirs(data_dir, exist_ok=True)
|
||||||
|
|
||||||
|
run_report, effective_gpu_mode, dri_device_args = compat.analyze_for_run(
|
||||||
|
args.container,
|
||||||
|
args.gpu_mode,
|
||||||
|
skip_check=args.no_compat_check,
|
||||||
|
)
|
||||||
|
if not args.no_compat_check:
|
||||||
|
compat.emit_compat_report(run_report)
|
||||||
|
if run_report.errors:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
args.container,
|
args.container,
|
||||||
"run",
|
"run",
|
||||||
@@ -294,6 +314,7 @@ def main():
|
|||||||
cmd.extend(["--memory", memory])
|
cmd.extend(["--memory", memory])
|
||||||
if not args.keep_container:
|
if not args.keep_container:
|
||||||
cmd.append("--rm")
|
cmd.append("--rm")
|
||||||
|
cmd.extend(dri_device_args)
|
||||||
cmd.append(image)
|
cmd.append(image)
|
||||||
# Init applies read-only props in order; later ro.adb.secure=0 is ignored if adb.secure
|
# Init applies read-only props in order; later ro.adb.secure=0 is ignored if adb.secure
|
||||||
# was already set (redroid-doc #579). Put these immediately after the image name.
|
# was already set (redroid-doc #579). Put these immediately after the image name.
|
||||||
@@ -308,7 +329,7 @@ def main():
|
|||||||
"ro.debuggable=1",
|
"ro.debuggable=1",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
cmd.append("androidboot.redroid_gpu_mode={}".format(args.gpu_mode))
|
cmd.append("androidboot.redroid_gpu_mode={}".format(effective_gpu_mode))
|
||||||
cmd.extend(bridge_props(bridge))
|
cmd.extend(bridge_props(bridge))
|
||||||
cmd.extend(preset_props(preset))
|
cmd.extend(preset_props(preset))
|
||||||
cmd.extend(device_profile_props(device_profile))
|
cmd.extend(device_profile_props(device_profile))
|
||||||
@@ -322,6 +343,10 @@ def main():
|
|||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print_color(
|
||||||
|
"Selected GPU mode: {} (androidboot.redroid_gpu_mode)".format(effective_gpu_mode),
|
||||||
|
bcolors.GREEN,
|
||||||
|
)
|
||||||
print_color("Selected native bridge: {}".format(bridge), bcolors.GREEN)
|
print_color("Selected native bridge: {}".format(bridge), bcolors.GREEN)
|
||||||
if preset != "none":
|
if preset != "none":
|
||||||
print_color("Selected launch preset: {}".format(preset), bcolors.GREEN)
|
print_color("Selected launch preset: {}".format(preset), bcolors.GREEN)
|
||||||
|
|||||||
167
tools/compat.py
Normal file
167
tools/compat.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
"""
|
||||||
|
Host and container-runtime compatibility checks for redroid-android13.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
_REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(_REPO_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(_REPO_ROOT))
|
||||||
|
|
||||||
|
from tools.helper import bcolors, print_color
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CompatReport:
|
||||||
|
errors: List[str] = field(default_factory=list)
|
||||||
|
warnings: List[str] = field(default_factory=list)
|
||||||
|
notes: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
def _runtime_available(runtime: str) -> bool:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[runtime, "version"],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
timeout=30,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def detect_dri_devices() -> List[str]:
|
||||||
|
"""Paths suitable for docker/podman --device (Linux DRM)."""
|
||||||
|
found: List[str] = []
|
||||||
|
for pattern in ("/dev/dri/card*", "/dev/dri/renderD*"):
|
||||||
|
found.extend(glob.glob(pattern))
|
||||||
|
return sorted(set(found))
|
||||||
|
|
||||||
|
|
||||||
|
def _linux_binder_notes() -> List[str]:
|
||||||
|
if platform.system() != "Linux":
|
||||||
|
return []
|
||||||
|
notes: List[str] = []
|
||||||
|
try:
|
||||||
|
with open("/proc/modules", "r", encoding="utf-8", errors="ignore") as handle:
|
||||||
|
loaded = handle.read()
|
||||||
|
if "binder_linux" not in loaded:
|
||||||
|
notes.append(
|
||||||
|
"binder_linux not loaded; redroid usually needs: "
|
||||||
|
"modprobe binder_linux devices=binder,hwbinder,vndbinder"
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return notes
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_for_build(container_runtime: str) -> CompatReport:
|
||||||
|
report = CompatReport()
|
||||||
|
if not _runtime_available(container_runtime):
|
||||||
|
report.errors.append(
|
||||||
|
"Container runtime {!r} not found or not working (install Docker/Podman and check PATH).".format(
|
||||||
|
container_runtime
|
||||||
|
)
|
||||||
|
)
|
||||||
|
system = platform.system()
|
||||||
|
if system == "Windows":
|
||||||
|
report.warnings.append(
|
||||||
|
"Running on Windows: redroid containers are meant for Linux hosts (binder/ashmem); "
|
||||||
|
"building files here is fine, running the container typically happens on Linux."
|
||||||
|
)
|
||||||
|
elif system != "Linux":
|
||||||
|
report.warnings.append(
|
||||||
|
"Host OS is not Linux; upstream redroid expects Linux with binder and related kernel pieces."
|
||||||
|
)
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_gpu_mode(requested: str, dri_devices: List[str]) -> Tuple[str, List[str]]:
|
||||||
|
"""
|
||||||
|
Map user --gpu-mode to an effective redroid mode and docker --device arguments.
|
||||||
|
auto -> host if DRI nodes exist, else guest.
|
||||||
|
"""
|
||||||
|
req = (requested or "auto").strip().lower()
|
||||||
|
if req == "auto":
|
||||||
|
effective = "host" if dri_devices else "guest"
|
||||||
|
elif req == "host":
|
||||||
|
effective = "host" if dri_devices else "guest"
|
||||||
|
elif req == "guest":
|
||||||
|
effective = "guest"
|
||||||
|
else:
|
||||||
|
effective = "guest"
|
||||||
|
|
||||||
|
device_args: List[str] = []
|
||||||
|
if effective == "host" and dri_devices:
|
||||||
|
for path in dri_devices:
|
||||||
|
device_args.extend(["--device", path])
|
||||||
|
return effective, device_args
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_for_run(
|
||||||
|
container_runtime: str,
|
||||||
|
gpu_mode: str,
|
||||||
|
skip_check: bool,
|
||||||
|
) -> Tuple[CompatReport, str, List[str]]:
|
||||||
|
"""
|
||||||
|
Returns (report, effective_gpu_mode, flat docker --device args).
|
||||||
|
"""
|
||||||
|
report = CompatReport()
|
||||||
|
dri = detect_dri_devices()
|
||||||
|
|
||||||
|
if not skip_check:
|
||||||
|
build = analyze_for_build(container_runtime)
|
||||||
|
report.errors.extend(build.errors)
|
||||||
|
report.warnings.extend(build.warnings)
|
||||||
|
report.notes.extend(build.notes)
|
||||||
|
if dri:
|
||||||
|
report.notes.append("DRI present: {} — GPU passthrough can be used.".format(", ".join(dri)))
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
report.warnings.append(
|
||||||
|
"No /dev/dri render nodes: software GLES (guest) often black-screens or flickers on headless servers."
|
||||||
|
)
|
||||||
|
report.notes.extend(_linux_binder_notes())
|
||||||
|
|
||||||
|
effective, device_args = resolve_gpu_mode(gpu_mode, dri)
|
||||||
|
req = (gpu_mode or "auto").strip().lower()
|
||||||
|
if req == "host" and effective == "guest":
|
||||||
|
report.warnings.append(
|
||||||
|
"--gpu-mode host requested but no DRI devices found; falling back to guest."
|
||||||
|
)
|
||||||
|
if req == "auto" and not skip_check:
|
||||||
|
report.notes.append("Resolved --gpu-mode auto -> {}".format(effective))
|
||||||
|
|
||||||
|
return report, effective, device_args
|
||||||
|
|
||||||
|
|
||||||
|
def emit_compat_report(report: CompatReport, prefix: str = "[compat] ") -> None:
|
||||||
|
for msg in report.errors:
|
||||||
|
print_color(prefix + msg, bcolors.RED)
|
||||||
|
for msg in report.warnings:
|
||||||
|
print_color(prefix + msg, bcolors.YELLOW)
|
||||||
|
for msg in report.notes:
|
||||||
|
print_color(prefix + msg, bcolors.GREEN)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
ap = argparse.ArgumentParser(description="Host/runtime compatibility probe (same logic as run_redroid.py)")
|
||||||
|
ap.add_argument("-c", "--container", default="docker", help="docker or podman")
|
||||||
|
ap.add_argument("--gpu-mode", default="auto", help="guest, host, or auto")
|
||||||
|
ns = ap.parse_args()
|
||||||
|
rep, gpu_eff, dev_args = analyze_for_run(ns.container, ns.gpu_mode, skip_check=False)
|
||||||
|
emit_compat_report(rep)
|
||||||
|
print("effective_gpu_mode={}".format(gpu_eff))
|
||||||
|
print("flat_device_args={}".format(dev_args))
|
||||||
Reference in New Issue
Block a user