android - ¿Cómo funciona setMicrophoneMute()?
android-audiomanager (3)
Encontré los mismos problemas en Samsung Galaxy y lo resolví usando el modo MODE_IN_COMMUNICATION
.
En el código fuente AudioManager.java dice:
-
MODE_IN_CALL
- En modo de audio de llamada. Se establece una llamada de telefonía. -
MODE_IN_COMMUNICATION
: en el modo de audio de comunicación. Se establece un chat de audio / video o una llamada VoIP.
Como utilizo la tercera biblioteca de VOIP, uso MODE_IN_COMMUNICATION
y solucionó el problema.
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);
He intentado usar AudioManager.setMicrophoneMute()
Android sin mucho éxito. Es decir, simplemente se niega a silenciar el micrófono, no importa lo que haga.
Busqué en la web algunas pistas y encontré varias referencias que informaban experiencias similares:
- La funcionalidad AudioManger.setMicrophoneMute no funciona?
- setMicrophoneMute (boolean) no funciona en algunos dispositivos
- No se puede silenciar el micrófono en Android
Lo cual plantea la pregunta: ¿ AudioManager.setMicrophoneMute()
? ¿Es solo un método stub, esperando ser implementado en alguna versión futura de Android? Si no, ¿cómo funciona? ¿Qué necesito para que funcione? ¿Cuáles son las condiciones que lo hacen funcionar como su nombre lo indica?
EDIT: noté que la documentación de este método dice:
Este método solo debe ser utilizado por aplicaciones que reemplacen la administración de toda la plataforma de la configuración de audio o la aplicación principal de telefonía.
¿Qué significa esto? ¿Por qué querría reemplazar la administración de toda la plataforma? ¿Realmente necesito hacer eso? Si es así, ¿cómo hago eso?
EDITAR: La respuesta a continuación es genial, pero aún no entiendo:
- ¿Cómo se usa esa bandera (SET_MIC_MUTE en la base de datos)?
- ¿Cuándo esta bandera realmente desconecta la señal del micrófono del circuito del preamplificador dentro del teléfono?
- Si no hace eso, ¿quién hace eso?
- Si nada hace eso, ¿cómo se espera que funcione este "silencio"?
Por favor explique. Gracias.
Intente mirar el código fuente de AudioManager :
public void setMicrophoneMute(boolean on){
IAudioService service = getService();
try {
service.setMicrophoneMute(on);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setMicrophoneMute", e);
}
}
La tarea de silenciar el micrófono se delega a un servicio llamado IAudioService :
public void setMicrophoneMute(boolean on) {
if (!checkAudioSettingsPermission("setMicrophoneMute()")) {
return;
}
synchronized (mSettingsLock) {
if (on != mMicMute) {
AudioSystem.muteMicrophone(on);
mMicMute = on;
}
}
}
Que, a su vez, lo delega a AudioSystem que parece implementarse en código nativo :
status_t AudioSystem::muteMicrophone(bool state) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setMicMute(state);
}
Que, a su vez, lo delega a IAudioFlinger como se puede encontrar en 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();
}
Para profundizar en la respuesta de an00b: s anterior y en la versión editada de la pregunta, debemos profundizar en el código fuente. IAudioflinger es la interfaz para el servicio AudioFlinger y la llamada a
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();
}
Es en realidad la transacción de Binder para silenciar el micrófono. El lado receptor de la llamada de Binder se ve así:
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;
...
}
}
y llama a la implementación real de setMicMute en el AudioFlinger . El siguiente paso es observar esta función:
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;
}
Aquí podemos observar dos cosas. El primero es que hay una verificación de permisos para poder silenciar el micrófono. El permiso que se está verificando en settingsAllowed es android.permission.MODIFY_AUDIO_SETTINGS, tal como se mencionó en uno de los comentarios de arriba, el primer requisito para silenciar el micrófono es que su aplicación haya declarado que necesita este permiso. Lo siguiente a tener en cuenta es que ahora llamamos a la versión específica de hardware de setMicMute usando mAudioHardware-> setMicMute (estado).
Para obtener más información sobre la forma en que se enchufa el hardware, estudie el archivo AudioHardwareInterface.cpp. Básicamente termina en un libhardware con una llamada externa C para crear Audio Hardware que conecta el AudioHardWare correcto para la plataforma. También hay interruptores para usar un hardware basado en A2DP, uno genérico para el emulador y el audio. Suponiendo que está trabajando en un dispositivo real, la implementación dependerá en gran medida del hardware. Para tener una idea, podemos usar el hardware de audio disponible de Crespo (Nexus S) como ejemplo.
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;
}
En base a este ejemplo, podemos concluir con una discusión sobre la implementación del enrutamiento de audio en teléfonos inteligentes. Como puede ver en la implementación de Crespo, la llamada de silencio de micrófono solo se respetará si no está en una llamada. La razón de esto es que el audio se enruta a través de la banda base analógica que maneja la regulación de potencia, la amplificación y otras cosas. Cuando está en una llamada, el audio de la voz a menudo es manejado por la banda de base analógica y la CPU del módem juntas y no se enruta a través de la CPU de la aplicación. En ese caso, es posible que deba pasar por la CPU del módem a través del RIL para silenciar el micrófono. Pero dado que este comportamiento depende del hardware, no hay una solución general.
Para dar la versión corta a sus 4 preguntas adicionales:
La bandera se pasa a través de varias capas de código hasta que termina en el micrófono mudo específico del hardware.
El micrófono se desconecta cuando el código específico del hardware se ha ejecutado, excepto cuando se realiza una llamada, al menos en algunos dispositivos.
Cuando setMicrophoneMute no silencia el micrófono, es decir, cuando en una llamada se puede hacer eso usando una de las API de telefonía, sugiero que se estudie la aplicación del teléfono.
Basado en la implementación actual, el silenciamiento parece funcionar cuando no está en una llamada, pero puede haber problemas específicos de hardware en plataformas que no hemos estudiado aquí.
EDITAR:
Hizo algunas excavaciones más y la forma de enviar un comando de silencio a la CPU del módem es a través de la interfaz interna del teléfono que es parte del paquete com.android.internal.telephony que no está disponible para los desarrolladores de SDK. Basado en el comentario que vio que esta función solo debería ser utilizada por aplicaciones que reemplazan la administración de audio o la aplicación de telefonía original, supongo que se suponía que AudioManager.setMicrophoneMute () siempre silenciara el micrófono independientemente. Pero dado que otras aplicaciones probablemente utilizan esto, agregaron un control en la implementación del hardware para no estropear el estado de la aplicación del teléfono que realiza un seguimiento de las conexiones silenciadas, así como el micrófono. Es probable que la función no funcione como se supone en este momento debido a los detalles de implementación del hardware y al hecho de que mute es una operación mucho más compleja de lo que inicialmente se podría pensar al considerar estados de llamada.