Builds a benchmark APK, deploys it, and measures battery drain over time.
Run this from your Dala app project directory (the one containing android/,
dala.exs, and your Elixir source). It requires bundle_id to be set in
dala.exs:
config :dala_dev,
dala_dir: "/path/to/dala",
bundle_id: "com.example.myapp"Reports mAh every 10 seconds and prints a summary at the end. WiFi ADB is required for accurate measurements (USB cable charges the battery).
Setup (one-time, while plugged in)
adb -s SERIAL tcpip 5555
adb connect PHONE_IP:5555
# then unplug and pass PHONE_IP:5555 as --deviceRecommended workflow
Same two-step pattern as iOS — push BEAM flags via mix dala.deploy, then
bench with --no-build. Lets you change tuning without a Gradle rebuild.
# 1. Push BEAM flags via dala.deploy (no APK rebuild — ~10 sec).
mix dala.deploy --beam-flags "" --android # tuned (Nerves)
mix dala.deploy --beam-flags "-S 4:4 -A 8" --android # untuned variant
# 2. Run the bench with --no-build.
mix dala.battery_bench_android --no-build --device 192.168.1.42:5555See README.md for the full rationale and recovery procedure.
Usage (with built-in Gradle build path)
mix dala.battery_bench_android
mix dala.battery_bench_android --no-beam
mix dala.battery_bench_android --preset nerves
mix dala.battery_bench_android --flags "-sbwt none -S 1:1"
mix dala.battery_bench_android --duration 3600 --device 192.168.1.42:5555
mix dala.battery_bench_android --no-build # re-run without rebuildingOptions
--duration N— benchmark duration in seconds (default: 1800 = 30 min)--device SERIAL— adb device serial or IP:port (auto-detected if omitted)--no-beam— baseline: build without starting the BEAM at all--no-keep-alive— skip the foreground-service background keep-alive call--preset NAME— named BEAM flag preset (Gradle-build path only)--flags "..."— arbitrary BEAM VM flags (Gradle-build path only)--no-build— skip APK build and install; run benchmark on current install--log-path PATH— override CSV log location (default:_build/bench/run_android_<ts>.csv)--no-csv— skip CSV logging--skip-preflight— bypass the preflight checks (adb/app/BEAM/RPC/NIF/keep-alive)
What the presets do
untuned— raw BEAM with no tuning flags (highest power use baseline)sbwt— only busy-wait disabled (-sbwt none)nerves— full Nerves set: single scheduler + busy-wait off + multi_time_warp- (default) — same as
nerves(production default)
Understanding the results
The BEAM with Nerves-style tuning flags uses roughly the same power as an app with no BEAM at all (~200 mAh/hr on a Moto G, 30-min run). The untuned BEAM uses ~25% more power due to scheduler busy-waiting. For most apps the overhead is in the noise; tune if you have stricter power budgets.
Under the hood
mix dala.battery_bench_android orchestrates the following adb and Gradle commands:
# Build and install
./gradlew assembleDebug [-PextraCppFlags="-DNO_BEAM|..."]
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Push BEAMs
adb push _build/dev/lib/*/ebin/*.beam /data/data/<pkg>/files/otp/<app>/
# Reset battery stats and launch
adb shell dumpsys batterystats --reset
adb shell am start -n <pkg>/.MainActivity
# Turn screen off
adb shell input keyevent 26 # KEYCODE_POWER
# Poll battery every 10s
adb shell dumpsys battery # reads "Charge counter: <µAh>"
# Stop app and collect final reading
adb shell am force-stop <pkg>
adb shell dumpsys batteryBEAM tuning flags are injected as C preprocessor defines (-DBEAM_UNTUNED,
-DBEAM_FULL_NERVES, etc.) or via a generated dala_beam_flags.h header, so
each preset compiles a different variant of the BEAM startup C code.