Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ANDROID_VERSION=30
GAPPS_VERSION=11
DNS=one.one.one.one
RAM_SIZE=4096
SCREEN_RESOLUTION=1080x1920
SCREEN_DENSITY=320
ROOT_SETUP=0
GAPPS_SETUP=0
74 changes: 32 additions & 42 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
FROM ubuntu:20.04
ARG ANDROID_VERSION=30
ENV ANDROID_VERSION=${ANDROID_VERSION}

# Install necessary packages
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libegl1 \
openjdk-17-jdk-headless \
wget \
curl \
git \
lzip \
xz-utils \
unzip \
supervisor \
qemu-kvm \
iproute2 \
socat \
tzdata && \
tzdata \
squashfs-tools \
procps && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Set up Android SDK
RUN curl -s "https://dl.google.com/android/repository/repository2-1.xml" | \
grep -oP 'emulator-linux_x64-\d+\.zip' | head -1 | \
awk '{print "https://dl.google.com/android/repository/" $0}' | \
wget -q -O /tmp/emulator.zip - || true && \
if [ -s /tmp/emulator.zip ] && [ -f /tmp/emulator.zip ]; then \
unzip -q /tmp/emulator.zip -d /opt/android-sdk && rm /tmp/emulator.zip; \
fi

RUN mkdir -p /opt/android-sdk/cmdline-tools && \
cd /opt/android-sdk/cmdline-tools && \
wget https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip -O cmdline-tools.zip && \
Expand All @@ -29,61 +39,41 @@ RUN mkdir -p /opt/android-sdk/cmdline-tools && \

ENV ANDROID_HOME=/opt/android-sdk
ENV ANDROID_AVD_HOME=/data
ENV ADB_DIR="$ANDROID_HOME/platform-tools"
ENV PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ADB_DIR:$PATH"
ENV PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH:$ANDROID_HOME/platform-tools"

# Initializing the required directories.
RUN mkdir /root/.android/ && \
touch /root/.android/repositories.cfg && \
mkdir /data && \
touch /root/.android/repositories.cfg && \
mkdir /data && \
mkdir /extras

# Copy emulator.zip
#COPY emulator.zip /root/emulator.zip
#COPY emulator/package.xml /root/package.xml
RUN export ANDROID_VERSION=$ANDROID_VERSION && \
yes | sdkmanager --sdk_root=$ANDROID_HOME "platform-tools" && \
if [ "$ANDROID_VERSION" -ge 35 ]; then \
yes | sdkmanager --sdk_root=$ANDROID_HOME "platforms;android-${ANDROID_VERSION}" "system-images;android-${ANDROID_VERSION};google_apis;x86_64" || true; \
else \
yes | sdkmanager --sdk_root=$ANDROID_HOME "emulator" "platforms;android-${ANDROID_VERSION}" "system-images;android-${ANDROID_VERSION};default;x86_64"; \
fi && \
yes | sdkmanager --sdk_root=$ANDROID_HOME "emulator" || true

RUN if [ ! -f "$ANDROID_HOME/platform-tools/adb" ]; then \
echo "ERROR: adb not found at $ANDROID_HOME/platform-tools/adb"; \
ls -la $ANDROID_HOME/; \
exit 1; \
fi

# Detect architecture and set environment variable
RUN yes | sdkmanager --sdk_root=$ANDROID_HOME "emulator" "platform-tools" "platforms;android-30" "system-images;android-30;default;x86_64"
# remove /opt/android-sdk/emulator/crashpad_handler
RUN rm -f /opt/android-sdk/emulator/crashpad_handler
# RUN if [ "$(uname -m)" = "aarch64" ]; then \
# unzip /root/emulator.zip -d $ANDROID_HOME && \
# mv /root/package.xml $ANDROID_HOME/emulator/package.xml && \
# rm /root/emulator.zip && \
# yes | sdkmanager --sdk_root=$ANDROID_HOME "platform-tools" "platforms;android-29" "system-images;android-29;default;arm64-v8a" && \
# echo "no" | avdmanager create avd -n test -k "system-images;android-29;default;arm64-v8a"; \
# else \
# yes | sdkmanager --sdk_root=$ANDROID_HOME "emulator" "platform-tools" "platforms;android-29" "system-images;android-29;default;x86_64" && \
# echo "no" | avdmanager create avd -n test -k "system-images;android-29;default;x86_64"; \
# fi

# Copy supervisor config
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Copy the rootAVD repository
#COPY rootAVD /root/rootAVD

# Copy the first-boot script
COPY first-boot.sh /root/first-boot.sh
RUN chmod +x /root/first-boot.sh

# Copy the start-emulator script
COPY start-emulator.sh /root/start-emulator.sh
RUN chmod +x /root/start-emulator.sh

# Expose necessary ports
EXPOSE 5554 5555

# Healthcheck to ensure the emulator is running
HEALTHCHECK --interval=10s --timeout=10s --retries=600 \
CMD adb devices | grep emulator-5554 && test -f /data/.first-boot-done || exit 1
HEALTHCHECK --interval=30s --timeout=10s --retries=60 \
CMD ps aux | grep -v grep | grep -q emulator || test -f /data/.first-boot-done

# Start Supervisor to manage the emulator
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

# docker build -t dockerify-android .
# docker run -d --name dockerify-android --device /dev/kvm --privileged -p 5555:5555 dockerify-android
# docker run -d --name dockerify-android --device /dev/kvm --privileged -p 5555:5555 shmayro/dockerify-android
# docker exec -it dockerify-android tail -f /var/log/supervisor/emulator.out
# docker exec -it dockerify-android tail -f /var/log/supervisor/first-boot.out.log
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,15 @@ scrcpy -s localhost:5555
| `SCREEN_DENSITY` | Screen pixel density in DPI | device default |
| `ROOT_SETUP` | Set to `1` to enable rooting and Magisk. Can be turned on after the first start but cannot be undone without recreating the data volume. | `0` |
| `GAPPS_SETUP` | Set to `1` to install PICO GAPPS. Can be turned on after the first start but cannot be undone without recreating the data volume. | `0` |
| `ANDROID_VERSION` | Android API version to emulate (30 = Android 11, 31 = Android 12, 33 = Android 13, 34 = Android 14, 35 = Android 15, 36 = Android 16). Also enables multi-version support. | `30` |
| `GAPPS_VERSION` | GAPPS version to download. Default behavior derives from ANDROID_VERSION. | `11` |


## 🔄 **First Boot Process**

The first time you start the container, it will perform a comprehensive setup process that includes:

1. **AVD Creation:** Creates a new Android Virtual Device running Android 30 (Android 11)
1. **AVD Creation:** Creates a new Android Virtual Device using the Android version configured via ANDROID_VERSION (default 30 – Android 11). This value selects the platform image (e.g., android-${ANDROID_VERSION}) for the AVD and SDK downloads.
2. **PICO GAPPS Installation** (when `GAPPS_SETUP=1`): Adds essential Google services.
3. **Rooting the Device** (when `ROOT_SETUP=1`): Performs multiple reboots to:
- Disable AVB verification
Expand Down
10 changes: 9 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ services:
image: shmayro/dockerify-android:latest
build:
context: .
env_file:
- .env
ports:
- "5555:5555"
volumes:
- ./data:/data
- ./extras:/extras
environment:
ANDROID_VERSION: 30
DNS: one.one.one.one
# RAM_SIZE: 4096
# Optional screen resolution in WIDTHxHEIGHT format
Expand All @@ -22,6 +25,12 @@ services:
privileged: true
devices:
- /dev/kvm
healthcheck:
test: ["CMD", "ps", "aux"]
interval: 30s
timeout: 10s
retries: 60
start_period: 60s

scrcpy-web:
container_name: scrcpy-web
Expand All @@ -38,4 +47,3 @@ services:
adb connect dockerify-android:5555 &&
npm start
"

64 changes: 49 additions & 15 deletions first-boot.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#!/bin/bash

# Android version and GAPPS version handling (defaults kept backward compatible)
ANDROID_VERSION=${ANDROID_VERSION:-30}
GAPPS_VERSION=${GAPPS_VERSION:-11}

bool_true() {
case "${1,,}" in
1|true|yes) return 0 ;;
Expand Down Expand Up @@ -44,30 +48,60 @@ prepare_system() {
install_gapps() {
prepare_system
echo "Installing GAPPS ..."
wget https://sourceforge.net/projects/opengapps/files/x86_64/20220503/open_gapps-x86_64-11.0-pico-20220503.zip/download -O gapps-11.zip
unzip gapps-11.zip 'Core/*' -d gapps-11 && rm gapps-11.zip
rm gapps-11/Core/setup*
lzip -d gapps-11/Core/*.lz
for f in gapps-11/Core/*.tar; do
tar -x --strip-components 2 -f "$f" -C gapps-11
# Determine GAPPS date and API tag based on Android version.
case "$ANDROID_VERSION" in
30)
GAPPS_DATE_FROM_VER="20220503"; GAPPS_API_TAG="11.0"; GAPPS_DIR="gapps-11" ;;
31)
GAPPS_DATE_FROM_VER="20230105"; GAPPS_API_TAG="12.0"; GAPPS_DIR="gapps-12" ;;
33)
GAPPS_DATE_FROM_VER="20220824"; GAPPS_API_TAG="13.0"; GAPPS_DIR="gapps-13" ;;
34)
GAPPS_DATE_FROM_VER="20230601"; GAPPS_API_TAG="14.0"; GAPPS_DIR="gapps-14" ;;
35)
GAPPS_DATE_FROM_VER="20240415"; GAPPS_API_TAG="15.0"; GAPPS_DIR="gapps-15" ;;
36)
GAPPS_DATE_FROM_VER="20250105"; GAPPS_API_TAG="16.0"; GAPPS_DIR="gapps-16" ;;
*)
GAPPS_DATE_FROM_VER="20220503"; GAPPS_API_TAG="11.0"; GAPPS_DIR="gapps-11" ;;
esac

# Allow override via GAPPS_VERSION, but keep defaults aligned with ANDROID_VERSION.
if [[ "$GAPPS_VERSION" =~ ^[0-9]+$ ]]; then
if [ "$GAPPS_VERSION" -ge 12 ]; then
GAPPS_API_TAG="12.0"; GAPPS_DATE_FROM_VER="20230105"; GAPPS_DIR="gapps-12"
fi
fi

OPEN_GAPPS_ARCHIVE="open_gapps-x86_64-${GAPPS_API_TAG}-pico-${GAPPS_DATE_FROM_VER}.zip"
GAPPS_URL="https://sourceforge.net/projects/opengapps/files/x86_64/${GAPPS_DATE_FROM_VER}/${OPEN_GAPPS_ARCHIVE}/download"
ZIP_FILE="gapps-${GAPPS_API_TAG%%.*}.zip"

wget "$GAPPS_URL" -O "$ZIP_FILE"
unzip "$ZIP_FILE" 'Core/*' -d "$GAPPS_DIR" && rm "$ZIP_FILE"
rm -f "$GAPPS_DIR/Core/setup*" || true
lzip -d "$GAPPS_DIR/Core/*.lz" || true
for f in "$GAPPS_DIR/Core/*.tar"; do
tar -x --strip-components 2 -f "$f" -C "$GAPPS_DIR"
done
adb push gapps-11/etc /system
adb push gapps-11/framework /system
adb push gapps-11/app /system
adb push gapps-11/priv-app /system
rm -r gapps-11
adb push "$GAPPS_DIR/etc" /system
adb push "$GAPPS_DIR/framework" /system
adb push "$GAPPS_DIR/app" /system
adb push "$GAPPS_DIR/priv-app" /system
rm -r "$GAPPS_DIR" || true
touch /data/.gapps-done
}

install_root() {
adb wait-for-device
echo "Root Script Starting..."
# Root the AVD by patching the ramdisk.
# Root the AVD by patching the ramdisk for the configured Android version.
git clone https://gitlab.com/newbit/rootAVD.git
pushd rootAVD
sed -i 's/read -t 10 choice/choice=1/' rootAVD.sh
./rootAVD.sh system-images/android-30/default/x86_64/ramdisk.img
cp /opt/android-sdk/system-images/android-30/default/x86_64/ramdisk.img /data/android.avd/ramdisk.img
# Use the dynamic Android version for ramdisk patching
./rootAVD.sh system-images/android-${ANDROID_VERSION}/default/x86_64/ramdisk.img
cp /opt/android-sdk/system-images/android-${ANDROID_VERSION}/default/x86_64/ramdisk.img /data/android.avd/ramdisk.img
popd
echo "Root Done"
sleep 10
Expand Down Expand Up @@ -102,7 +136,7 @@ if [ -f /data/.first-boot-done ]; then
fi

echo "Init AVD ..."
echo "no" | avdmanager create avd -n android -k "system-images;android-30;default;x86_64"
echo "no" | avdmanager create avd -n android -k "system-images;android-${ANDROID_VERSION};default;x86_64"

[ "$gapps_needed" = true ] && install_gapps && [ "$root_needed" = false ] && adb reboot
[ "$root_needed" = true ] && install_root
Expand Down