Why the Android 12 Mic Indicator Wouldn't Turn Off — A WebRTC Engine Investigation
"Says I'm muted but the mic light is still on. Are you recording me?? Uninstalling."
I have a soft spot for bugs like this one. On the surface, everything looked perfect — the app said muted, audio stopped going out, the code was clean. And users were still furious. Because Android's little green mic dot kept glowing after they hit mute, like the app was quietly listening in. For a video-calling app, that's about the scariest review you can get. So I started pulling threads… and didn't stop until I was inside the WebRTC engine itself.
What users were seeing
The symptom was annoyingly precise — and easy to reproduce:
- Join a call → green mic indicator appears (correct — the mic is in use).
- Tap mute → the app shows "muted," audio stops transmitting… but the green mic indicator stays on.
- It only cleared seconds later, or sometimes not until you left the call.
And here's the detail I kept coming back to: it only happened on Android 12+. On older phones, everything was fine. Whenever a bug shows up on exactly one OS version, the first thing I do is go read what changed in that release. That habit saved me here.
Chasing it down
Before blaming anything exotic, I ruled out the boring explanations — because it's usually the boring one:
- The UI was right. Mute state was correctly set; the app genuinely stopped sending audio. This wasn't a display bug in our code.
- The permission was normal. Nothing unusual about how we requested or held
RECORD_AUDIO. - It was version-specific. The bug appeared exactly on Android 12 — the release that introduced the new privacy indicators (the green/orange dots that tell users when the camera or mic is active).
That last point flipped the whole thing around for me. Those green/orange dots don't care what my app thinks it's doing — they react to whether the OS sees a live audio-capture session, full stop. The dot stays lit until the system gets a "stop," and even then it lingers for a few seconds by design. So I stopped staring at my own mute logic and asked a different question: when the user is "muted," does the operating system still think the mic is open?
It did. And once I understood why, the whole thing made sense.
The actual root cause
Here's the part you'd basically never catch from the app side: muting a WebRTC call doesn't turn the microphone off.
Under the hood, the capture loop in WebRtcAudioRecord keeps an AudioRecord instance happily reading from the mic the entire time. "Mute" is usually just it quietly throwing the samples away — zeroing them out before they ever get encoded or sent. To the user, it's silence. To Android 12, the mic is still wide open, because nothing ever told AudioRecord to stop.
So the app was honest about muting. The engine just never passed that message along to the OS. For years nobody noticed — until Android 12 put a literal spotlight on it and turned a harmless shortcut into a "is this app spying on me?" moment.
// WebRTC audio capture thread (simplified)
private void audioThreadProcess() {
while (keepAlive) {
int bytes = audioRecord.read(byteBuffer, capacity);
if (microphoneMute) {
// "mute" = wipe the samples, but keep recording
byteBuffer.clear();
byteBuffer.put(emptyBytes);
}
nativeDataIsRecorded(bytes); // session still active → OS keeps mic dot on
}
}
Before: muting discards the audio, but the AudioRecord keeps capturing.
Fixing it
The fix had to live where the problem actually was — in the engine, not the app. So instead of "muting" by tossing samples while the mic kept running, I made mute do the honest thing: stop and release the capture, then spin it back up on un-mute. The moment the session genuinely stops, the OS gets its "stop" signal and the green dot starts clearing on its own.
Since this all lives in the WebRTC SDK and not the app, I patched WebRtcAudioRecord.java in the engine source, built a custom WebRTC AAR, and dropped it in to replace the stock one. I sent the fix upstream as well — it's public, so you don't have to take my word for any of this: WebRTC change 340200.
public void setMicrophoneMute(boolean mute) {
this.microphoneMute = mute;
if (mute) {
stopRecording(); // actually release the AudioRecord session
} else {
startRecording(); // re-initialize capture on un-mute
}
}
After: muting genuinely stops capture, so the OS clears the indicator.
The payoff
The change was small, but the effect was immediate:
- Hit mute, and the green mic indicator starts clearing right away — exactly what people expect to happen.
- The "is this app recording me while I'm muted?" reviews dried up.
- And the trust came back — with a fix anyone can verify in the open-source engine, not a hack quietly bolted onto the app.
What I took away from it
A few lessons from this one that I keep running into:
- The OS decides what the privacy dots mean, not your UI. If the platform thinks the mic is open, your "muted" label is just a label. Make the real system state match what the user intended.
- "Muted" and "not recording" are not the same thing. A lot of real-time stacks mute by throwing away audio, not by stopping the mic. On today's Android and iOS, users can now see that difference — so it matters.
- Sometimes the bug isn't in your code at all — it's in the engine you depend on. Being willing to crack open the SDK, patch it, and ship your own build is often the whole difference between "we can't reproduce it" and "it's fixed."
This is the kind of problem that hides below the surface — and chasing things like it is what FixMyApp does best: deep engineering, root-cause investigation, and fixes for Mobile, Web, and Real-Time applications. Whether you need an urgent rescue, help getting an AI-built app live, or want to launch an idea in 30 days — reach out for a quote.
Request a quote / Let's talk