Подтвердить что ты не робот

Как работает setMicrophoneMute()?

Я пытаюсь использовать Android AudioManager.setMicrophoneMute() без особого успеха. То есть, он просто отказывается отключить микрофон, независимо от того, что я делаю.

Я искал в Интернете некоторые подсказки, и я нашел несколько ссылок, сообщающих о подобном опыте:

Что вызывает вопрос: работает ли AudioManager.setMicrophoneMute() вообще? Это только метод заглушки, ожидающий исполнения в какой-то будущей версии Android? Если нет, как это работает? Что мне нужно, чтобы заставить его работать? Каковы условия, которые заставляют его работать с его именем?

EDIT: Я заметил, что в документации для этого метода говорится:

Этот метод должен использоваться только приложениями, которые заменяют общее управление аудио настройками или основной телефонией приложение.

Что это значит? Почему я хочу заменить платформенное управление? Нужно ли мне это делать? Если да, то как это сделать?

РЕДАКТИРОВАТЬ: Ответ ниже велик, но я до сих пор не понимаю:

  • Как используется этот флаг (SET_MIC_MUTE в базе данных)?
  • Когда этот флаг фактически отключает сигнал микрофона от цепь предварительного усилителя внутри телефона?
  • Если это не так, кто это делает?
  • Если ничего не происходит, как ожидается, что этот "немой" будет работать?

Пожалуйста, объясните. Спасибо.

4b9b3361

Ответ 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;
...
}}

и вызывает фактическую реализацию setMicMute в AudioFlinger. Следующий шаг - посмотреть на эту функцию:

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

Здесь мы можем отметить две вещи. Во-первых, есть проверка разрешений, чтобы отключить микрофон. Разрешение, проверяемое в настройкахAllowed, - android.permission.MODIFY_AUDIO_SETTINGS, так что, как упоминалось в одном из комментариев выше, первое требование об отключении микрофона заключается в том, что ваше приложение объявило, что оно нуждается в этом разрешении. Следующее, что нужно отметить, это то, что мы теперь вызываем аппаратную версию setMicMute, используя mAudioHardware- > setMicMute (состояние).

Для получения дополнительной информации о том, как аппаратное обеспечение подключено, изучите файл AudioHardwareInterface.cpp. В основном это заканчивается в libhardware с внешним вызовом 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, вызов микрофона без звука будет соблюдаться только в том случае, если вы не звоните. Причиной этого является то, что звук направляется через аналоговую базовую полосу, которая обрабатывает регулирование мощности, усилие и другие вещи. Когда во время разговора голос аудио часто обрабатывается аналоговым базовым блоком и модемным процессором вместе и не маршрутизируется через процессор приложения. В этом случае вам может понадобиться пройти через модемный процессор через RIL, чтобы отключить микрофон. Но поскольку это поведение зависит от оборудования, общее решение отсутствует.

Чтобы дать короткую версию вашим 4 дополнительным вопросам:

  • Флаг передается через несколько уровней кода до тех пор, пока он не окажется в аппаратном микрофоне без звука.

  • Микрофон отключается, когда выполняется конкретный код оборудования, за исключением случаев, когда он выполняется по крайней мере на некоторых устройствах.

  • Когда setMicrophoneMute не отключает микрофон, т.е. когда в вызове это возможно сделать, используя один из API телефонии: s, я бы предложил изучить телефонное приложение.

  • На основе текущей реализации mute, похоже, работает, когда не в вызове, но могут возникнуть проблемы с аппаратным обеспечением на платформах, которые мы здесь не изучали.

EDIT:

Было ли еще некоторое копание и способ отправки команды mute на модемный процессор через внутренний интерфейс телефона, который является частью пакета com.android.internal.telephony, который недоступен разработчикам SDK. Основываясь на комментарии, вы заметили, что эта функция должна использоваться только приложениями, которые заменяют управление аудио или оригинальное приложение телефонии. Я предполагаю, что AudioManager.setMicrophoneMute() должен был всегда отключать микрофон независимо от него. Но так как другие приложения, вероятно, используют это, они добавили проверку в аппаратную реализацию, чтобы не испортить состояние приложения телефона, которое отслеживает приглушенные соединения, а также микрофон. Вероятно, эта функция не работает должным образом из-за деталей аппаратной реализации и того факта, что приглушение является гораздо более сложной операцией, чем вначале думать при рассмотрении состояний вызова.

Ответ 2

Попробуйте найти исходный код AudioManager:

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, который, как представляется, реализован в собственный код:

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

Ответ 3

Ребята. Я нашел те же проблемы на Samsung Galaxy, и я решил использовать режим MODE_IN_COMMUNICATION. Я видел в исходном коде AudioManager.java: 1.MODE_IN_CALL -'Возможность вызова аудиорежима. Установлен телефонный звонок. 2.MODE_IN_COMMUNICATION -'В режиме общения аудио. Установлен аудио/видео-чат или VoIP-вызов. ' Beacause я использую третий VOIP libray, затем я использую 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);

Ответ 4

По какой-то причине я получаю ошибки при публикации этого комментария. Вот некоторая информация, которую я узнал о том, какие устройства setMicrophoneMute() выполняет и не работает, и какие (несколько) способы работы вокруг нее - безусловно, не идеальное решение! setMicrophoneMute (boolean) не работает на некоторых устройствах