's'
This commit is contained in:
427
android13/redroid13.sh
Normal file
427
android13/redroid13.sh
Normal file
@@ -0,0 +1,427 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SSH_HOST="${REDROID_SSH_HOST:-192.168.2.179}"
|
||||
SSH_USER="${REDROID_SSH_USER:-eric}"
|
||||
REMOTE_REPO="${REDROID_REMOTE_REPO:-/home/eric/redroid-android13}"
|
||||
REDROID_IMAGE_OVERRIDE="${REDROID_IMAGE:-}"
|
||||
REDROID_IMAGE_X86="${REDROID_IMAGE_X86:-redroid/redroid:13.0.0_mindthegapps_houdini_magisk}"
|
||||
REDROID_IMAGE_ARM64="${REDROID_IMAGE_ARM64:-redroid/redroid:13.0.0_mindthegapps_magisk}"
|
||||
REDROID_ARCH_PROFILE="${REDROID_ARCH_PROFILE:-auto}"
|
||||
DEVICE_PROFILE="${REDROID_DEVICE_PROFILE:-pixel-7-pro}"
|
||||
PORTAINER_PORT="${REDROID_PORTAINER_PORT:-9000}"
|
||||
DEFAULT_PORT="${REDROID_DEFAULT_PORT:-5570}"
|
||||
DATA_PREFIX="${REDROID_DATA_PREFIX:-/home/eric/data-redroid13}"
|
||||
PORT_STEP="${REDROID_PORT_STEP:-1}"
|
||||
INSTANCE_PREFIX="${REDROID_INSTANCE_PREFIX:-redroid13}"
|
||||
DEFAULT_COUNT="${REDROID_DEFAULT_COUNT:-3}"
|
||||
GPU_MODE="${REDROID_GPU_MODE:-guest}"
|
||||
ADB_INSECURE="${REDROID_ADB_INSECURE:-1}"
|
||||
BOOT_TIMEOUT="${REDROID_BOOT_TIMEOUT:-120}"
|
||||
ROOT_TIMEOUT="${REDROID_ROOT_TIMEOUT:-60}"
|
||||
WAIT_TCP="${REDROID_WAIT_TCP:-120}"
|
||||
RESOLVED_IMAGE=""
|
||||
RESOLVED_ARCH=""
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "python3 is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! python3 - <<'PY' >/dev/null 2>&1
|
||||
import importlib.util
|
||||
raise SystemExit(0 if importlib.util.find_spec("paramiko") else 1)
|
||||
PY
|
||||
then
|
||||
echo "python3 paramiko is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -r -s -p "SSH password for ${SSH_USER}@${SSH_HOST}: " SSH_PASSWORD
|
||||
echo
|
||||
|
||||
sq() {
|
||||
printf '%q' "$1"
|
||||
}
|
||||
|
||||
remote_exec() {
|
||||
local remote_cmd="$1"
|
||||
SSH_HOST="$SSH_HOST" \
|
||||
SSH_USER="$SSH_USER" \
|
||||
SSH_PASSWORD="$SSH_PASSWORD" \
|
||||
SSH_CMD="$remote_cmd" \
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
import sys
|
||||
|
||||
import paramiko
|
||||
|
||||
host = os.environ["SSH_HOST"]
|
||||
user = os.environ["SSH_USER"]
|
||||
password = os.environ["SSH_PASSWORD"]
|
||||
cmd = os.environ["SSH_CMD"]
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(
|
||||
hostname=host,
|
||||
username=user,
|
||||
password=password,
|
||||
timeout=20,
|
||||
banner_timeout=20,
|
||||
auth_timeout=20,
|
||||
)
|
||||
stdin, stdout, stderr = client.exec_command(cmd, get_pty=True)
|
||||
out = stdout.read().decode(errors="replace")
|
||||
err = stderr.read().decode(errors="replace")
|
||||
if out:
|
||||
sys.stdout.write(out)
|
||||
if err:
|
||||
sys.stderr.write(err)
|
||||
status = stdout.channel.recv_exit_status()
|
||||
client.close()
|
||||
sys.exit(status)
|
||||
PY
|
||||
}
|
||||
|
||||
remote_repo_cmd() {
|
||||
local inner="$1"
|
||||
remote_exec "cd $(sq "$REMOTE_REPO") && ${inner}"
|
||||
}
|
||||
|
||||
normalize_arch() {
|
||||
case "${1,,}" in
|
||||
x86_64|amd64)
|
||||
printf '%s' "x86_64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
printf '%s' "arm64"
|
||||
;;
|
||||
*)
|
||||
printf '%s' "unknown"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_remote_arch() {
|
||||
remote_exec "uname -m | tr -d '\r'"
|
||||
}
|
||||
|
||||
resolve_image() {
|
||||
if [[ -n "$RESOLVED_IMAGE" ]]; then
|
||||
printf '%s' "$RESOLVED_IMAGE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "$REDROID_IMAGE_OVERRIDE" ]]; then
|
||||
RESOLVED_IMAGE="$REDROID_IMAGE_OVERRIDE"
|
||||
RESOLVED_ARCH="custom"
|
||||
printf '%s' "$RESOLVED_IMAGE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local arch="${REDROID_ARCH_PROFILE}"
|
||||
if [[ "$arch" == "auto" ]]; then
|
||||
arch="$(normalize_arch "$(detect_remote_arch)")"
|
||||
else
|
||||
arch="$(normalize_arch "$arch")"
|
||||
fi
|
||||
|
||||
case "$arch" in
|
||||
x86_64)
|
||||
RESOLVED_IMAGE="$REDROID_IMAGE_X86"
|
||||
RESOLVED_ARCH="x86_64"
|
||||
;;
|
||||
arm64)
|
||||
RESOLVED_IMAGE="$REDROID_IMAGE_ARM64"
|
||||
RESOLVED_ARCH="arm64"
|
||||
;;
|
||||
*)
|
||||
RESOLVED_IMAGE="$REDROID_IMAGE_X86"
|
||||
RESOLVED_ARCH="x86_64"
|
||||
;;
|
||||
esac
|
||||
|
||||
printf '%s' "$RESOLVED_IMAGE"
|
||||
}
|
||||
|
||||
resolve_arch() {
|
||||
if [[ -z "$RESOLVED_IMAGE" ]]; then
|
||||
resolve_image >/dev/null
|
||||
fi
|
||||
printf '%s' "${RESOLVED_ARCH:-x86_64}"
|
||||
}
|
||||
|
||||
remote_docker_ps() {
|
||||
remote_exec "docker ps --format 'table {{.Names}}\\t{{.Image}}\\t{{.Ports}}\\t{{.Status}}'"
|
||||
}
|
||||
|
||||
remote_redroid_ps() {
|
||||
local image
|
||||
image="$(resolve_image)"
|
||||
remote_exec "docker ps --format '{{.Names}} {{.Image}} {{.Ports}} {{.Status}}' | awk -v image=$(sq "$image") '\$2 == image || index(\$2, \"redroid/redroid:\") == 1 { print }'"
|
||||
}
|
||||
|
||||
remote_redroid_names() {
|
||||
local image
|
||||
image="$(resolve_image)"
|
||||
remote_exec "docker ps --format '{{.Names}} {{.Image}}' | awk -v image=$(sq "$image") '\$2 == image || index(\$2, \"redroid/redroid:\") == 1 {print \$1}'"
|
||||
}
|
||||
|
||||
show_runtime_info() {
|
||||
local image arch
|
||||
image="$(resolve_image)"
|
||||
arch="$(resolve_arch)"
|
||||
printf 'Remote arch profile: %s\n' "$arch"
|
||||
printf 'Resolved image: %s\n' "$image"
|
||||
printf 'Device profile: %s\n' "$DEVICE_PROFILE"
|
||||
printf 'GPU mode: %s\n' "$GPU_MODE"
|
||||
printf 'ADB insecure: %s\n' "$ADB_INSECURE"
|
||||
}
|
||||
|
||||
build_image() {
|
||||
remote_repo_cmd "env -u REDROID_MAGISK_APK -u REDROID_MAGISK_URL REDROID_OFFLINE=1 REDROID_MAGISK_BOOT_COMPLETE=1 python3 redroid.py -a 13.0.0 -mtg -i -m --offline --no-compat-check"
|
||||
}
|
||||
|
||||
make_instance_name() {
|
||||
local prefix="$1"
|
||||
local index="$2"
|
||||
printf '%s-%s' "$prefix" "$index"
|
||||
}
|
||||
|
||||
make_data_dir() {
|
||||
local prefix="$1"
|
||||
local name="$2"
|
||||
printf '%s-%s' "$prefix" "$name"
|
||||
}
|
||||
|
||||
start_single_instance() {
|
||||
local name="$1"
|
||||
local port="$2"
|
||||
local data_dir="$3"
|
||||
local image arch
|
||||
|
||||
local adb_arg=""
|
||||
if [[ "$ADB_INSECURE" != "0" ]]; then
|
||||
adb_arg="--adb-insecure"
|
||||
fi
|
||||
|
||||
image="$(resolve_image)"
|
||||
arch="$(resolve_arch)"
|
||||
remote_repo_cmd "python3 run_redroid.py --image $(sq "$image") --bridge auto --device-profile $(sq "$DEVICE_PROFILE") --gpu-mode $(sq "$GPU_MODE") --data-dir $(sq "$data_dir") --port $(sq "$port") --name $(sq "$name") --replace --keep-container --wait-tcp $(sq "$WAIT_TCP") ${adb_arg}"
|
||||
bootstrap_instance "$name"
|
||||
echo "Local ADB connect:"
|
||||
echo " adb connect ${SSH_HOST}:${port}"
|
||||
echo "Resolved arch/image: ${arch} / ${image}"
|
||||
}
|
||||
|
||||
start_instance() {
|
||||
local name port data_dir
|
||||
read -r -p "Instance name [${INSTANCE_PREFIX}-1]: " name
|
||||
name="${name:-${INSTANCE_PREFIX}-1}"
|
||||
|
||||
read -r -p "ADB port [${DEFAULT_PORT}]: " port
|
||||
port="${port:-$DEFAULT_PORT}"
|
||||
|
||||
read -r -p "Data dir [${DATA_PREFIX}-${name}]: " data_dir
|
||||
data_dir="${data_dir:-${DATA_PREFIX}-${name}}"
|
||||
|
||||
start_single_instance "$name" "$port" "$data_dir"
|
||||
}
|
||||
|
||||
start_batch_instances() {
|
||||
local count start_port prefix data_prefix
|
||||
read -r -p "How many instances [${DEFAULT_COUNT}]: " count
|
||||
count="${count:-$DEFAULT_COUNT}"
|
||||
|
||||
read -r -p "Start ADB port [${DEFAULT_PORT}]: " start_port
|
||||
start_port="${start_port:-$DEFAULT_PORT}"
|
||||
|
||||
read -r -p "Name prefix [${INSTANCE_PREFIX}]: " prefix
|
||||
prefix="${prefix:-$INSTANCE_PREFIX}"
|
||||
|
||||
read -r -p "Data prefix [${DATA_PREFIX}]: " data_prefix
|
||||
data_prefix="${data_prefix:-$DATA_PREFIX}"
|
||||
|
||||
local i name port data_dir
|
||||
for ((i = 1; i <= count; i++)); do
|
||||
name="$(make_instance_name "$prefix" "$i")"
|
||||
port="$((start_port + (i - 1) * PORT_STEP))"
|
||||
data_dir="$(make_data_dir "$data_prefix" "$name")"
|
||||
echo "Starting ${name} on port ${port}..."
|
||||
start_single_instance "$name" "$port" "$data_dir"
|
||||
echo
|
||||
done
|
||||
|
||||
local open_panel
|
||||
read -r -p "Start Portainer web panel now? [Y/n]: " open_panel
|
||||
case "${open_panel:-Y}" in
|
||||
n|N|no|NO|No) ;;
|
||||
*) start_portainer ;;
|
||||
esac
|
||||
}
|
||||
|
||||
bootstrap_instance() {
|
||||
local name="${1:?instance name required}"
|
||||
remote_repo_cmd "python3 bootstrap_redroid.py --container-name $(sq "$name") --container-runtime docker --device-profile $(sq "$DEVICE_PROFILE") --proxy-host none --timeout $(sq "$BOOT_TIMEOUT") --root-timeout $(sq "$ROOT_TIMEOUT")"
|
||||
}
|
||||
|
||||
bootstrap_all_instances() {
|
||||
local names
|
||||
names="$(remote_redroid_names)"
|
||||
if [[ -z "$names" ]]; then
|
||||
echo "No running redroid containers found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local name
|
||||
while IFS= read -r name; do
|
||||
[[ -z "$name" ]] && continue
|
||||
echo "Bootstrapping ${name}..."
|
||||
bootstrap_instance "$name"
|
||||
echo
|
||||
done <<< "$names"
|
||||
}
|
||||
|
||||
check_instance() {
|
||||
local port
|
||||
read -r -p "ADB port [${DEFAULT_PORT}]: " port
|
||||
port="${port:-$DEFAULT_PORT}"
|
||||
|
||||
remote_exec "
|
||||
set +e
|
||||
adb connect 127.0.0.1:${port} >/dev/null 2>&1
|
||||
adb -s 127.0.0.1:${port} shell getprop sys.boot_completed
|
||||
adb -s 127.0.0.1:${port} shell settings get global device_provisioned
|
||||
adb -s 127.0.0.1:${port} shell settings get secure user_setup_complete
|
||||
adb -s 127.0.0.1:${port} shell settings get global http_proxy
|
||||
adb -s 127.0.0.1:${port} shell getprop ro.build.fingerprint
|
||||
adb -s 127.0.0.1:${port} shell getprop ro.product.model
|
||||
adb -s 127.0.0.1:${port} shell su root id 2>/dev/null || adb -s 127.0.0.1:${port} shell su 0 id 2>/dev/null
|
||||
adb -s 127.0.0.1:${port} shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER io.github.huskydg.magisk 2>/dev/null | tail -n 2 || true
|
||||
adb -s 127.0.0.1:${port} shell pm path io.github.huskydg.magisk 2>/dev/null || true
|
||||
adb -s 127.0.0.1:${port} shell pm list packages 2>/dev/null | grep -E 'com.google.android.gms|com.google.android.setupwizard|io.github.huskydg.magisk' | sort || true
|
||||
adb -s 127.0.0.1:${port} shell dumpsys package com.google.android.gms 2>/dev/null | grep -E 'versionName=|pkg=|enabled=' | head -n 10 || true
|
||||
"
|
||||
}
|
||||
|
||||
start_portainer() {
|
||||
remote_exec "
|
||||
docker volume create portainer_data >/dev/null
|
||||
docker rm -f portainer >/dev/null 2>&1 || true
|
||||
docker run -d \
|
||||
--name portainer \
|
||||
--restart unless-stopped \
|
||||
-p ${PORTAINER_PORT}:9000 \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v portainer_data:/data \
|
||||
portainer/portainer-ce:latest
|
||||
"
|
||||
echo "Portainer: http://${SSH_HOST}:${PORTAINER_PORT}"
|
||||
}
|
||||
|
||||
stop_instance() {
|
||||
local name
|
||||
read -r -p "Container name [redroid13-1]: " name
|
||||
name="${name:-redroid13-1}"
|
||||
remote_exec "docker rm -f $(sq "$name")"
|
||||
}
|
||||
|
||||
menu() {
|
||||
while true; do
|
||||
cat <<EOF
|
||||
redroid13 menu
|
||||
1) Build Android 13 image
|
||||
2) Start redroid instance
|
||||
3) Start multiple instances
|
||||
4) Bootstrap instance
|
||||
5) List containers
|
||||
6) Check instance health
|
||||
7) Start Portainer web panel
|
||||
8) Stop instance
|
||||
9) Bootstrap all redroid containers
|
||||
10) Show runtime info
|
||||
11) Exit
|
||||
EOF
|
||||
read -r -p "Choose: " choice
|
||||
case "$choice" in
|
||||
1) build_image ;;
|
||||
2) start_instance ;;
|
||||
3) start_batch_instances ;;
|
||||
4)
|
||||
local name
|
||||
read -r -p "Container name [${INSTANCE_PREFIX}-1]: " name
|
||||
name="${name:-${INSTANCE_PREFIX}-1}"
|
||||
bootstrap_instance "$name"
|
||||
;;
|
||||
5) remote_docker_ps ;;
|
||||
6) check_instance ;;
|
||||
7) start_portainer ;;
|
||||
8) stop_instance ;;
|
||||
9) bootstrap_all_instances ;;
|
||||
10) show_runtime_info ;;
|
||||
11) exit 0 ;;
|
||||
*) echo "Invalid choice." ;;
|
||||
esac
|
||||
echo
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
build)
|
||||
build_image
|
||||
;;
|
||||
start)
|
||||
start_instance
|
||||
;;
|
||||
batch|multi)
|
||||
start_batch_instances
|
||||
;;
|
||||
bootstrap)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Usage: $0 bootstrap <container-name>" >&2
|
||||
exit 1
|
||||
fi
|
||||
bootstrap_instance "$2"
|
||||
;;
|
||||
list)
|
||||
remote_docker_ps
|
||||
;;
|
||||
redroid-list|rlist)
|
||||
remote_redroid_ps
|
||||
;;
|
||||
info|runtime)
|
||||
show_runtime_info
|
||||
;;
|
||||
check)
|
||||
check_instance
|
||||
;;
|
||||
status)
|
||||
check_instance
|
||||
;;
|
||||
portainer)
|
||||
start_portainer
|
||||
;;
|
||||
panel|web)
|
||||
start_portainer
|
||||
;;
|
||||
stop)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Usage: $0 stop <container-name>" >&2
|
||||
exit 1
|
||||
fi
|
||||
remote_exec "docker rm -f $(sq "$2")"
|
||||
;;
|
||||
bootstrap-all|ball)
|
||||
bootstrap_all_instances
|
||||
;;
|
||||
""|menu)
|
||||
menu
|
||||
;;
|
||||
*)
|
||||
cat <<EOF
|
||||
Usage: $0 [build|start|batch|multi|bootstrap <name>|bootstrap-all|ball|list|redroid-list|check|status|portainer|info|runtime|stop <name>]
|
||||
EOF
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user