Skip to content

Commit d2f890d

Browse files
authored
feat(emulator): make root and gapps setup optional (#23)
* fix: remove unnecessary crashpad_handler and improve healthcheck
1 parent 170eccd commit d2f890d

File tree

5 files changed

+121
-76
lines changed

5 files changed

+121
-76
lines changed

Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ RUN mkdir /root/.android/ && \
4444

4545
# Detect architecture and set environment variable
4646
RUN yes | sdkmanager --sdk_root=$ANDROID_HOME "emulator" "platform-tools" "platforms;android-30" "system-images;android-30;default;x86_64"
47+
# remove /opt/android-sdk/emulator/crashpad_handler
48+
RUN rm -f /opt/android-sdk/emulator/crashpad_handler
4749
# RUN if [ "$(uname -m)" = "aarch64" ]; then \
4850
# unzip /root/emulator.zip -d $ANDROID_HOME && \
4951
# mv /root/package.xml $ANDROID_HOME/emulator/package.xml && \
@@ -73,8 +75,8 @@ RUN chmod +x /root/start-emulator.sh
7375
EXPOSE 5554 5555
7476

7577
# Healthcheck to ensure the emulator is running
76-
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
77-
CMD adb devices | grep emulator-5554 || exit 1
78+
HEALTHCHECK --interval=10s --timeout=10s --retries=600 \
79+
CMD adb devices | grep emulator-5554 && test -f /data/.first-boot-done || exit 1
7880

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

README.md

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -135,32 +135,38 @@ scrcpy -s localhost:5555
135135

136136
> **Note:** Ensure `scrcpy` is installed on your host machine. [Installation Guide](https://github.com/Genymobile/scrcpy#installation)
137137
138-
### Customizing Device Screen
138+
## ⚙️ **Environment Variables**
139139

140-
The emulator's display can be adjusted with environment variables:
140+
| Variable | Description | Default |
141+
| --- | --- | --- |
142+
| `DNS` | Private DNS server used inside the emulator | `one.one.one.one` |
143+
| `RAM_SIZE` | RAM in megabytes allocated to the emulator | `4096` |
144+
| `SCREEN_RESOLUTION` | Screen size in `WIDTHxHEIGHT` format (e.g. `1080x1920`) | device default |
145+
| `SCREEN_DENSITY` | Screen pixel density in DPI | device default |
146+
| `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` |
147+
| `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` |
141148

142-
- `SCREEN_RESOLUTION` (optional): sets the screen size in `WIDTHxHEIGHT` format.
143-
- `SCREEN_DENSITY` (optional): overrides the device pixel density in DPI.
144-
145-
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.
146149

147150
## 🔄 **First Boot Process**
148151

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

151154
1. **AVD Creation:** Creates a new Android Virtual Device running Android 30 (Android 11)
152-
2. **Installing PICO GAPPS:** Adds essential Google services to the emulator
153-
3. **Rooting the Device:** Performs multiple reboots to:
155+
2. **PICO GAPPS Installation** (when `GAPPS_SETUP=1`): Adds essential Google services.
156+
3. **Rooting the Device** (when `ROOT_SETUP=1`): Performs multiple reboots to:
154157
- Disable AVB verification
155158
- Remount system as writable
156-
- Install root access via the rootAVD script
157-
- Install PICO GAPPS components
158-
- Configure optimal device settings
159+
- Install Magisk for root access
160+
- Reboot to apply root
161+
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.
162+
5. **Configuring optimal device settings**
163+
164+
`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.
159165

160166
> **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:
161167
> ```
162168
> Broadcast completed: result=0
163-
> Sucess !!
169+
> Success !!
164170
> 2025-04-22 13:45:18,724 INFO exited: first-boot (exit status 0; expected)
165171
> ```
166172
@@ -196,7 +202,7 @@ This includes:
196202
- [ ] Support for additional Android versions
197203
- [x] Integration with CI/CD pipelines
198204
- [ ] Support ARM64 CPU architecture
199-
- [x] Preinstall PICO GAPPS
205+
- [x] PICO GAPPS installation
200206
- [x] Support Magisk
201207
- [x] Adding web interface of [scrcpy](https://github.com/Shmayro/ws-scrcpy-docker)
202208
- [x] Redirect all logs to container stdout/stderr
@@ -217,8 +223,8 @@ This includes:
217223

218224
- **First Boot Taking Too Long:**
219225
- This is normal, as the first boot process needs to perform several operations including:
220-
- Installing GAPPS
221-
- Rooting the device
226+
- Installing GAPPS (if enabled)
227+
- Rooting the device (if enabled)
222228
- Configuring system settings
223229
- The process can take 10-15 minutes depending on your system performance
224230
- You can monitor progress with `docker logs -f dockerify-android`

docker-compose.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ services:
1111
- ./data:/data
1212
- ./extras:/extras
1313
environment:
14-
- DNS=one.one.one.one
15-
- RAM_SIZE=8192
14+
DNS: one.one.one.one
15+
# RAM_SIZE: 4096
1616
# Optional screen resolution in WIDTHxHEIGHT format
17-
#- SCREEN_RESOLUTION=720x720
17+
#SCREEN_RESOLUTION: 720x720
1818
# Optional screen density (dpi)
19-
#- SCREEN_DENSITY=227
19+
#SCREEN_DENSITY: 227
20+
ROOT_SETUP: 0 # set to 1 to enable rooting
21+
GAPPS_SETUP: 0 # set to 1 to install PICO GAPPS
2022
privileged: true
2123
devices:
2224
- /dev/kvm
@@ -36,3 +38,4 @@ services:
3638
adb connect dockerify-android:5555 &&
3739
npm start
3840
"
41+

first-boot.sh

100644100755
Lines changed: 75 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#!/bin/bash
22

3-
# apply settings
3+
bool_true() {
4+
case "${1,,}" in
5+
1|true|yes) return 0 ;;
6+
*) return 1 ;;
7+
esac
8+
}
9+
410
apply_settings() {
511
adb wait-for-device
612
# Waiting for the boot sequence to be completed.
@@ -24,67 +30,84 @@ apply_settings() {
2430
adb shell svc wifi enable
2531
}
2632

27-
# Detect ip and forward ADB ports from the container's network
28-
# interface to localhost.
33+
prepare_system() {
34+
adb wait-for-device
35+
adb root
36+
adb shell avbctl disable-verification
37+
adb disable-verity
38+
adb reboot
39+
adb wait-for-device
40+
adb root
41+
adb remount
42+
}
43+
44+
install_gapps() {
45+
prepare_system
46+
echo "Installing GAPPS ..."
47+
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
48+
unzip gapps-11.zip 'Core/*' -d gapps-11 && rm gapps-11.zip
49+
rm gapps-11/Core/setup*
50+
lzip -d gapps-11/Core/*.lz
51+
for f in gapps-11/Core/*.tar; do
52+
tar -x --strip-components 2 -f "$f" -C gapps-11
53+
done
54+
adb push gapps-11/etc /system
55+
adb push gapps-11/framework /system
56+
adb push gapps-11/app /system
57+
adb push gapps-11/priv-app /system
58+
rm -r gapps-11
59+
touch /data/.gapps-done
60+
}
61+
62+
install_root() {
63+
adb wait-for-device
64+
echo "Root Script Starting..."
65+
# Root the AVD by patching the ramdisk.
66+
git clone https://gitlab.com/newbit/rootAVD.git
67+
pushd rootAVD
68+
sed -i 's/read -t 10 choice/choice=1/' rootAVD.sh
69+
./rootAVD.sh system-images/android-30/default/x86_64/ramdisk.img
70+
cp /opt/android-sdk/system-images/android-30/default/x86_64/ramdisk.img /data/android.avd/ramdisk.img
71+
popd
72+
echo "Root Done"
73+
sleep 10
74+
rm -r rootAVD
75+
touch /data/.root-done
76+
}
77+
78+
copy_extras() {
79+
adb wait-for-device
80+
# Push any Magisk modules for manual installation later
81+
for f in $(ls /extras/*); do
82+
adb push $f /sdcard/Download/
83+
done
84+
}
85+
86+
# Detect the container's IP and forward ADB to localhost.
2987
LOCAL_IP=$(ip addr list eth0 | grep "inet " | cut -d' ' -f6 | cut -d/ -f1)
3088
socat tcp-listen:"5555",bind="$LOCAL_IP",fork tcp:127.0.0.1:"5555" &
3189

32-
echo "Emulator is healthy. Proceeding..."
90+
gapps_needed=false
91+
root_needed=false
92+
if bool_true "$GAPPS_SETUP" && [ ! -f /data/.gapps-done ]; then gapps_needed=true; fi
93+
if bool_true "$ROOT_SETUP" && [ ! -f /data/.root-done ]; then root_needed=true; fi
3394

34-
# Check if the script has already run
95+
# Skip initialization if first boot already completed.
3596
if [ -f /data/.first-boot-done ]; then
97+
[ "$gapps_needed" = true ] && install_gapps && [ "$root_needed" = false ] && adb reboot
98+
[ "$root_needed" = true ] && install_root
3699
apply_settings
100+
copy_extras
37101
exit 0
38102
fi
39103

40-
echo "Init ADV ..."
41-
104+
echo "Init AVD ..."
42105
echo "no" | avdmanager create avd -n android -k "system-images;android-30;default;x86_64"
43106

44-
echo "Preparation ..."
45-
46-
adb wait-for-device
47-
adb root
48-
adb shell avbctl disable-verification
49-
adb disable-verity
50-
adb reboot
51-
adb wait-for-device
52-
adb root
53-
adb remount
54-
for f in $(ls /extras/*); do
55-
adb push $f /sdcard/Download/
56-
done
57-
58-
echo "Installing GAPPS ..."
59-
60-
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
61-
unzip gapps-11.zip 'Core/*' -d gapps-11 && rm gapps-11.zip
62-
rm gapps-11/Core/setup*
63-
lzip -d gapps-11/Core/*.lz
64-
for f in $(ls gapps-11/Core/*.tar); do
65-
tar -x --strip-components 2 -f $f -C gapps-11
66-
done
67-
68-
adb push gapps-11/etc /system
69-
adb push gapps-11/framework /system
70-
adb push gapps-11/app /system
71-
adb push gapps-11/priv-app /system
72-
73-
echo "Root Script Starting..."
74-
75-
# Root the VM
76-
git clone https://gitlab.com/newbit/rootAVD.git
77-
pushd rootAVD
78-
sed -i 's/read -t 10 choice/choice=1/' rootAVD.sh
79-
./rootAVD.sh system-images/android-30/default/x86_64/ramdisk.img
80-
cp /opt/android-sdk/system-images/android-30/default/x86_64/ramdisk.img /data/android.avd/ramdisk.img
81-
popd
82-
echo "Root Done"
83-
sleep 15
84-
echo "Cleanup ..."
85-
# done
86-
rm -r gapps-11
87-
rm -r rootAVD
107+
[ "$gapps_needed" = true ] && install_gapps && [ "$root_needed" = false ] && adb reboot
108+
[ "$root_needed" = true ] && install_root
88109
apply_settings
110+
copy_extras
111+
89112
touch /data/.first-boot-done
90-
echo "Sucess !!"
113+
echo "Success !!"

start-emulator.sh

100644100755
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#!/bin/bash
22

3-
# Check if the .first-boot-done file exists
4-
if [ -f /data/.first-boot-done ]; then
3+
# Kill any running emulator instances before starting a new one
4+
pkill -f "/opt/android-sdk/emulator/emulator"
5+
6+
# Use custom ramdisk if present
7+
if [ -f /data/android.avd/ramdisk.img ]; then
58
RAMDISK="-ramdisk /data/android.avd/ramdisk.img"
69
fi
710

@@ -13,5 +16,13 @@ if [ -n "$SCREEN_DENSITY" ]; then
1316
SCREEN_DENSITY_FLAG="-dpi-device $SCREEN_DENSITY"
1417
fi
1518

19+
# Configure optional screen resolution and density
20+
if [ -n "$SCREEN_RESOLUTION" ]; then
21+
SCREEN_RESOLUTION_FLAG="-skin $SCREEN_RESOLUTION"
22+
fi
23+
if [ -n "$SCREEN_DENSITY" ]; then
24+
SCREEN_DENSITY_FLAG="-dpi-device $SCREEN_DENSITY"
25+
fi
26+
1627
# Start the emulator with the appropriate ramdisk.img
17-
/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}"
28+
/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}

0 commit comments

Comments
 (0)