web-dev-qa-db-ja.com

setMicrophoneMute()はどのように機能しますか?

私はAndroidのAudioManager.setMicrophoneMute()を使おうと試みましたがあまり成功しませんでした。つまり、私が何をしても、マイクをミュートすることを拒否するだけです。

私はいくつかの手がかりをウェブで検索し、同様の経験を報告しているいくつかの参考文献を見つけました:

どちらが疑問を投げかけます:AudioManager.setMicrophoneMute()はまったく機能しますか?これはスタブメソッドにすぎず、Androidの将来のバージョンで実装されるのを待っていますか?そうでない場合、それはどのように機能しますか?それを機能させるには何が必要ですか?その名前が示すようにそれを機能させる条件は何ですか?

EDIT:このメソッドのドキュメントには次のように書かれていることに気づきました:

この方法は、オーディオ設定のプラットフォーム全体の管理を置き換えるアプリケーションまたはメインのテレフォニーアプリケーションでのみ使用する必要があります。

これは何を意味するのでしょうか?プラットフォーム全体の管理を置き換えたいのはなぜですか?私は本当にそれをする必要がありますか?もしそうなら、どうすればいいですか?

編集:以下の答えは素晴らしいですが、私はまだ理解していません:

  1. そのフラグ(データベースのSET_MIC_MUTE)はどのように使用されていますか?
  2. このフラグが実際に電話内のプリアンプ回路からマイク信号を切断するのはいつですか?
  3. それがしない場合、誰がそれをしますか?
  4. それが何もしない場合、この「ミュート」はどのように機能すると予想されますか?

説明してください。ありがとう。

30
ateiob

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

実際には、マイクをミュートするためのバインダートランザクションです。バインダー呼び出しの受信側は次のようになります。

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

ここで2つのことに注意することができます。 1つ目は、マイクをミュートできるようにするための権限チェックがあることです。 settingsAllowedでチェックされる権限はAndroid.permission.MODIFY_AUDIO_SETTINGSであるため、上記のコメントの1つに記載されているように、マイクをミュートするための最初の要件は、アプリケーションがこの権限が必要であると宣言していることです。次に注意することは、mAudioHardware-> setMicMute(state)を使用してハードウェア固有のバージョンのsetMicMuteを呼び出すことです。

ハードウェアの接続方法の詳細については、AudioHardwareInterface.cppファイルを参照してください。基本的には、プラットフォームに適切なAudioHardWareをプラグインするcreateAudioHardwareへのexternC呼び出しを伴うlibhardwareになります。 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の1つを使用してミュートできる場合は、電話アプリを調べることをお勧めします。

  4. 現在の実装に基づくと、ミュートは通話中でないときに機能するようですが、ここで調査していないプラットフォームではハードウェア固有の問題がある可能性があります。

編集:

さらに掘り下げて、モデムCPUにミュートコマンドを送信する方法は、SDK開発者が利用できないcom.Android.internal.telephonyパッケージの一部である内部電話インターフェイスを介することです。この関数は、オーディオ管理を置き換えるアプリケーションまたは元のテレフォニーアプリケーションでのみ使用する必要があるというコメントに基づいて、AudioManager.setMicrophoneMute()は常にマイクをミュートすることになっていると思います。しかし、他のアプリケーションはおそらくこれを使用するため、マイクだけでなくミュートされた接続を追跡する電話アプリケーションの状態を台無しにしないために、ハードウェア実装にチェックを追加しました。ハードウェア実装の詳細と、ミュートが呼び出し状態を考慮するときに最初に考えるよりもはるかに複雑な操作であるという事実のために、この関数はおそらく現在想定どおりに機能していません。

23
BMB

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

サムスンギャラクシーで同じ問題を見つけ、MODE_IN_COMMUNICATIONモードを使用して解決しました。

AudioManager.Javaのソースコードでは、次のように述べています。

  1. MODE_IN_CALL-通話音声モード。テレフォニーコールが確立されます。
  2. MODE_IN_COMMUNICATION-通信オーディオモード。オーディオ/ビデオチャットまたはVoIP通話が確立されます。

3番目の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);
8
alexwan02