How does setMicrophoneMute() work?
Asked Answered
V

3

32

I have been trying to use Android's AudioManager.setMicrophoneMute() without much success. That is, it simply refuses to mute the microphone, no matter what I do.

I searched the web for some clues and I found several references reporting similar experience:

Which begs the question: Does AudioManager.setMicrophoneMute() work at all? Is it only a stub method, waiting to be implemented in some future version of Android? If not, how does it work? What do I need to make it work? What are the conditions that make it work as its name implies?

EDIT: I noticed that the documentation for this method says:

This method should only be used by applications that replace the platform-wide management of audio settings or the main telephony application.

What does this mean? Why would I want to replace the platform-wide management? Do I really need to do that? If so, how do I do that?

EDIT: The answer below is great but I still don't understand:

  1. How is that flag (SET_MIC_MUTE in database) being used?
  2. When does this flag actually disconnect the microphone signal from the pre-amplifier circuit inside the phone?
  3. If it doesn't do that, who does that?
  4. If nothing does that, how is this "mute" expected to work?

Please explain. Thanks.

Vtarj answered 29/7, 2011 at 14:44 Comment(6)
I'm sure you must have - but did you enabled permissions tested by checkAudioSettingsPermission?Helmand
@Helmand I checked the "documentation" for checkAudioSettingsPermission and except for returning a boolean, there is no clue as to what it does and what it expects to receive in the String parameter. I really don't want to change the question from setMicrophoneMute() to checkAudioSettingsPermission, but do you have any idea what checkAudioSettingsPermission does and what's the meaning of that String parameter? +1Vtarj
You may want to outline what you wanted to achieve initially. It sounds like you got a bit off course. You probably do not want to replace the telephony-apllication and neither should you (and you can't by the way without having a platform cert).Sharpset
@Sharpset I really don't want to replace anything. :) All I am looking to do is mute the microphone. Android has a function (actually 4 of them as shown below by the only one who was brave enough to answer) that claims to do that, but it doesn't work. What am I to do? +1 for trying to help.Vtarj
just checking: do you have the MODIFY_AUDIO_SETTINGS or RECORD_AUDIO permission in the manifest?Hegelianism
@Hegelianism Of course I have MODIFY_AUDIO_SETTINGS and RECORD_AUDIO permissions in the manifest. I thought that was implied by providing the 1st link which refers to these settings.Vtarj
M
23

To elaborate on an00b:s answer above and the edited version of the question we have to dig deeper into the source code. IAudioflinger is the interface to the AudioFlinger service and the call to

virtual status_t setMicMute(bool state)
{
    Parcel data, reply;
    data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
    data.writeInt32(state);
    remote()->transact(SET_MIC_MUTE, data, &reply);
    return reply.readInt32();
}

is actually the Binder transaction to mute the microphone. The receiving side of the Binder call looks like:

status_t BnAudioFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)  { 
    switch(code) {
        ...
        case SET_MIC_MUTE: {
            CHECK_INTERFACE(IAudioFlinger, data, reply);
            int state = data.readInt32();
            reply->writeInt32( setMicMute(state) );
            return NO_ERROR;
        } break;
    ...
    }
}

and calls to the actual implementation of setMicMute in the AudioFlinger. Next step is to look at this function:

status_t AudioFlinger::setMicMute(bool state) {
    // check calling permissions
    if (!settingsAllowed()) {
        return PERMISSION_DENIED;
    }

    AutoMutex lock(mHardwareLock);
    mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
    status_t ret = mAudioHardware->setMicMute(state);
    mHardwareStatus = AUDIO_HW_IDLE;
    return ret;
}

Here we can note two things. The first is that there is a permissions check to be able to mute the microphone. The permission being checked for in settingsAllowed is android.permission.MODIFY_AUDIO_SETTINGS so as mentioned in one of the comments above the first requirement for muting the microphone is that your application has declared that it needs this permission. The next thing to note is that we now call in to the hardware specific version of setMicMute using mAudioHardware->setMicMute(state).

For more info on the way the hardware is plugged study the file AudioHardwareInterface.cpp. Basically it ends up in a libhardware with an extern C call to createAudioHardware which plugs in the correct AudioHardWare for the platform. There are also switches for using an A2DP based hardware, a generic one for the emulator and stubbing the audio. Assumed that you are working on an actual device the implementation is then very much hardware depending. To get a feel for it we can use the available audiohardware from Crespo (Nexus S) as an example.

status_t AudioHardware::setMicMute(bool state) {
    LOGV("setMicMute(%d) mMicMute %d", state, mMicMute);
    sp<AudioStreamInALSA> spIn;
    {
        AutoMutex lock(mLock);
        if (mMicMute != state) {
            mMicMute = state;
            // in call mute is handled by RIL
            if (mMode != AudioSystem::MODE_IN_CALL) {
                spIn = getActiveInput_l();
            }
        }
    }

    if (spIn != 0) {
        spIn->standby();
    }

    return NO_ERROR;
}

Based on this example we may wrap up with a discussion of the implementation of audio routing in smartphones. As you can see in the Crespo implementation the mic mute call will only be respected if you are not in a call. The reason for this is that audio is routed through the analog baseband which handles power regulation, amplification and other things. When in a call the voice audio is often handled by analog baseband and modem CPU together and is not routed throught the application CPU. In that case you may need to go through the modem CPU through the RIL in order to mute the microphone. But since this behavior is hardware dependent there is no general solution.

To give the short version to your 4 additional questions:

  1. The flag is passed on through several layers of code until it ends up in the hardware specific mute microphone.

  2. The mic is disconnected when the hardware specific code has run except when in a call at least on some devices.

  3. When setMicrophoneMute does not mute the mic, i.e. when in a call it may be possible to do that using one of the telephony API:s, I would suggest studying the phone app.

  4. Based on the current implementation mute seems to work when not in a call but there may be hardware specific issues on platforms we have not studied here.

EDIT:

Did some more digging and the way to send a mute command to the modem CPU is via the internal Phone interface that is part of the com.android.internal.telephony package that is not available to SDK developers. Based on the comment you saw that this function should only be used by applications that replace audio management or the original telephony application I would guess that AudioManager.setMicrophoneMute() was supposed to always mute the microphone regardless. But since other applications probably use this they added a check in the hardware implementation in order not to mess up the state of the phone application which keeps track of muted connections as well as the microphone. The function is probably not working as supposed to right now due to hardware implementation details and the fact that mute is a much more complex operation than one would initially think when considering call states as well.

Manslaughter answered 18/8, 2011 at 9:55 Comment(4)
Wow! I couldn't have expected a better answer than this. an00b's answer was a good starting point but you certainly delved into the deepest level, which is what I needed to understand why it doesn't work for me when RecognitionListener is listening. That is, if I try to call mAudioManager.setMicrophoneMute(true) inside RecognitionListener.onReadyForSpeech(), nothing happens: The microphone continues to accept speech as if Mute were never called. And there is no call in progress! Does this mean that Google's RecognitionListener sets AudioSystem::MODE_IN_CALL?Vtarj
I just found another version of AudioHardware.cpp which implements it in an even much more complex manner... And all I wanted is simply to briefly disconnect the microphone while in onReadyForSpeech()... Is there a way to accomplish this at all? What does replacing audio management entail?Vtarj
I would need to dig a bit more into the Audio code again to answer your question about onReadyForSpeech() but so far I would say it seems that the API function AudioManager.setMicroPhoneMute() does not really reflect the complexity of audio routing in modern smartphones. That probably means that there is no generic way to implement what you want since each hardware platform seem to handle muting the microphone in different ways. I will edit this comment if I find a way.Manslaughter
Thanks +1 again. If you can help me find the right AudioHardware.cpp for Nexus One (especially CyanogenMod 6), this would be much appreciated.Vtarj
A
12

Try looking at the AudioManager source code:

public void setMicrophoneMute(boolean on){
    IAudioService service = getService();
    try {
        service.setMicrophoneMute(on);
    } catch (RemoteException e) {
        Log.e(TAG, "Dead object in setMicrophoneMute", e);
    }
}

The task of muting the microphone is delegated to a service named IAudioService:

public void setMicrophoneMute(boolean on) {
    if (!checkAudioSettingsPermission("setMicrophoneMute()")) {
        return;
    }
    synchronized (mSettingsLock) {
        if (on != mMicMute) {
            AudioSystem.muteMicrophone(on);
            mMicMute = on;
        } 
    }
}

Which, in turn, delegates it to AudioSystem which seems to be implemented in native code:

status_t AudioSystem::muteMicrophone(bool state) {
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;
    return af->setMicMute(state);
}

Which, in turn, delegates it to IAudioFlinger as can be found in IAudioFlinger.cpp:

virtual status_t setMicMute(bool state)
{
    Parcel data, reply;
    data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
    data.writeInt32(state);
    remote()->transact(SET_MIC_MUTE, data, &reply);
    return reply.readInt32();
}
Adnopoz answered 29/7, 2011 at 19:45 Comment(1)
Thanks but I still don't understand how it works. All I see is that it goes through numerous layers and wrappers to basically set or clear a flag in some database. But how is that flag being used? When does this flag actually disconnects the microphone signal from the pre-amplifier circuit inside the phone? If it doesn't do that, who does that? If nothing does that, how is this "mute" expected to work? Please explain. +1.Vtarj
G
9

I found the same issues on samsung Galaxy and I solved it by using MODE_IN_COMMUNICATION mode.

In the AudioManager.java source codeit says :

  1. MODE_IN_CALL - In call audio mode. A telephony call is established.
  2. MODE_IN_COMMUNICATION - In communication audio mode. An audio/video chat or VoIP call is established.

Because i use the third VOIP library, I use the MODE_IN_COMMUNICATION and it solved the issue.

AudioManager audioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);
// get original mode 
int originalMode = audioManager.getMode();
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
// change mute 
boolean state = !audioManager.isMicrophoneMute();
audioManager.setMicrophoneMute(state);
// set mode back 
audioManager.setMode(originalMode);
Goulash answered 22/11, 2015 at 6:31 Comment(2)
MODE_IN_COMMUNICATION did the trick, I was trying to mute/unmute the mic using MODE_IN_CALL. Thanks for helpTorritorricelli
Nice trick but it doesn't work on a Google Pixel 4a as its DeviceHAL keeps logging a warning: "Device set_mic_mute: Function not implemented"Sidestep

© 2022 - 2024 — McMap. All rights reserved.