diff --git a/Dockerfile b/Dockerfile index cfdfb19..29e0701 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,8 @@ RUN mkdir /root/.android/ && \ # 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 && \ @@ -73,8 +75,8 @@ RUN chmod +x /root/start-emulator.sh EXPOSE 5554 5555 # Healthcheck to ensure the emulator is running -HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ - CMD adb devices | grep emulator-5554 || exit 1 +HEALTHCHECK --interval=10s --timeout=10s --retries=600 \ + CMD adb devices | grep emulator-5554 && test -f /data/.first-boot-done || exit 1 # Start Supervisor to manage the emulator CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/README.md b/README.md index d7e0594..f0fce8e 100644 --- a/README.md +++ b/README.md @@ -135,32 +135,38 @@ scrcpy -s localhost:5555 > **Note:** Ensure `scrcpy` is installed on your host machine. [Installation Guide](https://github.com/Genymobile/scrcpy#installation) -### Customizing Device Screen +## ⚙️ **Environment Variables** -The emulator's display can be adjusted with environment variables: +| Variable | Description | Default | +| --- | --- | --- | +| `DNS` | Private DNS server used inside the emulator | `one.one.one.one` | +| `RAM_SIZE` | RAM in megabytes allocated to the emulator | `4096` | +| `SCREEN_RESOLUTION` | Screen size in `WIDTHxHEIGHT` format (e.g. `1080x1920`) | device default | +| `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` | -- `SCREEN_RESOLUTION` (optional): sets the screen size in `WIDTHxHEIGHT` format. -- `SCREEN_DENSITY` (optional): overrides the device pixel density in DPI. - -Configure these variables in your `docker-compose.yml` file (the provided example contains commented entries for reference). If `SCREEN_RESOLUTION` or `SCREEN_DENSITY` are omitted, the emulator uses its default settings. ## 🔄 **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) -2. **Installing PICO GAPPS:** Adds essential Google services to the emulator -3. **Rooting the Device:** Performs multiple reboots to: +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 - Remount system as writable - - Install root access via the rootAVD script - - Install PICO GAPPS components - - Configure optimal device settings + - Install Magisk for root access + - Reboot to apply root +4. **Extras Copied:** Pushes everything from the `extras` directory to `/sdcard/Download` so files like APKs or Magisk modules are ready for manual installation on the device. +5. **Configuring optimal device settings** + +`ROOT_SETUP` and `GAPPS_SETUP` are checked on every start. If you enable them after the first boot, the script installs the requested components once and marks them complete so they won't run again. Removing them later requires recreating the data volume. > **Important:** The first boot can take 10-15 minutes to complete. You'll know the process is finished when you see the following log output: > ``` > Broadcast completed: result=0 -> Sucess !! +> Success !! > 2025-04-22 13:45:18,724 INFO exited: first-boot (exit status 0; expected) > ``` @@ -196,7 +202,7 @@ This includes: - [ ] Support for additional Android versions - [x] Integration with CI/CD pipelines - [ ] Support ARM64 CPU architecture -- [x] Preinstall PICO GAPPS +- [x] PICO GAPPS installation - [x] Support Magisk - [x] Adding web interface of [scrcpy](https://github.com/Shmayro/ws-scrcpy-docker) - [x] Redirect all logs to container stdout/stderr @@ -217,8 +223,8 @@ This includes: - **First Boot Taking Too Long:** - This is normal, as the first boot process needs to perform several operations including: - - Installing GAPPS - - Rooting the device + - Installing GAPPS (if enabled) + - Rooting the device (if enabled) - Configuring system settings - The process can take 10-15 minutes depending on your system performance - You can monitor progress with `docker logs -f dockerify-android` diff --git a/docker-compose.yml b/docker-compose.yml index d505122..1dd05c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,12 +11,14 @@ services: - ./data:/data - ./extras:/extras environment: - - DNS=one.one.one.one - - RAM_SIZE=8192 + DNS: one.one.one.one + # RAM_SIZE: 4096 # Optional screen resolution in WIDTHxHEIGHT format - #- SCREEN_RESOLUTION=720x720 + #SCREEN_RESOLUTION: 720x720 # Optional screen density (dpi) - #- SCREEN_DENSITY=227 + #SCREEN_DENSITY: 227 + ROOT_SETUP: 0 # set to 1 to enable rooting + GAPPS_SETUP: 0 # set to 1 to install PICO GAPPS privileged: true devices: - /dev/kvm @@ -36,3 +38,4 @@ services: adb connect dockerify-android:5555 && npm start " + diff --git a/first-boot.sh b/first-boot.sh old mode 100644 new mode 100755 index da825d8..20aafe0 --- a/first-boot.sh +++ b/first-boot.sh @@ -1,6 +1,12 @@ #!/bin/bash -# apply settings +bool_true() { + case "${1,,}" in + 1|true|yes) return 0 ;; + *) return 1 ;; + esac +} + apply_settings() { adb wait-for-device # Waiting for the boot sequence to be completed. @@ -24,67 +30,84 @@ apply_settings() { adb shell svc wifi enable } -# Detect ip and forward ADB ports from the container's network -# interface to localhost. +prepare_system() { + adb wait-for-device + adb root + adb shell avbctl disable-verification + adb disable-verity + adb reboot + adb wait-for-device + adb root + adb remount +} + +install_gapps() { + prepare_system + echo "Installing GAPPS ..." + wget https://netcologne.dl.sourceforge.net/project/opengapps/x86_64/20220503/open_gapps-x86_64-11.0-pico-20220503.zip?viasf=1 -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 + 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 + touch /data/.gapps-done +} + +install_root() { + adb wait-for-device + echo "Root Script Starting..." + # Root the AVD by patching the ramdisk. + 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 + popd + echo "Root Done" + sleep 10 + rm -r rootAVD + touch /data/.root-done +} + +copy_extras() { + adb wait-for-device + # Push any Magisk modules for manual installation later + for f in $(ls /extras/*); do + adb push $f /sdcard/Download/ + done +} + +# Detect the container's IP and forward ADB to localhost. LOCAL_IP=$(ip addr list eth0 | grep "inet " | cut -d' ' -f6 | cut -d/ -f1) socat tcp-listen:"5555",bind="$LOCAL_IP",fork tcp:127.0.0.1:"5555" & -echo "Emulator is healthy. Proceeding..." +gapps_needed=false +root_needed=false +if bool_true "$GAPPS_SETUP" && [ ! -f /data/.gapps-done ]; then gapps_needed=true; fi +if bool_true "$ROOT_SETUP" && [ ! -f /data/.root-done ]; then root_needed=true; fi -# Check if the script has already run +# Skip initialization if first boot already completed. if [ -f /data/.first-boot-done ]; then + [ "$gapps_needed" = true ] && install_gapps && [ "$root_needed" = false ] && adb reboot + [ "$root_needed" = true ] && install_root apply_settings + copy_extras exit 0 fi -echo "Init ADV ..." - +echo "Init AVD ..." echo "no" | avdmanager create avd -n android -k "system-images;android-30;default;x86_64" -echo "Preparation ..." - -adb wait-for-device -adb root -adb shell avbctl disable-verification -adb disable-verity -adb reboot -adb wait-for-device -adb root -adb remount -for f in $(ls /extras/*); do - adb push $f /sdcard/Download/ -done - -echo "Installing GAPPS ..." - -wget https://netcologne.dl.sourceforge.net/project/opengapps/x86_64/20220503/open_gapps-x86_64-11.0-pico-20220503.zip?viasf=1 -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 $(ls gapps-11/Core/*.tar); do - tar -x --strip-components 2 -f $f -C gapps-11 -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 - -echo "Root Script Starting..." - -# Root the VM -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 -popd -echo "Root Done" -sleep 15 -echo "Cleanup ..." -# done -rm -r gapps-11 -rm -r rootAVD +[ "$gapps_needed" = true ] && install_gapps && [ "$root_needed" = false ] && adb reboot +[ "$root_needed" = true ] && install_root apply_settings +copy_extras + touch /data/.first-boot-done -echo "Sucess !!" \ No newline at end of file +echo "Success !!" diff --git a/start-emulator.sh b/start-emulator.sh old mode 100644 new mode 100755 index 11db5f3..3411601 --- a/start-emulator.sh +++ b/start-emulator.sh @@ -1,7 +1,10 @@ #!/bin/bash -# Check if the .first-boot-done file exists -if [ -f /data/.first-boot-done ]; then +# Kill any running emulator instances before starting a new one +pkill -f "/opt/android-sdk/emulator/emulator" + +# Use custom ramdisk if present +if [ -f /data/android.avd/ramdisk.img ]; then RAMDISK="-ramdisk /data/android.avd/ramdisk.img" fi @@ -13,5 +16,13 @@ if [ -n "$SCREEN_DENSITY" ]; then SCREEN_DENSITY_FLAG="-dpi-device $SCREEN_DENSITY" fi +# Configure optional screen resolution and density +if [ -n "$SCREEN_RESOLUTION" ]; then + SCREEN_RESOLUTION_FLAG="-skin $SCREEN_RESOLUTION" +fi +if [ -n "$SCREEN_DENSITY" ]; then + SCREEN_DENSITY_FLAG="-dpi-device $SCREEN_DENSITY" +fi + # Start the emulator with the appropriate ramdisk.img -/opt/android-sdk/emulator/emulator -avd android -nojni -netfast -writable-system -no-window -no-audio -no-boot-anim -skip-adb-auth -gpu swiftshader_indirect -no-snapshot -no-metrics $SCREEN_RESOLUTION_FLAG $SCREEN_DENSITY_FLAG $RAMDISK -qemu -m "${RAM_SIZE:-4096}" +/opt/android-sdk/emulator/emulator -avd android -nojni -netfast -writable-system -no-window -no-audio -no-boot-anim -skip-adb-auth -gpu swiftshader_indirect -no-snapshot -no-metrics $SCREEN_RESOLUTION_FLAG $SCREEN_DENSITY_FLAG $RAMDISK -qemu -m ${RAM_SIZE:-4096}