Emulator and simulator audio¶
Feed known audio into an Android emulator’s or iOS Simulator’s microphone and check whether an app played sound. Use these to test voice notes, voice search, calls, notification sounds and media playback on headless CI.
All three tools are off by default; enable them with setToolEnabled. They work on local Android
emulators and iOS Simulators. Physical devices are refused.
For host setup, how to check what a device supports, and troubleshooting, see Headless host audio setup.
Android emulators¶
Each emulator has its own audio channel, so emulators running side by side never hear each
other. On macOS, a host microphone initialization failure can prevent safe injection; grant
Microphone permission to the host app that runs the emulator or restart the emulator.
Keep audio enabled on the emulator: do not set AUTOMOBILE_EMULATOR_AUDIO=false.
The result’s route is emulator-grpc.
iOS Simulators¶
A simulator records from the Mac’s default input device and plays to its default output device,
so audio goes through loopback devices on the Mac. The result’s route is host-loopback.
Set up the Mac once:
- Install a loopback audio driver such as BlackHole 2ch.
- Make the loopback device the default input (for
injectAudioInput) and the default output (forcaptureAudio). - Set
AUTOMOBILE_IOS_AUDIO_LOOPBACK_HELPERto the path of anaudio-loopback-helperexecutable. - Give the process that runs AutoMobile Microphone permission, which
captureAudioneeds to read the loopback device.
Every simulator on the Mac shares these devices:
- Only one simulator injection runs at a time per Mac; a second gets
audio_injection_busy. - A capture hears every simulator on the Mac. Run audio tests on one simulator at a time.
- With one loopback device as both default input and output, a simulator’s own playback also reaches its microphone.
AutoMobile starts each emulator it boots with a gRPC port (-grpc) on a free host port from 8554
to 8653, held while that emulator runs, so no extra launch flags are needed. A -grpc flag you
pass in AUTOMOBILE_EMULATOR_ARGS or in the launch’s extra arguments is used as given.
injectAudioInput¶
Plays audio into the microphone and returns as soon as streaming starts. On Android, the
AutoMobile app on the emulator records the guest microphone, so the receiving app need not
already be recording and no image tool is needed. AutoMobile verifies that capture stays
running for three seconds within an eight-second deadline before injection starts. If capture
cannot be verified, injection is refused. Stop competing recordings and retry; if microphone
permission is missing, reinstall the AutoMobile app on the emulator. AutoMobile does not enable
adb root.
On Android, injection is refused before recording or streaming if captured emulator output shows a host microphone failure. AutoMobile cannot check that output for emulators it did not launch. A second injection in the same emulator boot is refused, even after the first stops: restart the emulator before another injection. This also applies to emulators launched outside AutoMobile. Granting Microphone permission can fix host setup, but a repeat injection still requires an emulator restart.
Only one injection may start or run per device. On Android, AutoMobile stops only its own recording when injection stops, finishes, reaches its duration limit, or fails, including on session release. Cleanup is skipped if the emulator restarted; a restarted app no longer holds the recording. Android injection is refused if its start delay, maximum duration (or clip length), and one-minute capture margin exceed 62 minutes.
| Parameter | Description |
|---|---|
file |
Host path to an uncompressed PCM WAV file (up to 50 MB). |
tone |
{ frequencyHz?, durationMs? }: a built-in sine tone, default 1000 Hz for 2 s. |
loop |
Repeat until stopped. A loop with no maxDurationMs stops after 10 minutes. |
startDelayMs |
Wait before the first audio, so the app can start recording. |
gainDb |
Gain, from -60 to +20 dB. |
maxDurationMs |
Stop after this long. |
Pass file or tone, not both. With neither, a 2-second tone plays.
The result has injectionId, durationMs (of one pass), sampleRate and route. On an
emulator, a refusal (busy microphone, rejected credentials) fails the call itself rather than a
later stopAudioInput. In a session,
releasing the session stops the injection. Without a session, the injection stops when a session
takes the device. A relative file path is relative to the directory the server was started in.
stopAudioInput¶
Stops an injection by injectionId, on the device it was started on. Stopping one that already
ended is not an error: you get its result again, with framesQueued, framesPlayed and reason (completed,
stopped or max_duration). The server keeps the last 64 ended injections; an older id, or any
id after a server restart, is an error. If the emulator exited during injection, stopping reports
[audio_emulator_exited] with the device name; restart the emulator before injecting again.
captureAudio¶
Records the device’s audio output for seconds (at most 20) and reports:
silent: true when no 20 ms window reachessilenceThresholdDbfs(default -50).rmsDbfs,peakDbfs: overall level; digital silence reports -120.activeRatio: the share of windows with sound.timedOut: true when the device produced less audio than asked for. A silent app givessilent: trueandtimedOut: true, not an error.
With saveArtifact: true the capture is also written as a WAV file, and path and sizeBytes
are returned.
Assert loosely, on silent and a rough duration, not on exact waveforms: a loaded host can
glitch real-time audio.
Audio in video recordings¶
Start videoRecording with audio: true to record the device’s audio into the file as an AAC
track. The stop result has hasAudioTrack: true when the file has the track, false with a
warning when audio could not be added (the video is kept either way). The same field and warning
come back when the recording stopped at its duration limit, on a repeated stop, and when the
recording is read later. A device that plays nothing gets a silent track.
- It needs FFmpeg on the host; without it the start is refused.
- On Android it supports recordings up to 180 s (one segment).
- On an iOS Simulator it records the Mac’s default output, so it hears every simulator on the Mac.
Errors¶
Errors start with a code in brackets:
| Code | Meaning and what to do |
|---|---|
audio_injection_busy |
Another injection is starting or already feeds this emulator’s microphone, or, for a simulator, any simulator’s on the same Mac. Stop it first. |
audio_injection_unsupported |
No route. An emulator is not local or its build has no gRPC audio: use a recent emulator on the same host. A simulator’s Mac has no loopback default input or no helper configured: follow the setup above. |
audio_capture_unsupported |
The same, for capture; a simulator needs a loopback default output. |
audio_grpc_auth_rejected |
The emulator refused AutoMobile’s gRPC credentials. Relaunch it with -grpc-use-token, or with -grpc <port> and no JWT, then retry. |
audio_grpc_failed |
Any other emulator error; the message carries its detail. |
audio_loopback_failed |
The Mac’s audio system refused the loopback device, for example without Microphone permission; the message carries the detail. |
audio_record_permission_not_granted |
CtrlProxy has no android.permission.RECORD_AUDIO grant that could be verified or repaired. Run adb shell pm grant dev.jasonpearson.automobile.ctrlproxy android.permission.RECORD_AUDIO and retry. |
audio_guest_capture_unavailable |
The guest microphone capture could not be verified for three seconds. Stop competing recordings and retry. If the app is outdated, reinstall the AutoMobile app on the emulator. AutoMobile does not enable adb root. |
audio_host_microphone_unavailable |
The emulator reported a host microphone failure. Grant macOS Microphone permission to the host app that runs the emulator in System Settings > Privacy and Security > Microphone, or restart the emulator. Retrying unchanged is unsafe. |
audio_repeat_injection_unsafe |
An injection was already started in this emulator boot; another may crash it. Grant macOS Microphone permission to the host app that runs the emulator in System Settings > Privacy and Security > Microphone, or restart the emulator. Stop any earlier injection and restart the emulator before injecting again. |
audio_emulator_exited |
The emulator exited during injection. Restart the named emulator and start a new injection. |