diff --git a/README.md b/README.md index 8e0a61d..56a0f1e 100644 --- a/README.md +++ b/README.md @@ -135,13 +135,24 @@ If you still see `unauthorized` after an upgrade, stop the container, remove per rm -rf ~/data-redroid13/misc/adb ``` +## ADB `failed to connect` / connection refused + +This means **nothing is listening on the host port** (not the same as `unauthorized`). Typical causes: + +1. **Container exited** — especially with `--rm`, a crash removes the instance. Run `docker ps -a` and `docker logs` on a **non-`--rm`** run (`--keep-container`) to see init/EGL errors. +2. **`androidboot.redroid_gpu_mode=host`** — Mesa/EGL or DRI inside the guest can fail on some hosts; Android never reaches **adbd**. **Workaround:** force software rendering: + ```bash + python run_redroid.py ... --adb-insecure --gpu-mode guest + ``` +3. **Still booting** — wait longer, or use **`--wait-tcp 90`** on `run_redroid.py` to block until TCP opens and print logs if it does not. + ## 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: +1. **GPU on the host:** **`--gpu-mode auto`** (default) enables **host** when `/dev/dri` exists. If the instance **crashes** or **ADB never connects**, host EGL may be incompatible — fall back to **`--gpu-mode guest`**. If you must pass devices manually, for example: ```bash docker run ... --device /dev/dri ... ``` diff --git a/run_redroid.py b/run_redroid.py index 45edda0..9387444 100644 --- a/run_redroid.py +++ b/run_redroid.py @@ -4,8 +4,10 @@ import argparse import os import re import shlex +import socket import subprocess import sys +import time import tools.compat as compat from tools.helper import bcolors, print_color @@ -181,6 +183,18 @@ def quote_cmd(args): return " ".join(shlex.quote(part) for part in args) +def wait_tcp_port(host, port, timeout_sec, interval=1.0): + """Return True once TCP connect succeeds, False on timeout.""" + deadline = time.monotonic() + timeout_sec + while time.monotonic() < deadline: + try: + with socket.create_connection((host, port), timeout=2.0): + return True + except OSError: + time.sleep(interval) + return False + + def main(): parser = argparse.ArgumentParser( description="Launch a redroid container with a tested native bridge configuration." @@ -276,6 +290,16 @@ def main(): action="store_true", help="Skip compatibility messages and docker/podman probe (still resolves --gpu-mode auto from DRI)", ) + parser.add_argument( + "--wait-tcp", + type=int, + default=0, + metavar="SEC", + help=( + "After start, wait up to SEC seconds for 127.0.0.1:PORT to accept TCP (Android adbd). " + "If it never opens, print container logs. Use 90 when debugging failed adb connect." + ), + ) args = parser.parse_args() image = args.image @@ -365,7 +389,31 @@ def main(): subprocess.run(cmd, check=True) print_color("Container started as {}".format(name), bcolors.GREEN) + if args.wait_tcp > 0: + print_color( + "Waiting up to {}s for 127.0.0.1:{} (adbd TCP) ...".format(args.wait_tcp, args.port), + bcolors.GREEN, + ) + if not wait_tcp_port("127.0.0.1", args.port, args.wait_tcp): + print_color( + "Port {} never opened. Common with androidboot.redroid_gpu_mode=host when EGL/GPU fails inside " + "the guest — try: python run_redroid.py ... --gpu-mode guest (and drop DRI if needed).".format( + args.port + ), + bcolors.RED, + ) + print_color("Last logs from container {!r}:".format(name), bcolors.YELLOW) + subprocess.run([args.container, "logs", "--tail", "100", name], check=False) + print( + "\nIf the container already exited with --rm, logs are gone; re-run with " + "--keep-container (no --rm) to capture docker logs after a crash." + ) print("ADB: adb connect 127.0.0.1:{}".format(args.port)) + if effective_gpu_mode == "host": + print( + "Note: host GPU mode can prevent full boot; if connect fails, use --gpu-mode guest or " + "--wait-tcp 90 to diagnose." + ) if not args.adb_insecure: print( "If the device shows as unauthorized, stop the container and re-run with --adb-insecure " diff --git a/tools/compat.py b/tools/compat.py index ef72208..b7db1e4 100644 --- a/tools/compat.py +++ b/tools/compat.py @@ -141,6 +141,11 @@ def analyze_for_run( ) if req == "auto" and not skip_check: report.notes.append("Resolved --gpu-mode auto -> {}".format(effective)) + if effective == "host" and not skip_check: + report.notes.append( + "GPU host mode: if ADB cannot connect (connection refused), Android may not have finished boot " + "or init crashed (EGL/driver mismatch); try --gpu-mode guest or docker logs on the container." + ) return report, effective, device_args