#!/bin/sh set -eu usage() { cat <<'EOF' Usage: sh android-plugin/run-avd-ci.sh create \ --api-level N \ --target TARGET \ --arch ARCH \ --profile PROFILE \ --avd-name NAME sh android-plugin/run-avd-ci.sh start \ --avd-name NAME \ --gpu GPU \ --emulator-log FILE \ --pid-file FILE \ --boot-timeout SECONDS sh android-plugin/run-avd-ci.sh stop \ --avd-name NAME \ --emulator-log FILE \ --pid-file FILE EOF } die() { echo "run-avd-ci.sh: $*" >&2 usage >&2 exit 2 } next_arg() { if [ "$#" -lt 2 ]; then die "$1 requires a value" fi printf '%s' "$2" } require_value() { value="$1" name="$2" if [ -z "${value}" ]; then die "missing ${name}" fi } find_android_tool() { tool="$1" fallback="$2" if command -v "${tool}" >/dev/null 2>&1; then command -v "${tool}" return 0 fi sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" if [ -n "${sdk_root}" ] && [ -x "${sdk_root}/${fallback}" ]; then printf '%s\n' "${sdk_root}/${fallback}" return 0 fi die "unable to find Android SDK tool: ${tool}" } ensure_android_avd_home() { if [ -z "${ANDROID_AVD_HOME:-}" ]; then export ANDROID_AVD_HOME="${RUNNER_TEMP:-${PWD}}/mobilegl-avd" fi mkdir -p "${ANDROID_AVD_HOME}" } print_diagnostics() { emulator_log="$1" adb_bin="$(find_android_tool adb platform-tools/adb)" "${adb_bin}" devices -l >&2 || true if [ -f "${emulator_log}" ]; then echo "----- emulator log -----" >&2 cat "${emulator_log}" >&2 || true echo "----- end emulator log -----" >&2 fi } create_avd() { api_level="" target="" arch="" profile="" avd_name="" while [ "$#" -gt 0 ]; do case "$1" in --api-level) api_level="$(next_arg "$@")"; shift 2 ;; --target) target="$(next_arg "$@")"; shift 2 ;; --arch) arch="$(next_arg "$@")"; shift 2 ;; --profile) profile="$(next_arg "$@")"; shift 2 ;; --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; -h|--help) usage; exit 0 ;; *) die "unknown create argument: $1" ;; esac done require_value "${api_level}" "--api-level" require_value "${target}" "--target" require_value "${arch}" "--arch" require_value "${profile}" "--profile" require_value "${avd_name}" "--avd-name" sdkmanager_bin="$(find_android_tool sdkmanager cmdline-tools/latest/bin/sdkmanager)" avdmanager_bin="$(find_android_tool avdmanager cmdline-tools/latest/bin/avdmanager)" ensure_android_avd_home system_image="system-images;android-${api_level};${target};${arch}" yes | "${sdkmanager_bin}" --licenses >/dev/null yes | "${sdkmanager_bin}" "platform-tools" "emulator" "platforms;android-${api_level}" "${system_image}" echo no | "${avdmanager_bin}" create avd --force --name "${avd_name}" --package "${system_image}" --device "${profile}" } start_avd() { avd_name="" gpu="" emulator_log="" pid_file="" boot_timeout="" while [ "$#" -gt 0 ]; do case "$1" in --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; --gpu) gpu="$(next_arg "$@")"; shift 2 ;; --emulator-log) emulator_log="$(next_arg "$@")"; shift 2 ;; --pid-file) pid_file="$(next_arg "$@")"; shift 2 ;; --boot-timeout) boot_timeout="$(next_arg "$@")"; shift 2 ;; -h|--help) usage; exit 0 ;; *) die "unknown start argument: $1" ;; esac done require_value "${avd_name}" "--avd-name" require_value "${gpu}" "--gpu" require_value "${emulator_log}" "--emulator-log" require_value "${pid_file}" "--pid-file" require_value "${boot_timeout}" "--boot-timeout" adb_bin="$(find_android_tool adb platform-tools/adb)" emulator_bin="$(find_android_tool emulator emulator/emulator)" ensure_android_avd_home mkdir -p "$(dirname "${emulator_log}")" "$(dirname "${pid_file}")" "${emulator_bin}" -avd "${avd_name}" \ -no-window \ -gpu "${gpu}" \ -no-snapshot \ -noaudio \ -no-boot-anim \ -camera-back none \ -camera-front none \ > "${emulator_log}" 2>&1 & emulator_pid="$!" echo "${emulator_pid}" > "${pid_file}" if ! timeout "${boot_timeout}" "${adb_bin}" wait-for-device; then print_diagnostics "${emulator_log}" die "emulator did not connect to adb within ${boot_timeout}s" fi booted="" for _ in $(seq 1 "${boot_timeout}"); do booted="$("${adb_bin}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)" if [ "${booted}" = "1" ]; then break fi sleep 1 done if [ "${booted}" != "1" ]; then print_diagnostics "${emulator_log}" die "emulator did not boot within ${boot_timeout}s" fi "${adb_bin}" shell settings put global window_animation_scale 0 "${adb_bin}" shell settings put global transition_animation_scale 0 "${adb_bin}" shell settings put global animator_duration_scale 0 "${adb_bin}" shell input keyevent 82 || true } stop_avd() { avd_name="" emulator_log="" pid_file="" while [ "$#" -gt 0 ]; do case "$1" in --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; --emulator-log) emulator_log="$(next_arg "$@")"; shift 2 ;; --pid-file) pid_file="$(next_arg "$@")"; shift 2 ;; -h|--help) usage; exit 0 ;; *) die "unknown stop argument: $1" ;; esac done require_value "${avd_name}" "--avd-name" require_value "${emulator_log}" "--emulator-log" require_value "${pid_file}" "--pid-file" adb_bin="$(find_android_tool adb platform-tools/adb)" ensure_android_avd_home "${adb_bin}" emu kill >/dev/null 2>&1 || true if [ -f "${pid_file}" ]; then emulator_pid="$(cat "${pid_file}")" if [ -n "${emulator_pid}" ] && kill -0 "${emulator_pid}" 2>/dev/null; then kill "${emulator_pid}" >/dev/null 2>&1 || true fi fi } if [ "$#" -eq 0 ]; then die "missing command" fi command="$1" shift case "${command}" in create) create_avd "$@" ;; start) start_avd "$@" ;; stop) stop_avd "$@" ;; -h|--help) usage; exit 0 ;; *) die "unknown command: ${command}" ;; esac