文章目录
简介-AudioFocus
AudioFocus是一个Audio协调机制,当多方需要使用Audio资源时可以通过AudioFocus机制来协调配合。
AudioFocus机制
使用AudioFocus机制主要通过android.media.AudioManager中的requestAudioFocus方法请求获取焦点,若获取成功返回int值AudioManager.AUDIOFOCUS_REQUEST_GRANTED,失败则返回AudioManager.AUDIOFOCUS_REQUEST_FAILED
实现流程
requestAudioFocus方法:
- OnAudioFocusChangeListener: 是一个接口,仅定义了一个方法onAudioFocusChange,具体实现如下文
- requestAttributes: 根据传进来的streamType构造AudioAttributes对象向下传递,该对象存储了一些音频流信息的属性,对flag进行&操作,(由于之前传入为0,则转换后还是0)
- durationHint: 获取焦点的时长,同时通知其他获取音频焦点的OnAudioFocusChangeListener该相互配合,对应值信息:
- AUDIOFOCUS_GAIN 代表此次申请的音频焦点需要长时间持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS。
- AUDIOFOCUS_GAIN_TRANSIENT 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:适用于短暂的音频,在接收到事件通知等情景时可使用该durationHint。
- AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:在需要录音或语音识别等情景时可使用该durationHint。
- AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 代表此次申请不需要暂停其它申请的音频播放,应用跟其他应用共用焦点但播放的时候其他音频会降低音量。原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK。
- frameworks/base/media/java/com/android/media/AudioManager.java
@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
int flags,
AudioPolicy ap) throws IllegalArgumentException {
// parameter checking
if (requestAttributes == null) {
throw new IllegalArgumentException("Illegal null AudioAttributes argument");
}
// durationHint 表示获取焦点的时长
if ((durationHint < AUDIOFOCUS_GAIN) ||
(durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
throw new IllegalArgumentException("Invalid duration hint");
}
if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
throw new IllegalArgumentException("Illegal flags 0x"
+ Integer.toHexString(flags).toUpperCase());
}
if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) {
throw new IllegalArgumentException(
"Illegal null focus listener when flagged as accepting delayed focus grant");
}
if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {
throw new IllegalArgumentException(
"Illegal null audio policy when locking audio focus");
}
int status = AUDIOFOCUS_REQUEST_FAILED;
// 注册所有的AudioFocusRequest,具体方法见下
registerAudioFocusListener(l);
IAudioService service = getService();
try {
// 通过IAudioService通信实现audio焦点的获取
status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
getContext().getOpPackageName() /* package name */, flags,
ap != null ? ap.cb() : null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return status;
}
---------------------------------------------------------------
registerAudioFocusListener()
/**
* @hide
* Registers a listener to be called when audio focus changes. Calling this method is optional
* before calling {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, as it
* will register the listener as well if it wasn't registered already.
* @param l the listener to be notified of audio focus changes.
*/
public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
synchronized(mFocusListenerLock) {
// mAudioFocusIdListenerMap根据AudioFocusRequest中OnAudioFocusChangeListener对象生成一个key,value存储在mAudioFocusIdListenerMap对象中
if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) {
return;
}
mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l);
}
}
OnAudioFocusChangeListener:
是一个接口,仅定义了一个方法onAudioFocusChange(int focusChange),该方法在焦点状态变化时被调用,参数focusChange代表变化后当前状态,共有四个值:
- AUDIOFOCUS_GAIN 重新获取到音频焦点时触发的状态。
- AUDIOFOCUS_LOSS 失去音频焦点时触发的状态,且该状态应该会长期保持,此时应当暂停音频并释放音频相关的资源。
- AUDIOFOCUS_LOSS_TRANSIENT 失去音频焦点时触发的状态,但是该状态不会长时间保持,此时应该暂停音频,且当重新获取音频焦点的时候继续播放。
- AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 失去音频焦点时触发的状态,在该状态的时候不需要暂停音频,但是应该降低音频的声音
/**
* Interface definition for a callback to be invoked when the audio focus of the system is
* updated.
*/
public interface OnAudioFocusChangeListener {
/**
* Called on the listener to notify it the audio focus for this listener has been changed.
* The focusChange value indicates whether the focus was gained,
* whether the focus was lost, and whether that loss is transient, or whether the new focus
* holder will hold it for an unknown amount of time.
* When losing focus, listeners can use the focus change information to decide what
* behavior to adopt when losing focus. A music player could for instance elect to lower
* the volume of its music stream (duck) for transient focus losses, and pause otherwise.
* @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
* {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
* and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
*/
public void onAudioFocusChange(int focusChange);
}
AudioManager中还有一个电话相关的调用
AudioService中的requestAudioFocus并且往其中设置了一个clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID的状态。
进入到AudioService.requestAudioFocus中首先会进行权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID 也就是说如果clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID,且要申请到MODIFY_PHONE_STATE的权限,否则会申请焦点失败。
/**
* @hide
* Used internally by telephony package to request audio focus. Will cause the focus request
* to be associated with the "voice communication" identifier only used in AudioService
* to identify this use case.
* @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for
* the establishment of the call
* @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
* media applications resume after a call
*/
public void requestAudioFocusForCall(int streamType, int durationHint) {
IAudioService service = getService();
try {
service.requestAudioFocus(new AudioAttributes.Builder()
.setInternalLegacyStreamType(streamType).build(),
durationHint, mICallBack, null,
AudioSystem.IN_VOICE_COMM_FOCUS_ID,
getContext().getOpPackageName(),
AUDIOFOCUS_FLAG_LOCK,
null /* policy token */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
requestAudioFocus方法实际的实现在AudioService.java
- frameworks/base/service/core/java/com/android/sever/audio/AudioService.java
//==========================================================================================
// Audio Focus
//==========================================================================================
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
IAudioPolicyCallback pcb) {
// permission checks
if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
if