Skip to main content
Version: next

Audio & permissions

Voice is the only part of the SDK that uses native code. The text path uses none. The native audio engine does one job: capture the microphone as PCM16 and play the bot's PCM back, with hardware echo cancellation.

No AI model runs on the device. The SDK only moves audio bytes — Gemini stays server-side. See Privacy & store compliance.

Sample rates (fixed)

The Gemini bridge fixes the rates and the WebSocket path does not negotiate them (there is no voice_config frame). The SDK hardcodes both in one documented constant:

DirectionFormat
Uplink (mic → server)PCM16, mono, signed LE, 16000 Hz
Downlink (server → speaker)PCM16, mono, signed LE, 24000 Hz

If the device's native audio rate differs (commonly 44.1 / 48 kHz), the SDK resamples at the audio-engine boundary — you don't handle this. Uplink frames are 20–40 ms (20 ms = 640 bytes).

Native audio engine

PlatformCapture / playbackEcho cancellation
iOSAVAudioEngine in .voiceChat modehardware AEC via the voice-chat audio session
AndroidAudioRecord with source VOICE_COMMUNICATIONAcousticEchoCanceler

The engine also handles interruptions and route changes (a call comes in, headphones are plugged/unplugged) with auto-resume, and barge-in flush — when the backend sends { "type": "barge_in" } (the user started talking over the bot), the SDK clears the playback buffer immediately.

Microphone permission

The SDK requests the microphone permission for you the first time you start voice — on both platforms — and resolves a structured result (denial never crashes the app). You still declare the permission strings in your app (iOS NSMicrophoneUsageDescription, Android RECORD_AUDIO); the SDK owns the runtime prompt.

// startVoice() requests RECORD_AUDIO (Android) / the mic (iOS) for you, then captures.
// Denial is non-fatal — it rejects with a typed error and text keeps working.
session.startVoice().catch((err) => console.warn('voice unavailable', err));

The Expo config plugin (or, on bare React Native, your Info.plist) supplies NSMicrophoneUsageDescription (iOS); the package ships the RECORD_AUDIO declaration (Android) — see Quickstart → Install.

Graceful degradation

The voice path never crashes the app. Each failure resolves to a structured state:

SituationBehavior
Microphone deniedvoice does not start; text keeps working; the bot adapts
Native module missing (e.g. running in Expo Go or a simulator)startVoice() rejects; text unaffected
Voice provider down (4503)session ends with a fatal close; surface it and offer text/retry
Hard offlinethe SDK settles into a graceful state after ≤3 reconnect attempts; no crash

Foreground-only (v1)

v1 is foreground-only. There is no background audio — this avoids the iOS UIBackgroundModes: audio review friction and keeps the store path simple. When the app is backgrounded, the socket may drop and the 60s reattach window applies (see Lifecycle); voice resumes when the app returns to the foreground.

Minimum versions

Package floorVoice / audio path
iOS13+16+
AndroidAPI 21+API 24+

App Attest needs iOS 14+; Play Integrity needs recent Play Services — gate attestation accordingly.