1{ composeAndroidPackages, stdenv, lib, runtimeShell }:
2{ name, app ? null
3, platformVersion ? "33"
4, abiVersion ? "armeabi-v7a"
5, systemImageType ? "default"
6, enableGPU ? false
7, extraAVDFiles ? []
8, package ? null
9, activity ? null
10, androidUserHome ? null
11, avdHomeDir ? null # Support old variable with non-standard naming!
12, androidAvdHome ? avdHomeDir
13, sdkExtraArgs ? {}
14, androidAvdFlags ? null
15, androidEmulatorFlags ? null
16}:
17
18let
19 sdkArgs = {
20 includeEmulator = true;
21 includeSystemImages = true;
22 } // sdkExtraArgs // {
23 cmdLineToolsVersion = "8.0";
24 platformVersions = [ platformVersion ];
25 systemImageTypes = [ systemImageType ];
26 abiVersions = [ abiVersion ];
27 };
28
29 sdk = (composeAndroidPackages sdkArgs).androidsdk;
30in
31stdenv.mkDerivation {
32 inherit name;
33
34 buildCommand = ''
35 mkdir -p $out/bin
36
37 cat > $out/bin/run-test-emulator << "EOF"
38 #!${runtimeShell} -e
39
40 # We need a TMPDIR
41 if [ "$TMPDIR" = "" ]
42 then
43 export TMPDIR=/tmp
44 fi
45
46 ${if androidUserHome == null then ''
47 # Store the virtual devices somewhere else, instead of polluting a user's HOME directory
48 export ANDROID_USER_HOME=$(mktemp -d $TMPDIR/nix-android-user-home-XXXX)
49 '' else ''
50 mkdir -p "${androidUserHome}"
51 export ANDROID_USER_HOME="${androidUserHome}"
52 ''}
53
54 ${if androidAvdHome == null then ''
55 export ANDROID_AVD_HOME=$ANDROID_USER_HOME/avd
56 '' else ''
57 mkdir -p "${androidAvdHome}"
58 export ANDROID_AVD_HOME="${androidAvdHome}"
59 ''}
60
61 # We need to specify the location of the Android SDK root folder
62 export ANDROID_SDK_ROOT=${sdk}/libexec/android-sdk
63
64 ${lib.optionalString (androidAvdFlags != null) ''
65 # If NIX_ANDROID_AVD_FLAGS is empty
66 if [[ -z "$NIX_ANDROID_AVD_FLAGS" ]]; then
67 NIX_ANDROID_AVD_FLAGS="${androidAvdFlags}"
68 fi
69 ''}
70
71 ${lib.optionalString (androidEmulatorFlags != null) ''
72 # If NIX_ANDROID_EMULATOR_FLAGS is empty
73 if [[ -z "$NIX_ANDROID_EMULATOR_FLAGS" ]]; then
74 NIX_ANDROID_EMULATOR_FLAGS="${androidEmulatorFlags}"
75 fi
76 ''}
77
78 # We have to look for a free TCP port
79
80 echo "Looking for a free TCP port in range 5554-5584" >&2
81
82 for i in $(seq 5554 2 5584)
83 do
84 if [ -z "$(${sdk}/bin/adb devices | grep emulator-$i)" ]
85 then
86 port=$i
87 break
88 fi
89 done
90
91 if [ -z "$port" ]
92 then
93 echo "Unfortunately, the emulator port space is exhausted!" >&2
94 exit 1
95 else
96 echo "We have a free TCP port: $port" >&2
97 fi
98
99 export ANDROID_SERIAL="emulator-$port"
100
101 # Create a virtual android device for testing if it does not exist
102 ${sdk}/bin/avdmanager list target
103
104 if [ "$(${sdk}/bin/avdmanager list avd | grep 'Name: device')" = "" ]
105 then
106 # Create a virtual android device
107 yes "" | ${sdk}/bin/avdmanager create avd --force -n device -k "system-images;android-${platformVersion};${systemImageType};${abiVersion}" -p $ANDROID_AVD_HOME $NIX_ANDROID_AVD_FLAGS
108
109 ${lib.optionalString enableGPU ''
110 # Enable GPU acceleration
111 echo "hw.gpu.enabled=yes" >> $ANDROID_AVD_HOME/device.avd/config.ini
112 ''}
113
114 ${lib.concatMapStrings (extraAVDFile: ''
115 ln -sf ${extraAVDFile} $ANDROID_AVD_HOME/device.avd
116 '') extraAVDFiles}
117 fi
118
119 # Launch the emulator
120 echo "\nLaunch the emulator"
121 $ANDROID_SDK_ROOT/emulator/emulator -avd device -no-boot-anim -port $port $NIX_ANDROID_EMULATOR_FLAGS &
122
123 # Wait until the device has completely booted
124 echo "Waiting until the emulator has booted the device and the package manager is ready..." >&2
125
126 ${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port wait-for-device
127
128 echo "Device state has been reached" >&2
129
130 while [ -z "$(${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell getprop dev.bootcomplete | grep 1)" ]
131 do
132 sleep 5
133 done
134
135 echo "dev.bootcomplete property is 1" >&2
136
137 #while [ -z "$(${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell getprop sys.boot_completed | grep 1)" ]
138 #do
139 #sleep 5
140 #done
141
142 #echo "sys.boot_completed property is 1" >&2
143
144 echo "ready" >&2
145
146 ${lib.optionalString (app != null) ''
147 # Install the App through the debugger, if it has not been installed yet
148
149 if [ -z "${package}" ] || [ "$(${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell pm list packages | grep package:${package})" = "" ]
150 then
151 if [ -d "${app}" ]
152 then
153 appPath="$(echo ${app}/*.apk)"
154 else
155 appPath="${app}"
156 fi
157
158 ${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port install "$appPath"
159 fi
160
161 # Start the application
162 ${lib.optionalString (package != null && activity != null) ''
163 ${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell am start -a android.intent.action.MAIN -n ${package}/${activity}
164 ''}
165 ''}
166 EOF
167 chmod +x $out/bin/run-test-emulator
168 '';
169}