282 lines
11 KiB
Markdown
282 lines
11 KiB
Markdown
# redroid-android13
|
||
|
||
This repo is the minimal Android 13 redroid bundle that matches the optimized `5570` instance.
|
||
|
||
It keeps only the parts that were actually used:
|
||
|
||
- `redroid/redroid:13.0.0`
|
||
- `MindTheGapps`
|
||
- `libhoudini`
|
||
- `Kitsune Mask / Magisk Delta`
|
||
- `Pixel 7 Pro` device profile
|
||
- the `post-fs-data` fix that keeps `Magisk Installed` and the settings page healthy
|
||
|
||
## Included files
|
||
|
||
- `redroid.py`
|
||
- `run_redroid.py`
|
||
- `bootstrap_redroid.py`
|
||
- `verify_offline_bundle.py`
|
||
- `tools/compat.py` (host / GPU / runtime checks)
|
||
- `tools/ssh_remote.py` (Paramiko SSH: remote `docker` / `adb` diagnostics)
|
||
- `offline/README.md`
|
||
- `offline/manifest.json`
|
||
- `stuff/mindthegapps.py`
|
||
- `stuff/houdini.py`
|
||
- `stuff/houdini_hack.py`
|
||
- `stuff/magisk.py`
|
||
- `assets/redroid_extreme_post_fs_data.sh`
|
||
- local `offline/downloads/`
|
||
- local `offline/images/`
|
||
|
||
## Offline archive
|
||
|
||
The repo tracks the offline bundle metadata under [offline](offline). The large zip/apk/tar payloads are intentionally kept out of Git and should be stored locally under `offline/downloads/` and `offline/images/`.
|
||
|
||
Use [offline/manifest.json](offline/manifest.json) for checksums and source metadata, and [offline/README.md](offline/README.md) for the expected local layout.
|
||
|
||
Expected local files:
|
||
|
||
- `offline/downloads/mindthegapps.zip`
|
||
- `offline/downloads/libhoudini.zip`
|
||
- `offline/downloads/libhoudini_hack.zip`
|
||
- `offline/downloads/magisk.apk`
|
||
- `offline/downloads/magisk.version`
|
||
- `offline/images/redroid-redroid-13.0.0-latest.tar` (optional if the base image is already in local Docker storage)
|
||
|
||
### Build resource order (`redroid.py`)
|
||
|
||
- **Base image:** prefers an existing local `redroid/redroid:<ver>-latest`, then `offline/images/redroid-redroid-13.0.0-latest.tar` (`docker load`), then a registry pull (skipped when offline-only).
|
||
- **Zips / APK:** prefers files under `offline/downloads/` whose **MD5** matches the pinned upstream artifacts; downloads only if missing or mismatch (skipped when offline-only).
|
||
- **Strict no-network builds:** pass `--offline` or set `REDROID_OFFLINE=1` so missing bundles fail fast instead of downloading.
|
||
|
||
### Verify script notes
|
||
|
||
```bash
|
||
python verify_offline_bundle.py
|
||
```
|
||
|
||
- `magisk.version` is checked against exact **size** and **sha256** from `manifest.json`. A short version string without the expected newline/padding will report `size mismatch` even when `magisk.apk` is fine for the image build.
|
||
- A missing `offline/images/*.tar` is expected to **FAIL** here if you rely on a pre-pulled Docker image instead; that does not block `redroid.py` when the base image already exists locally.
|
||
|
||
## Dependencies
|
||
|
||
```bash
|
||
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 guest` (default):** software GLES — most stable on headless / server GPUs where host EGL inside the guest often breaks boot or **adbd**.
|
||
- **`--gpu-mode auto`:** stays **`guest`** unless **`REDROID_USE_HOST_GPU=1`** (or `true`/`yes`/`on`) **and** `/dev/dri` nodes exist; then **`host`** and `docker run --device …` per DRI node.
|
||
- **`--gpu-mode host`:** pass-through when DRI exists; otherwise **falls back to `guest`** with a warning.
|
||
|
||
Quick probe only:
|
||
|
||
```bash
|
||
python tools/compat.py
|
||
python tools/compat.py -c podman --gpu-mode guest
|
||
```
|
||
|
||
## Build the 5570 image
|
||
|
||
```bash
|
||
python redroid.py -mtg -i -m
|
||
```
|
||
|
||
Build flags (short options):
|
||
|
||
| Flag | Long form | Meaning |
|
||
|------|-----------|---------|
|
||
| **`-mtg`** | `--install-mindthegapps` | Bake **MindTheGapps** (Google apps framework) into the image |
|
||
| **`-i`** | `--install-houdini` | Bake **libhoudini** + **redroid houdini hack** (ARM translation on x86) |
|
||
| **`-m`** | `--install-magisk` | Bake **Kitsune Mask / Magisk** bootless payload into the image |
|
||
|
||
Together they produce `redroid/redroid:13.0.0_mindthegapps_houdini_magisk`. None of these control the **screen**; display is set when you **run** the container (`run_redroid.py`).
|
||
|
||
Air-gapped (requires a complete `offline/` tree):
|
||
|
||
```bash
|
||
REDROID_OFFLINE=1 python redroid.py -mtg -i -m
|
||
# or
|
||
python redroid.py -mtg -i -m --offline
|
||
```
|
||
|
||
Expected image tag:
|
||
|
||
```bash
|
||
redroid/redroid:13.0.0_mindthegapps_houdini_magisk
|
||
```
|
||
|
||
## Launch the container
|
||
|
||
Headless servers have **no screen** to approve the USB debugging RSA dialog. Always pass **`--adb-insecure`** so boot props disable ADB RSA checks and apply **before** the Pixel user profile (`androidboot.adb.secure=0`, `ro.adb.secure=0`, `ro.secure=0`, `ro.debuggable=1`). **Do not expose the ADB port on a public network.**
|
||
|
||
### Persist after reboot (default)
|
||
|
||
By default **`run_redroid.py` does not pass `--rm`**: the container stays on disk when it stops, and uses **`--restart unless-stopped`** so **Docker/Podman will start it again after the daemon or host reboots** (unless you ran `docker stop <name>`).
|
||
|
||
- **Always restart** even after explicit stop: `--restart always`
|
||
- **No auto-restart** but still keep the container (no `--rm`): `--restart no`
|
||
- **One-off / dev only** (old behavior, removed on stop): pass **`--rm`**
|
||
|
||
On Ubuntu, ensure the engine starts on boot:
|
||
|
||
```bash
|
||
sudo systemctl enable docker --now
|
||
# or: sudo systemctl enable podman.socket --now # if you use Podman rootful similarly
|
||
```
|
||
|
||
```bash
|
||
python run_redroid.py \
|
||
--image redroid/redroid:13.0.0_mindthegapps_houdini_magisk \
|
||
--bridge libhoudini \
|
||
--device-profile pixel-7-pro \
|
||
--data-dir ~/data-redroid13 \
|
||
--port 5570 \
|
||
--replace \
|
||
--adb-insecure
|
||
```
|
||
|
||
On the **same machine as Docker**, one-shot status + logs + `logcat` after start:
|
||
|
||
```bash
|
||
python run_redroid.py ... --adb-insecure --diagnose --diagnose-settle 20
|
||
```
|
||
|
||
Then:
|
||
|
||
```bash
|
||
adb kill-server && adb start-server
|
||
adb connect 127.0.0.1:5570
|
||
adb devices
|
||
```
|
||
|
||
If you still see `unauthorized` after an upgrade, stop the container, remove persisted keys on the host, and retry:
|
||
|
||
```bash
|
||
rm -rf ~/data-redroid13/misc/adb
|
||
```
|
||
|
||
## Remote diagnostics over SSH (Paramiko)
|
||
|
||
When redroid runs on **another Linux machine**, you can drive checks from your PC with **`tools/ssh_remote.py`** (uses [Paramiko](https://www.paramiko.org/), declared in `requirements.txt`).
|
||
|
||
**Do not put passwords in Git.** Use a key file or a one-time prompt:
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
export REDROID_SSH_HOST=192.168.2.179
|
||
export REDROID_SSH_USER=eric
|
||
python tools/ssh_remote.py --password-prompt diagnose
|
||
```
|
||
|
||
Key-based:
|
||
|
||
```bash
|
||
export REDROID_SSH_HOST=... REDROID_SSH_USER=eric REDROID_SSH_KEY=$HOME/.ssh/id_ed25519
|
||
python tools/ssh_remote.py diagnose
|
||
```
|
||
|
||
Optional env: **`REDROID_CONTAINER`** (default `redroid_13.0.0_mindthegapps_houdini_magisk`), **`REDROID_ADB_PORT`** (default `5570`). You can also pass **`--container-name`** / **`--adb-port`**.
|
||
|
||
Arbitrary remote command (note the **`--`** before the shell snippet):
|
||
|
||
```bash
|
||
python tools/ssh_remote.py -H 192.168.2.179 -U eric --password-prompt exec -- bash -lc 'docker ps -a | head -20'
|
||
```
|
||
|
||
The `shell` subcommand is a minimal pseudo-shell only; for a full TTY, use normal OpenSSH **`ssh`**.
|
||
|
||
## 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** — if you used **`--rm`**, a crash removes the instance. By default the container is **kept**; use `docker ps -a` and `docker logs` to inspect. Ensure **`--restart`** is not `no` if you want auto-start after reboot.
|
||
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`** (the default), the display uses **software GLES** (SwiftShader). On many **headless servers**, SurfaceFlinger may still show **black screen** or **flicker** — try **`--soft-display`** and more RAM. Host GPU needs **`REDROID_USE_HOST_GPU=1`** with **`--gpu-mode auto`** or **`--gpu-mode host`** when DRI works inside the guest.
|
||
|
||
**What to try**
|
||
|
||
1. **GPU on the host:** set **`REDROID_USE_HOST_GPU=1`** and **`--gpu-mode auto`**, or **`--gpu-mode host`**. If **ADB never connects**, host EGL is likely broken — use default **`--gpu-mode guest`**. Manual devices 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. **Software GLES (`guest`)** — force low load on the virtual display (often reduces black screen / flicker):
|
||
```bash
|
||
python run_redroid.py ... --adb-insecure --gpu-mode guest --soft-display --memory 4g
|
||
```
|
||
`--soft-display` sets 720×1280, 15 FPS, dpi 280. You can tune further with `--extra-prop` if needed.
|
||
|
||
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
|
||
|
||
```bash
|
||
python bootstrap_redroid.py \
|
||
--serial 127.0.0.1:5570 \
|
||
--device-profile pixel-7-pro \
|
||
--proxy-host auto \
|
||
--install-extreme-script
|
||
```
|
||
|
||
This post-boot step:
|
||
|
||
- disables SetupWizard
|
||
- marks the device provisioned
|
||
- enables high accuracy location mode
|
||
- allows `com.android.shell` to use mock location
|
||
- applies the `Pixel 7 Pro / user / release-keys / locked / green` identity
|
||
|
||
With `--install-extreme-script`, the validated script [assets/redroid_extreme_post_fs_data.sh](assets/redroid_extreme_post_fs_data.sh) is copied to `/data/adb/post-fs-data.d/20-redroid-extreme.sh` so that:
|
||
|
||
- `vendor/bin/su` stays visible to app namespaces after reboot
|
||
- `Pixel 7 Pro` properties are re-applied on every boot
|
||
|
||
## Verified 5570 state
|
||
|
||
The matching `5570` instance was already validated with:
|
||
|
||
- `ro.product.model=Pixel 7 Pro`
|
||
- `ro.build.type=user`
|
||
- `ro.build.tags=release-keys`
|
||
- `ro.boot.vbmeta.device_state=locked`
|
||
- `ro.boot.verifiedbootstate=green`
|
||
- app UID `su -V = 27001`
|
||
- app UID `su -mm -c id = uid=0(root)`
|
||
- `Kitsune` home page shows a normal `Installed` state
|
||
- `Kitsune` settings page opens normally and `SuList / MagiskHide` is configurable
|
||
|
||
## Current scope
|
||
|
||
This repo only reproduces the system environment for the optimized `5570` build.
|
||
|
||
It does not yet include:
|
||
|
||
- virtual camera
|
||
- camera HAL
|
||
- TikTok Live specific patches
|