Use AOQ to connect to qwen-audio-3.0-realtime-plus and use server-side VAD to build low-latency real-time voice conversations. Client code examples use Android Java.
Solution overview
Qwen-Audio is an end-to-end real-time voice interaction model for low-latency scenarios such as voice assistants, customer service, and AI companions. AOQ transports audio and events on separate tracks. The Audio track carries microphone PCM uplink and model PCM downlink, and the Data track carries Realtime protocol events.
This tutorial uses server_vad. The client continuously sends audio, and the service detects when the user starts and stops speaking and triggers a response.
Prerequisites
- Activate QwenCloud and obtain an API Key following Get and configure an API Key. Store the API Key only on your application server. Do not include it in client code or commit it to a code repository.
- Download the latest AOQ Client SDK as described in SDK download.
- Build an application server and implement proxy authentication as described in Token authentication. Before each new connection, the client must obtain new connection credentials from the application server.
Import the SDK
Import the SDK for your development platform. The client implementation uses Android Java. Other platforms provide the same interfaces and event flow. This tutorial uses PCM audio streams. Opus encoding is provided by a plugin. Import the Opus plugin if the uplink uses Opus.
- Android
- iOS
- HarmonyOS
- Linux (Python)
- Place AoqClientSdk-release.aar in app/libs, and configure the dependency and SDK-supported ABIs in app/build.gradle:
- Declare the following permissions in AndroidManifest.xml:
- Request the RECORD_AUDIO permission at runtime before the corresponding devices are used.
Try the demo
QwenCloud provides an Android demo app to quickly verify AOQ connectivity. Download the APK and configure the API key and workspaceId to try selected models.
Scan the following QR code to download the demo:

Implementation flow
- The application server obtains credentials for the current AOQ connection to qwen-audio-3.0-realtime-plus from the Realtime token URL.
- The client configures the SDK uplink encoder and downlink decoder for the selected model and the application's audio format.
- The client initializes the recording and playback devices and creates AoqConnectConfig. It populates the credential fields for the current connection and configures the Audio and Data tracks to publish and subscribe to. The client keeps Audio-track sending disabled and calls connect to establish the AOQ connection.
- After the connection is established, the client sends session.update. It enables the Audio track only after session.updated is received.
- Server-side VAD automatically determines turn boundaries. Model audio is played over the Audio track and conversation events are returned over the Data track.
- To finish, disconnect and destroy the engine. The SDK automatically closes the audio devices.

Obtain a token from the application server
Set DASHSCOPE_API_KEY on the application server and send the request to the endpoint. clientIp is the actual public IP address of the client. This field is optional, but specifying it helps the service allocate an appropriate relay endpoint.
If the application server cannot obtain the actual public IP address of the client, omit
clientIp instead of passing an empty string.| Response field | SDK field |
|---|---|
| aoqTokenForClient | AoqConnectConfig.token |
| sid | AoqConnectConfig.sid |
| clientRelayCertFingerprint | AoqConnectConfig.certFingerprint |
| clientRelayEndpoints | AoqConnectConfig.relayEndpoints |
Implement the Android client
Before each connection, the client obtains new connection credentials from the application server and creates AoqConnectConfig. Map the token response fields and add client-side connection settings such as the publish and subscribe tracks. Follow these steps to implement real-time voice conversations on Android.
1. Create the engine and register callbacks
Create the singleton AOQ engine and register event callbacks. Configure the session after the connection succeeds, and dispatch server events to the UI and application state machine.
2. Configure audio codecs
Configure the SDK uplink encoder and downlink decoder for the selected model and the application's audio format. The following values are PCM examples for this tutorial and do not restrict the audio format of your application.
3. Configure tracks and connect
Use SDK interfaces to start audio capture and playback. Map the current application-server token response to the credential fields in AoqConnectConfig, and configure the Audio and Data tracks in publishTracks and subscribeTracks. Keep Audio-track sending disabled when you call connect. Enable sending only after session.updated is received.
4. Send session.update
After the connection succeeds, configure output modalities, voice, audio formats, instructions, and VAD. Both input_audio_format and output_audio_format use pcm. The SDK codec configuration determines the sample rates. For all parameters, see Client events.
5. Enable uplink after session.updated
session.updated indicates that the session configuration is active. Enable Audio-track sending only at this point so that audio captured earlier is not sent to the model.
6. Handle server events
In onDataMsg, use type to display user and model transcripts and handle errors. For all event fields, see Server events.
7. Disconnect and destroy the engine
When the conversation ends, disconnect and destroy the singleton engine. disconnect or destroy automatically closes audio capture and playback, so you do not need to stop the devices separately.
Main server events
Data-track events are identified by type. The client must handle the following key events. For complete event schemas, see Server events.
| Event | Description |
|---|---|
| session.created | The session is created and default settings are returned |
| session.updated | Client settings are active and audio uplink can be enabled |
| input_audio_buffer.speech_started | The service detects that the user started speaking |
| input_audio_buffer.speech_stopped | The service detects that the user stopped speaking |
| input_audio_buffer.committed | Audio for the turn is committed |
| response.created | The model starts generating a response |
| response.audio_transcript.delta | Incremental model transcript |
| conversation.item.input_audio_transcription.completed | The final user transcript is available |
| response.done | The response is complete |
| error | A server error occurs |
Complete example
The following class accepts an AoqConnectConfig populated with credentials for the current connection and adds the audio-device, publish-track, and subscribe-track settings. Obtain new credentials and create a new connection configuration for every reconnection. Add permissions, UI state, and reconnection logic in production.
Run and verify
- Microphone audio starts streaming only after session.updated is received.
- After the user stops speaking, the service commits the audio and starts responding. Text events and Audio-track audio are returned continuously.
Common scenarios
Change the interaction mode
Use server_vad for silence-based turn detection, smart_turn for acoustic and semantic turn detection, or set turn_detection to null for push-to-talk. turn_detection can be changed only before the first audio input. Establish a new session to change modes.
Change the voice
Set session.voice in the first session.update. Supported system voices vary by model. For supported voices and voice cloning, see Qwen-Audio real-time voice conversation.
Speaker or earpiece
Set the default output device by using AoqAudioPlaybackConfig.isDefaultSpeaker, and call enableSpeakerphone to switch while the session is active.
Background calls on Android
On Android 10 or later, use a foreground service with foregroundServiceType="microphone|mediaPlayback" to continue capture and playback in the background. Start it while the app is visible to the user.
Troubleshooting
| Issue | Solution |
|---|---|
| The connection fails | Make sure that the token is valid, the endpoint matches the deployment region, and AoqConnectConfig fields are mapped correctly. |
| The session is established but no response is returned | Make sure that the Audio track is enabled after session.updated and that the SDK uplink encoder matches the model and application audio format. |
| The response has no audio | Make sure that the Audio track is subscribed and the audio player is running, and then verify that the SDK downlink decoder matches the model output audio format. |