Relacionado con el coche
Después de buscar un poco, solo este lugar tiene configurado mFocusListener, usemos Car para investigar.
paquetes/servicios/Coche/servicio/src/com/android/coche/CarAudioService.java
inicio vacío público () { AudioPolicy . Constructor constructor = nueva AudioPolicy . Constructor ( mContexto ); ... constructor . setAudioPolicyFocusListener ( mSystemFocusListener ); ... }
mSystemFocusListener
//SystemFocusListener是CarAudioService的内部类 private class SystemFocusListener extends AudioPolicyFocusListener { public void onAudioFocusGrant ( AudioFocusInfo afi , int requestResult ) { ... if ( requestResult == AudioManager . AUDIOFOCUS_REQUEST_GRANTED ) { sincronizado ( mLock ) { mPending FocusChanges . agregarPrimero ( afi ); } mFocusHandler .handleAndroidFocusChange (); } ... } public void onAudioFocusLoss ( AudioFocusInfo afi , boolean wasNotified ) { // No entiendo el motivo, en resumen, es seguro ignorar la pérdida como ganancia de seguimiento en este lugar. suficiente. Al menos el oyente inferior estará // siempre allí y recibiendo la concesión de atención. Por lo tanto, es seguro ignorar esto aquí. } }
Definición de mFocusHandler:
mFocusHandlerThread = nuevo HandlerThread ( CarLog . TAG_AUDIO ); mFocusHandler = new CarAudioFocusChangeHandler ( mFocusHandlerThread . getLooper ());
mFocusHandler.handleAndroidFocusChange
privado void handleAndroidFocusChange () { cancelFocusReleaseRequest (); Mensaje msg = obtener Mensaje ( MSG_ANDROID_FOCUS_CHANGE ); enviarMensaje ( mensaje ); }
case MSG_ANDROID_FOCUS_CHANGE : doHandleAndroidFocusChange ( false /* triggerByStreamChange * /); romper ;
Encontré una función más complicada doHandleAndroidFocusChange@CarAudioService
private void doHandleAndroidFocusChange ( boolean triggerByStreamChange ) { ... //Se realizó durante onAudioFocusGrant@SystemFocusListener antes de //mPendingFocusChanges.addFirst(afi); newTopInfo = mPendingFocusChanges .getFirst (); //Esta es una LinkedList . No sé por qué mPendingFocusChanges debe borrarse inmediatamente después de getFirst .clear (); ... if ( newTopInfo ! = null ) { if ( newTopInfo . getGainRequest () == AudioManager . AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ) { //La nueva solicitud es para obtener un pato temporal //Deje que el antiguo jefe baje de categoría a adjunto mSecondaryFocusInfo = mPrimaryFocusInfo ; } else { //De lo contrario, no establezca el adjunto mSecondaryFocusInfo = null ; } //El nuevo se convierte en el jefe mPrimaryFocusInfo = newTopInfo ; } //Consulte el análisis más adelante focusRequested = handleCarFocusRequestAndResponseLocked (); ... //Si hay una respuesta, o es obligatorio un tiempo de espera if ( focusRequested ) { doHandleCarFocusChange (); } }
newTopInfo se remonta a mFocusStack.peek().toAudioFocusInfo() de MediaFocusControl, que en realidad es la información actual del propietario del enfoque de audio en la parte superior de la pila mFocusStack.
Aquí primero hacemos un análisis de handleCarFocusRequestAndResponseLocked
private boolean handleCarFocusRequestAndResponseLocked () { boolean focusRequested = reevaluateCarAudioFocusAndSendFocusLocked (); if ( focusRequested ) { ... // envía el cambio de contexto después de obtener la respuesta de enfoque. //处理context的改变!关键的新概念,CarAudioContext if ( mCarAudioContextChangeHandler != null ) { mCarAudioContextChangeHandler . requestContextChangeNotification ( mAudioContextChangeListener , mCurrentPrimaryAudioContext , mCurrentPrimaryPhysicalStream ); } // ¿Comprobar el estado de funcionamiento del bus can? checkCanStatus (); } devuelve foco solicitado ; }
Esta función finalmente devuelve focusRequested, devuelto desde reevaluateCarAudioFocusAndSendFocusLocked
Echemos un vistazo a esta enorme función.
reevaluarCarAudioFocusAndSendFocusLocked@CarAudioService
if ( mPrimaryFocusInfo == null ) { if ( mSystemSoundPhysicalStreamActive ) { // finalmente llamado mVehicleHal.set(AUDIO_FOCUS).to(payload); //payload = { request, streams, extFocus, audioContexts }; return requestFocusForSystemSoundOnlyCaseLocked (); } . .. } ... // Después de una sección de evaluación del cálculo y la asignación, asigne //focusToRequest, streamsToRequest, extFocus, //audioContexts, routeHintChanged estos parámetros ... return sendFocusRequestToCarIfNecessaryLocked ( focusToRequest , streamsToRequest , extFocus , contextos de audio , rutaHintChanged ); }
Y sendFocusRequestToCarIfNecessaryLocked también llama
maudiohal . requestAudioFocusChange ( focusToRequest , streamsToRequest , extFocus , audioContexts );
Entonces
int [] carga útil = { solicitud , flujos , extFocus , audioContexts }; mVehicleHal . establecer ( AUDIO_ENFOQUE ). a ( carga útil );
Dado que se han analizado llamadas similares anteriormente, se requiere que el proveedor lo implemente más adelante.
hardware/interfaces/automoción/vehículo/2.0/tipos.hal
La primera son sus anotaciones (no se publica el texto original, hay demasiadas)
AUDIO_FOCUS = ( 0x0900 | VehiclePropertyGroup : SYSTEM | VehiclePropertyType : INT32_VEC | VehicleArea : GLOBAL ),
Representa el estado del foco de audio en el lado de Android. Tenga en cuenta que el módulo de audio del automóvil debe tener foco de audio y otorgar (¿permiso, liberación?) foco de audio al lado de Android (cuando lo solicite el lado de Android). El foco tiene características de transmisión y características globales.
preguntar:
La solicitud de enfoque (obtener de esta propiedad) debe tener el siguiente formato (el índice definido en VehicleAudioFocusIndex) (al igual que la llamada anterior)
int32Values[0]: VehicleAudioFocusRequest tipo
int32Values[1]: indicadores de bit de flujos solicitados por esta solicitud de enfoque.
Puede haber hasta 32 flujos.
int32Values[2]:Indicadores de estado de foco externo....
int32Values[3]:Contextos de audio que desean estar activos....
respuesta:
Respuesta de enfoque (devolución de llamada establecida y de suscripción para esta propiedad)
int32Values[0]: tipo VehicleAudioFocusState
int32Values[1]: indicadores de bits de flujos permitidos.
int32Values[2]: Estado de foco externo...(GANANCIA,GANANCIA_TRANSIENT,PÉRDIDA,PÉRDIDA_TRANSIENT)
int32Values[3]: contexto(s) de audio que pueden estar activos. Al responder positivamente a un
solicitud de enfoque de Android, el contexto original de la solicitud debe ser
repetido aquí. Al desviar el foco, o negar una solicitud, el
el contexto rechazado o detenido tendría su bit correspondiente borrado.
Contexto de audio: Cuando se aprueba, repite la solicitud, si se rechaza o se detiene, el contexto debe borrar el bit correspondiente.
Cada solicitud de enfoque debe tener una respuesta de enfoque, incluso si el estado de enfoque no ha cambiado. Esto puede suceder en el caso de una solicitud de enfoque
solo implica un cambio de contexto donde el lado de Android todavía necesita una respuesta de enfoque coincidente para confirmar que el módulo de audio ha realizado los cambios necesarios (contexto 有变化的时候).
Si el automóvil no es compatible con AUDIO_FOCUS, se supone que siempre se otorga el enfoque.
//Una propiedad para permitir que un componente externo controle el foco de audio. //No sé qué hacer.//El formato de datos es el mismo que la propiedad AUDIO_FOCUS.(El formato de datos es el mismo que AUDIO_FOCUS) AUDIO_FOCUS_EXT_SYNC = ( 0x0910 | VehiclePropertyGroup : SYSTEM | VehiclePropertyType : INT32_VEC | VehicleArea : GLOBAL ),
El VehicleAudioFocusIndex mencionado en los comentarios
/** * Índice en int32Values para la propiedad VehicleProperty#AUDIO_FOCUS. */ enum VehicleAudioFocusIndex : int32_t { FOCUS = 0 , FLUJOS = 1 , EXTERNAL_FOCUS_STATE = 2 , AUDIO_CONTEXTS = 3 , };
Ejemplo de VehicleAudioFocusIndex (la definición de VehicleAudioFocusIndex se genera automáticamente):
servicios/Coche/servicio/src/com/android/coche/hal/AudioHalService.java
... int focusState = vec . get ( VehículoAudioFocusIndex . FOCUS ); flujos int = vec . get ( VehículoAudioFocusIndex . FLUJOS ); int focoexterno = vec . get ( VehicleAudioFocusIndex . EXTERNAL_FOCUS_STATE ); ...
Ejemplo de uso de AudioContext:
/packages/services/Car/service/src/com/android/car/VolumeUtils.java
public static final int [] CAR_AUDIO_CONTEXT = { VehicleAudioContextFlag . MUSIC_FLAG , VehicleAudioContextFlag . NAVIGATION_FLAG , VehicleAudioContextFlag . VOICE_COMMAND_FLAG , VehicleAudioContextFlag . CALL_FLAG , Indicador de contexto de audio del vehículo . ALARM_FLAG Indicador de contexto de audio del vehículo . NOTIFICATION_FLAG , VehicleAudioContextFlag . UNKNOWN_FLAG , VehicleAudioContextFlag . SAFETY_ALERT_BANDERA , Indicador de contexto de audio del vehículo . CD_ROM_FLAG , VehicleAudioContextFlag . AUX_AUDIO_FLAG , Indicador de contexto de audio del vehículo . SYSTEM_SOUND_FLAG , VehicleAudioContextFlag . RADIO_BANDERA }; //Android传统的stream到CarContext的转换public static int androidStreamToCarContext ( int logicalAndroidStream ) { switch ( logicalAndroidStream ) { case AudioManager . STREAM_VOICE_CALL : devuelve VehicleAudioContextFlag . LLAMADA_BANDERA ; caso AudioManager . STREAM_SYSTEM : devuelve VehicleAudioContextFlag . SISTEMA_SONIDO_BANDERA ; caso AudioManager . STREAM_RING : devuelve VehicleAudioContextFlag . LLAMADA_BANDERA ; caso AudioManager . STREAM_MUSIC : devuelve VehicleAudioContextFlag . MÚSICA_BANDERA ; caso AudioManager . STREAM_ALARM : devuelve VehicleAudioContextFlag . ALARMA_BANDERA ; caso AudioManager . STREAM_NOTIFICATION : devuelve VehicleAudioContextFlag . NOTIFICACIÓN_BANDERA ; caso AudioManager . STREAM_DTMF : devuelve VehicleAudioContextFlag . SISTEMA_SONIDO_BANDERA ; predeterminado : devuelve VehicleAudioContextFlag . BANDERA_DESCONOCIDA ; } }
Vi el ejemplo de mVehicleHal.set(AUDIO_FOCUS).to(payload) anteriormente. Averigüemos de nuevo, el proceso de respuesta en el lado del vehículo.
onPropertyEvent@VehicleHal
public void onPropertyEvent ( ArrayList < VehiclePropValue > propValues ) { for ( VehiclePropValue v : propValues ) { //mPropertyHandlers已经存有对应关系HalServiceBase service = mPropertyHandlers . obtener ( v . prop ); servicio _ getDispatchList (). agregar ( v ); mServicesToDispatch . agregar ( servicio ); } ... para ( HalServiceBase s : mServicesToDispatch ) { s . handleHalEvents ( s . getDispatchList ()); ... } }
Debido a que VehicleHal ya ha atado las propiedades que le importan a cada servicio al llamar a init, por lo que cuando haya un evento popValues en este lugar, puede encontrar el servicio correspondiente.Si no me cree, eche un vistazo:
//init@VehicleHal Collection < VehiclePropConfig > tomado = servicio . TakeSupportedProperties ( propiedades ); for ( VehiclePropConfig p : tomado ) { mPropertyHandlers . agregar ( p . prop , servicio ); }
takeSupportedProperties@AudioHalService
public sincronizado Collection < VehiclePropConfig > takeSupportedProperties ( Collection < VehiclePropConfig > allProperties ) { for ( VehiclePropConfig p : allProperties ) { switch ( p . prop ) { //Estos son todos los accesorios que le interesan a nuestro AudioHalService. case Vehicle AUDProperty_ . : case VehicleProperty AUDIO_VOLUME : case VehicleProperty _ . AUDIO_VOLUME_LIMIT : caso VehicleProperty . AUDIO_HW_VARIANT : caso PropiedadVehículo . AUDIO_EXT_ROUTING_HINT : caso VehicleProperty . AUDIO_PARAMETERS : case VehicleProperty . AUDIO_STREAM_STATE : mProperties . poner ( p . prop , p ); romper ; } } devuelve nueva ArrayList <> ( mProperties . valores ()); }
El anterior s.handleHalEvents es
handleHalEvents@AudioHalService
public void handleHalEvents ( List < VehiclePropValue > valores ) { AudioHalFocusListener focusListener ; AudioHalVolumeListener volumenListener ; OnParameterChangeListener parámetroListener ; sincronizado ( esto ) { focusListener = mFocusListener ; VolumeListener = mVolumeListener ; parameterListener = mOnParameterChangeListener ; } DispatchEventToListener ( focusListener , volumenListener , parámetroListener , valores ); }
El VehicleAudioFocusIndex mencionado anteriormente aparecerá pronto
dispatchEventToListener@AudioHalService
... caso PropiedadVehículo . AUDIO_FOCUS : { ArrayList < Integer > vec = v . valor _ int32Valores ; //即是types.hal中定义的AUDIO_FOCUS的response方式(¿dónde está Audio Context?) int focusState = vec . get ( VehículoAudioFocusIndex . FOCUS ); flujos int = vec . get ( VehículoAudioFocusIndex . FLUJOS ); int externalFocus = vec . get ( VehicleAudioFocusIndex . EXTERNAL_FOCUS_STATE ); if ( focusListener != null ) { focusListener . onFocusChange ( focusState , streams , externalFocus ); } } ...
Quien es este Listrner, no te lo pienses, eso es todo
onFocusChange@CarAudioService
public void onFocusChange ( int focusState , int streams , int externalFocus ) { sincronizado ( mLock ) { mFocusReceived = FocusState . crear ( focusState , streams , externalFocus ); // activa el subproceso esperando la respuesta del foco. mlock . notificar a todos (); } mFocusHandler . manejarCambioEnfoque (); }
Después de omitir algunos pasos, aquí estamos.
doHandleCarFocusChange@CarAudioService
private void doHandleCarFocusChange () { ... newFocusState = ??? ... switch ( newFocusState ) { case AudioHalService . VEHICLE_AUDIO_FOCUS_STATE_GAIN : doHandleFocusGainFromCar ( mCurrentFocusState , topInfo , systemSoundActive ); romper ; caso AudioHalService . VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT : doHandleFocusGainTransientFromCar ( mCurrentFocusState , topInfo , sonido del sistema activo ); romper ; caso AudioHalService . VEHICLE_AUDIO_FOCUS_STATE_LOSS : doHandleFocusLossFromCar ( mCurrentFocusState , topInfo ); romper ; caso AudioHalService . VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT : doHandleFocusLossTransientFromCar ( mCurrentFocusState ); romper ; caso AudioHalService . VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK : doHandleFocusLossTransientCanDuckFromCar ( mCurrentFocusState ); romper ; caso AudioHalService . VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE : doHandleFocusLossTransientExclusiveFromCar ( mCurrentFocusState ); romper ; } }
LossTransientExclusive)FromCar Esta serie de funciones básicamente llama a mAudioManager.abandonAudioFocus y otras funciones tradicionales.