Skip to content

/docs · HearLab · Deep

Hearing-aid routing through Android Audio Framework

How AAudio, ASHA, and LE Audio actually deliver audio to hearing devices on modern Android.


Streaming audio from a phone to a hearing aid sounds like it should be solved. It’s 2026; Bluetooth has been around forever. Yet the routing layer is one of the most surprising parts of the Android stack, and the parts that work well are the parts no consumer app surfaces.

This is a practical map of how the Android audio framework routes to hearing devices, what each layer is good for, and where you actually need to write code if you’re building hearing-support apps.

The layers, top to bottom

App

AudioManager / AudioAttributes

AAudio / OpenSL ES        ← realtime path

Audio HAL

Android Bluetooth stack   ← ASHA, LE Audio, Classic A2DP/HFP

Hearing device

Each layer has its own quirks. Knowing where to write your code is half the battle.

ASHA — the steady incumbent

ASHA (Audio Streaming for Hearing Aids) was Google’s 2018 addition to the Bluetooth stack for direct streaming to hearing aids. It’s a proprietary-style profile but well-documented, and it’s the basis for most “Made for Android” hearing aids in market right now.

What works:

  • Low-latency mono streams at 16 kHz (voice quality) or stereo at higher rates.
  • Direct from the phone to the hearing aid — no intermediary device required.
  • Reliable battery life on the hearing-aid side (the profile is optimised for low duty-cycle).
  • Integration with media and call streams via AudioAttributes.USAGE_VOICE_COMMUNICATION or USAGE_MEDIA.

What doesn’t:

  • Quality is engineered for speech, not music. Streaming a podcast through ASHA sounds great; streaming a film score sounds compressed.
  • Only one device at a time per stream. Binaural pairs are handled by the hearing-aid manufacturer’s side, not the OS.
  • App-side control over codec or stream profile is essentially absent.

LE Audio — the future, mostly arrived

LE Audio (Bluetooth 5.2+, LC3 codec, BAP profile) is the new standard. It’s in shipping hardware on most flagship Android devices and an increasing share of mid-tier. As of late 2025 it’s mainstream enough that a serious hearing-support product needs to plan for it.

What changes:

  • LC3 codec is materially better than SBC and competitive with AAC for music quality, while being more efficient.
  • Multi-stream support: stereo to a binaural pair without the manufacturer-specific glue.
  • Broadcast Audio (Auracast) lets a single source stream to many devices — the basis for assistive listening in public spaces.
  • Better battery life on both sides.

What still bites:

  • Hardware support is uneven. Older devices won’t get LE Audio via OS update.
  • The OS-level routing UI is still maturing.
  • Auracast adoption in public spaces is early.

Where you actually write code

For a hearing-support app, the integration surface is smaller than it looks. Three points:

1. AudioAttributes.USAGE

Set this correctly on your media or call streams. The OS routes based on usage:

val attrs = AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
    .build()

For HearLab’s captions mode, USAGE_ASSISTANCE_ACCESSIBILITY is the right call — it’s the channel the OS uses for accessibility audio.

2. MediaRouter for output device discovery

If you want to know whether the user has a hearing device paired and what its capabilities are:

val router = MediaRouter.getInstance(context)
val info = router.selectedRoute
// info.deviceType tells you if it's a hearing aid
// info.supportedTypes tells you what the device can do

Don’t assume — check. A surprising number of apps stream to the default output without considering the user’s hearing aid.

3. AAudio for low-latency, where you need it

For realtime captioning, audio-rate processing, or anything where round-trip latency matters, use AAudio directly (or Oboe as a wrapper). The system audio stack adds buffering that AAudio bypasses.

AAudioStreamBuilder *builder = nullptr;
AAudio_createStreamBuilder(&builder);
AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

This won’t route to a hearing aid directly — the routing is decided one layer up — but it gets you the audio thread budget needed for realtime work.

The thing nobody surfaces

The single most useful UI for a hearing-aid user is “where is my audio actually going right now?” The Android OS has the information; nobody shows it.

If you build the most basic version of a routing-status banner — “Streaming to: [device name] via [LE Audio / ASHA / wired / speaker]” — and surface it whenever the app is in the foreground, you’ll instantly be ahead of every consumer app I’ve tested.

This is what HearLab Companion exposes by default. It’s not technically hard. It’s just that the rest of the industry doesn’t bother.