Skip to content

Commit 949006c

Browse files
committed
feat(emulator): make root and gapps setup optional
1 parent 170eccd commit 949006c

4 files changed

Lines changed: 116 additions & 76 deletions

File tree

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: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ services:
99
- "5555:5555"
1010
volumes:
1111
- ./data:/data
12-
- ./extras:/extras
12+
- ./extras:/extras # Magisk modules placed here are auto-installed once root is available
1313
environment:
14-
- DNS=one.one.one.one
15-
- RAM_SIZE=8192
14+
DNS: one.one.one.one
15+
RAM_SIZE: 8192
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: 76 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
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
6-
# Waiting for the boot sequence to be completed.
12+
# Wait until the boot sequence is fully completed before applying tweaks.
713
COMPLETED=$(adb shell getprop sys.boot_completed | tr -d '\r')
814
while [ "$COMPLETED" != "1" ]; do
915
COMPLETED=$(adb shell getprop sys.boot_completed | tr -d '\r')
@@ -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: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

3-
# Check if the .first-boot-done file exists
4-
if [ -f /data/.first-boot-done ]; then
3+
# Use custom ramdisk if present
4+
if [ -f /data/android.avd/ramdisk.img ]; then
55
RAMDISK="-ramdisk /data/android.avd/ramdisk.img"
66
fi
77

@@ -13,5 +13,13 @@ if [ -n "$SCREEN_DENSITY" ]; then
1313
SCREEN_DENSITY_FLAG="-dpi-device $SCREEN_DENSITY"
1414
fi
1515

16+
# Configure optional screen resolution and density
17+
if [ -n "$SCREEN_RESOLUTION" ]; then
18+
SCREEN_RESOLUTION_FLAG="-skin $SCREEN_RESOLUTION"
19+
fi
20+
if [ -n "$SCREEN_DENSITY" ]; then
21+
SCREEN_DENSITY_FLAG="-dpi-device $SCREEN_DENSITY"
22+
fi
23+
1624
# 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}"
25+
/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)