【问题标题】:How does setMicrophoneMute() work?setMicrophoneMute() 是如何工作的?
【发布时间】:2011-10-16 00:51:41
【问题描述】:

我一直在尝试使用 Android 的 AudioManager.setMicrophoneMute(),但没有取得多大成功。也就是说,无论我做什么,它都拒绝让麦克风静音。

我在网上搜索了一些线索,并找到了一些报告类似经历的参考资料:

这引出了一个问题:AudioManager.setMicrophoneMute() 是否有效?它只是一个存根方法,等待在未来的 Android 版本中实现吗?如果没有,它是如何工作的?我需要什么才能让它工作?顾名思义,它发挥作用的条件是什么?

编辑:我注意到这个方法的文档说:

此方法只能由替换 音频设置或主电话的平台范围管理 应用。

这是什么意思?为什么要替换平台范围的管理?我真的需要这样做吗?如果是这样,我该怎么做?

编辑:下面的答案很好,但我还是不明白:

  1. 如何使用该标志(数据库中的 SET_MIC_MUTE)?
  2. 此标志何时真正断开麦克风信号与 手机内部的前置放大电路?
  3. 如果它不这样做,那是谁做的?
  4. 如果不这样做,这个“静音”应该如何工作?

请解释一下。谢谢。

【问题讨论】:

  • 我确定您必须拥有 - 但您是否启用了 checkAudioSettingsPermission 测试的权限?
  • @YetAnotherUser 我检查了checkAudioSettingsPermission 的“文档”,除了返回一个布尔值外,没有任何关于它的作用以及它期望在字符串参数中接收什么的线索。我真的不想将问题从setMicrophoneMute() 更改为checkAudioSettingsPermission,但是您知道checkAudioSettingsPermission 的作用以及String 参数的含义是什么吗? +1
  • 您可能想要概述您最初想要实现的目标。听起来你有点跑题了。您可能不想更换电话应用程序,也不应该更换(顺便说一句,如果没有平台证书,您也不能这样做)。
  • @marsbear 我真的不想替换任何东西。 :) 我要做的就是静音麦克风。 Android 有一个声称可以做到这一点的功能(实际上是其中的 4 个,如下所示,由唯一一个敢于回答的人显示),但它不起作用。我是什么做的? +1 试图提供帮助。
  • @Smugrik 当然,我在清单中有 MODIFY_AUDIO_SETTINGS RECORD_AUDIO 权限。我认为这是通过提供引用这些设置的第一个链接来暗示的。

标签: android microphone android-audiomanager


【解决方案1】:

要详细说明上面的 an00b:s 答案和问题的编辑版本,我们必须更深入地挖掘源代码。 IAudioflinger 是 AudioFlinger 服务的接口和对

的调用
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();
}

实际上是使麦克风静音的Binder事务。 Binder 调用的接收端如下所示:

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;
    ...
    }
}

并在AudioFlinger 中调用 setMicMute 的实际实现。下一步就是看这个函数了:

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;
}

在这里我们可以注意到两件事。首先是有一个权限检查可以使麦克风静音。在 settingsAllowed 中检查的权限是 android.permission.MODIFY_AUDIO_SETTINGS ,因此在上面的其中一个 cmets 中提到,使麦克风静音的第一个要求是您的应用程序已声明它需要此权限。接下来要注意的是,我们现在使用 mAudioHardware->setMicMute(state) 调用硬件特定版本的 setMicMute。

有关硬件插入方式的更多信息,请参阅文件 AudioHardwareInterface.cpp。基本上,它最终在一个 libhardware 中,通过 extern C 调用 createAudioHardware 为平台插入正确的 AudioHardWare。还有一些用于使用基于 A2DP 的硬件的开关,一种用于仿真器的通用硬件和存根音频。假设您正在使用实际设备,那么实现很大程度上取决于硬件。为了感受它,我们可以使用来自 Crespo (Nexus S) 的可用音频硬件作为示例。

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;
}

基于这个例子,我们可以总结一下智能手机中音频路由的实现。正如您在 Crespo 实现中看到的那样,只有在您未通话时才会尊重麦克风静音呼叫。原因是音频通过模拟基带路由,该基带处理功率调节、放大和其他事情。在通话中,语音音频通常由模拟基带和调制解调器 CPU 一起处理,而不是通过应用程序 CPU 路由。在这种情况下,您可能需要通过 RIL 通过调制解调器 CPU 才能使麦克风静音。但由于此行为取决于硬件,因此没有通用解决方案。

为您的 4 个附加问题提供简短版本:

  1. 标志通过几层代码传递,直到最终到达硬件特定的静音麦克风。

  2. 当硬件特定代码运行时,麦克风会断开连接,至少在某些设备上通话时除外。

  3. 当 setMicrophoneMute 不使麦克风静音时,即在通话时可以使用其中一个电话 API:s 来做到这一点,我建议研究电话应用程序。

  4. 根据当前的实现,静音似乎在不在通话中时有效,但在我们未在此处研究的平台上可能存在特定于硬件的问题。

编辑:

进行了更多挖掘,向调制解调器 CPU 发送静音命令的方法是通过内部电话接口,该接口是 SDK 开发人员无法使用的 com.android.internal.telephony 包的一部分。根据您看到的评论,此功能应仅由替换音频管理或原始电话应用程序的应用程序使用,我猜 AudioManager.setMicrophoneMute() 无论如何都应该始终使麦克风静音。但是由于其他应用程序可能会使用它,因此他们在硬件实现中添加了一个检查,以免弄乱电话应用程序的状态,该应用程序跟踪静音连接和麦克风。由于硬件实现细节以及静音操作比人们在考虑调用状态时最初想象的要复杂得多这一事实,该功能现在可能无法正常工作。

【讨论】:

  • 哇!我无法期待比这更好的答案。 an00b 的回答是一个很好的起点,但您肯定深入研究了最深层次,这就是我需要了解为什么当RecognitionListener 正在收听时它对我不起作用的原因。也就是说,如果我尝试在 RecognitionListener.onReadyForSpeech() 中呼叫 mAudioManager.setMicrophoneMute(true),则不会发生任何事情:麦克风继续接受语音,就好像从未呼叫过静音一样。而且没有正在进行的通话!这是否意味着 Google 的 RecognitionListener 设置了 AudioSystem::MODE_IN_CALL?
  • 我刚刚找到了另一个版本的AudioHardware.cpp,它以更复杂的方式实现它......我想要的只是在onReadyForSpeech() 中短暂断开麦克风......有没有办法做到这一点?替换音频管理需要什么?
  • 我需要再次深入研究音频代码才能回答您关于 onReadyForSpeech() 的问题,但到目前为止,我想说 API 函数 AudioManager.setMicroPhoneMute() 似乎并没有真正反映现代智能手机中音频路由的复杂性。这可能意味着没有通用的方法来实现您想要的,因为每个硬件平台似乎都以不同的方式处理麦克风静音。如果我找到办法,我会编辑此评论。
  • 再次感谢 +1。如果您能帮我找到适用于 Nexus One(尤其是 CyanogenMod 6)的正确 AudioHardware.cpp,我们将不胜感激。
【解决方案2】:

尝试查看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);
    }
}

将麦克风静音的任务委托给名为IAudioService的服务:

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

这又将其委托给AudioSystem,这似乎在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);
}

这反过来又将其委托给IAudioFlinger,可以在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();
}

【讨论】:

  • 谢谢,但我仍然不明白它是如何工作的。我所看到的是,它通过许多层和包装器基本上设置或清除某个数据库中的标志。但是该标志是如何使用的呢?这个标志何时真正断开麦克风信号与手机内部前置放大器电路的连接?如果它不这样做,那是谁做的?如果没有这样做,那么这种“静音”如何工作?请解释。 +1。
【解决方案3】:

我在三星 Galaxy 上发现了同样的问题,我使用MODE_IN_COMMUNICATION 模式解决了它。

在 AudioManager.java 源代码中它说:

  1. MODE_IN_CALL - 通话音频模式。电话已建立。
  2. MODE_IN_COMMUNICATION - 处于通信音频模式。已建立音频/视频聊天或 VoIP 呼叫。

因为我使用第三个 VOIP 库,所以我使用了MODE_IN_COMMUNICATION,它解决了这个问题。

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);

【讨论】:

  • MODE_IN_COMMUNICATION 成功了,我试图使用MODE_IN_CALL 将麦克风静音/取消静音。感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 2017-07-24
  • 2016-11-13
  • 2017-10-11
  • 2021-10-13
  • 2011-02-24
  • 2013-11-16
  • 2012-11-03
  • 2015-10-08
相关资源
最近更新 更多