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

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

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

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

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

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

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

EDIT: ответ ниже велик, но я все еще не понимаю:

  1. как используется этот флаг (SET_MIC_MUTE в базе данных)?
  2. когда этот флаг на самом деле отключите сигнал микрофона от схема предварительного усилителя внутри телефона?
  3. если он этого не делает, кто это делает?
  4. если ничего этого не происходит, как ожидается, что этот "немой" будет работать?

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

3 ответов


чтобы уточнить ответ 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;
}

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

для получения дополнительной информации на оборудование подключено изучить файл AudioHardwareInterface.СРР. В основном это заканчивается в libhardware с вызовом extern C для createAudioHardware, который подключает правильное AudioHardWare для платформы. Существуют также коммутаторы для использования аппаратного обеспечения на основе A2DP, общего для эмулятора и глушения звука. Предполагается, что вы работаете над фактическим устройством, реализация тогда очень аппаратное обеспечение в зависимости. Чтобы почувствовать это, мы можем использовать доступное audiohardware от 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, вызов MIC mute будет соблюдаться только в том случае, если вы не находитесь в вызове. Причина этого в том, что звук маршрутизируется через аналоговую базовую полосу, которая обрабатывает регулирование мощности, усиление и другие вещи. При вызове голосовой звук часто обрабатывается аналоговой основной полосой и модемным процессором вместе и не маршрутизируется через процессор приложения. В этом случае вам может потребоваться пройти через процессор модема через RIL, чтобы отключить микрофон. Но поскольку это поведение зависит от оборудования, общего решения нет.

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

  1. флаг передается через несколько слоев кода, до его заканчивается в аппаратном микрофоне mute.

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

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

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

EDIT:

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


попробуйте посмотреть на исходный код 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;
        } 
    }
}

, который, в свою очередь, перепоручает это аудиосистема который, кажется, реализован в машинный код:

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

я нашел те же проблемы на samsung 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);