AudioFocus是一个Audio协调机制,当多方需要使用Audio资源时可以通过AudioFocus机制来协调配合。
使用AudioFocus机制主要通过android.media.AudioManager中的requestAudioFocus方法请求获取焦点,若获取成功返回int值AudioManager.AUDIOFOCUS_REQUEST_GRANTED,失败则返回AudioManager.AUDIOFOCUS_REQUEST_FAILED
- 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;
}
---------------------------------------------------------------
/**
* @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);
}
}
是一个接口,仅定义了一个方法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);
}
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();
}
}
- 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 (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)) {
Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
} else {
// only a registered audio policy can be used to lock focus
synchronized (mAudioPolicies) {
if (!mAudioPolicies.containsKey(pcb.asBinder())) {
Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
}
}
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, flags);
}
AudioService.java相当于中转站将参数传入MediaFocusControl类中的requestAudioFocus方法,返回结果
- 判断mFocusStack值的size不能超过100
- 检查当前栈顶的元素是否是Phone应用占用状态,若Phone处于占用状态,那么focusGrantDelayed = true
- 压栈之前,需要检查当前栈中是否已经有这个应用的记录,如含有则删除。
若mFocusStrack不为null(!mFocusStack.empty()),且栈顶的clientId与需要申请焦点的clientId相同(mFocusStack.peek().hasSameClient(clientId)),得到栈顶元素即FocusRequester对象。若申请的时长(focusChangeHint)及flag相同,则表示申请重复,直接返回AUDIOFOCUS_REQUEST_GRANTED;若二者有一个不同则表示需要重新申请,同时此时focusGrantDelayed为false需要将栈顶元素出栈并将其释放- removeFocusStackEntry-- 移除可能在栈中(栈顶或栈中)其他位置存在着相同的clientId的元素
- 栈顶:释放后通知栈顶应用,使其获得audioFocus
- 非栈顶:只是移除该记录,无需更改当前audioFocus的占有情况
- 创建FocusRequester实例将其请求包含的各种信息传入
- 如果focusGrantDelayed = true会延迟申请,进入pushBelowLockedFocusOwners方法,在方法中把此次请求FocusRequester实例入栈,而非被压在栈顶,放在lastLockedFocusOwnerIndex位置,即电话记录之后,则电话的焦点释放后则该请求开始申请; 如果focusGrantDelayed = false则无需延迟获取焦点,同样创建FocusRequester实例,但需先通知栈中其他记录失去焦点,然后入栈,最后通知自己获得焦点
- 继续会进入propagateFocusLossFromGain_syncAf方法,遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法,通知栈中其他元素丢失焦点流程。通过while(stackIterator.hasNext())得到FocusRequester对象,进入handleExternalFocusGain方法
- notifyExtPolicyFocusGrant_syncAf做相应的通知
// 将FocusRequester存入栈中,后续的获取及释放焦点则是对该栈的出栈入栈处理,用以维护各个client的申请和释放
private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
-frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
/** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
Log.i(TAG, " AudioFocus requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+ "/" + Binder.getCallingPid()
+ " clientId=" + clientId
+ " req=" + focusChangeHint
+ " flags=0x" + Integer.toHexString(flags));
// we need a valid binder callback for clients
if (!cb.pingBinder()) {
Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
callingPackageName) != AppOpsManager.MODE_ALLOWED) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
synchronized(mAudioFocusLock) {
boolean focusGrantDelayed = false;
if (!canReassignAudioFocus()) {
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
} else {
// request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
// granted right now, so the requester will be inserted in the focus stack
// to receive focus later
focusGrantDelayed = true;
}
}
// handle the potential premature death of the new holder of the focus
// (premature death == death before abandoning focus)
// Register for client death notification
AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
try {
cb.linkToDeath(afdh, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
// 注解3
if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
// if focus is already owned by this client and the reason for acquiring the focus
// hasn't changed, don't do anything
final FocusRequester fr = mFocusStack.peek();
if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
// unlink death handler so it can be gc'ed.
// linkToDeath() creates a JNI global reference preventing collection.
cb.unlinkToDeath(afdh, 0);
notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
// the reason for the audio focus request has changed: remove the current top of
// stack and respond as if we had a new focus owner
if (!focusGrantDelayed) {
mFocusStack.pop();
// the entry that was "popped" is the same that was "peeked" above
fr.release();
}
}
// focus requester might already be somewhere below in the stack, remove it
// 注解4
removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
// 注解5
final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
clientId, afdh, callingPackageName, Binder.getCallingUid(), this);
// 注解6
if (focusGrantDelayed) {
// focusGrantDelayed being true implies we can't reassign focus right now
// which implies the focus stack is not empty.
final int requestResult = pushBelowLockedFocusOwners(nfr);
if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
}
return requestResult;
} else {
// propagate the focus change through the stack
if (!mFocusStack.empty()) {
propagateFocusLossFromGain_syncAf(focusChangeHint);
}
// push focus requester at the top of the audio focus stack
mFocusStack.push(nfr);
}
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
}//synchronized(mAudioFocusLock)
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
----------------------------------------------------------------------------
/**
* Helper function
* Pre-conditions: focus stack is not empty, there is one or more locked focus owner
* at the top of the focus stack
* Push the focus requester onto the audio focus stack at the first position immediately
* following the locked focus owners.
* @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
* {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
*/
private int pushBelowLockedFocusOwners(FocusRequester nfr) {
int lastLockedFocusOwnerIndex = mFocusStack.size();
for (int index = mFocusStack.size()-1; index >= 0; index--) {
if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
lastLockedFocusOwnerIndex = index;
}
}
if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
// this should not happen, but handle it and log an error
Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
new Exception());
// no exclusive owner, push at top of stack, focus is granted, propagate change
propagateFocusLossFromGain_syncAf(nfr.getGainRequest());
mFocusStack.push(nfr);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
} else {
mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
}
}
----------------------------------------------------------------------------
/**
* Focus is requested, propagate the associated loss throughout the stack.
* @param focusGain the new focus gain that will later be added at the top of the stack
*/
private void propagateFocusLossFromGain_syncAf(int focusGain) {
// going through the audio focus stack to signal new focus, traversing order doesn't
// matter as all entries respond to the same external focus gain
Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
stackIterator.next().handleExternalFocusGain(focusGain);
}
}
----------------------------------------------------------------------------------
// 通知操作
/**
* Called synchronized on mAudioFocusLock
*/
void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
for (IAudioPolicyCallback pcb : mFocusFollowers) {
try {
// oneway
pcb.notifyAudioFocusGrant(afi, requestResult);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
+ pcb.asBinder(), e);
}
}
}
/**
* Called synchronized on mAudioFocusLock
*/
void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
for (IAudioPolicyCallback pcb : mFocusFollowers) {
try {
// oneway
pcb.notifyAudioFocusLoss(afi, wasDispatched);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
+ pcb.asBinder(), e);
}
}
}
根据MediaFocusControl.java中propagateFocusLossFromGain_syncAf传递来的nfr.getGainRequest()值做焦点的获取/丢失处理
变量:
- gainRequest: 根据传入值做对应的返回值
- 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN,return AudioManager.AUDIOFOCUS_LOSS
- 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
- mFocusLossReceived: 方法handleFocusLoss中赋值,默认为AudioManager.AUDIOFOCUS_NONE
-frameworks/base/services/core/java/com/android/server/audio/FocusRequester.java
/**
* Called synchronized on MediaFocusControl.mAudioFocusLock
*/
void handleExternalFocusGain(int focusGain) {
int focusLoss = focusLossForGainRequest(focusGain);
handleFocusLoss(focusLoss);
}
/**
* For a given audio focus gain request, return the audio focus loss type that will result
* from it, taking into account any previous focus loss.
* @param gainRequest
* @return the audio focus loss type that matches the gain request
*/
private int focusLossForGainRequest(int gainRequest) {
switch(gainRequest) {
case AudioManager.AUDIOFOCUS_GAIN:
switch(mFocusLossReceived) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_NONE:
return AudioManager.AUDIOFOCUS_LOSS;
}
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
switch(mFocusLossReceived) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_NONE:
return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
case AudioManager.AUDIOFOCUS_LOSS:
return AudioManager.AUDIOFOCUS_LOSS;
}
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
switch(mFocusLossReceived) {
case AudioManager.AUDIOFOCUS_NONE:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
case AudioManager.AUDIOFOCUS_LOSS:
return AudioManager.AUDIOFOCUS_LOSS;
}
default:
Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
return AudioManager.AUDIOFOCUS_NONE;
}
}
fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);通过IAudioFocusDispatcher调用dispatchAudioFocusChange方法传入mFocusLossReceived, mClientId;此处IAudioFocusDispatcher最终是在AudioManager中定义实现。IAudioFocusDispatcher回调dispatchAudioFocusChange方法
在IAudioFocusDispatcher回调dispatchAudioFocusChange方法中发送MSSG_FOCUS_CHANGE消息;在ServiceEventHandlerDelegate中创建handleMessage在其中根据MSSG_FOCUS_CHANGE回调,其中msg.arg1为focusChange即为之前传入的mFocusLossReceived,通过listener.onAudioFocusChange(msg.arg1);实现音频焦点的变化监听
/**
* Called synchronized on MediaFocusControl.mAudioFocusLock
*/
void handleFocusLoss(int focusLoss) {
try {
if (focusLoss != mFocusLossReceived) {
mFocusLossReceived = focusLoss;
// before dispatching a focus loss, check if the following conditions are met:
// 1/ the framework is not supposed to notify the focus loser on a DUCK loss
// 2/ it is a DUCK loss
// 3/ the focus loser isn't flagged as pausing in a DUCK loss
// if they are, do not notify the focus loser
if (!mFocusController.mustNotifyFocusOwnerOnDuck()
&& mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
&& (mGrantFlags
& AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
if (DEBUG) {
Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
+ " to " + mClientId + ", to be handled externally");
}
mFocusController.notifyExtPolicyFocusLoss_syncAf(
toAudioFocusInfo(), false /* wasDispatched */);
return;
}
final IAudioFocusDispatcher fd = mFocusDispatcher;
if (fd != null) {
if (DEBUG) {
Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
+ mClientId);
}
mFocusController.notifyExtPolicyFocusLoss_syncAf(
toAudioFocusInfo(), true /* wasDispatched */);
fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
}
}
} catch (android.os.RemoteException e) {
Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
}
}
-----------------------------------------------------------------------
- frameworks/base/media/java/com/android/media/AudioManager.java
private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
public void dispatchAudioFocusChange(int focusChange, String id) {
final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
mServiceEventHandlerDelegate.getHandler().sendMessage(m);
}
};
/**
* Helper class to handle the forwarding of audio service events to the appropriate listener
*/
private class ServiceEventHandlerDelegate {
private final Handler mHandler;
ServiceEventHandlerDelegate(Handler handler) {
Looper looper;
if (handler == null) {
if ((looper = Looper.myLooper()) == null) {
looper = Looper.getMainLooper();
}
} else {
looper = handler.getLooper();
}
if (looper != null) {
// implement the event handler delegate to receive events from audio service
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSSG_FOCUS_CHANGE:
OnAudioFocusChangeListener listener = null;
synchronized(mFocusListenerLock) {
listener = findFocusListener((String)msg.obj);
}
if (listener != null) {
Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
+ msg.arg1 + ") for " + msg.obj);
listener.onAudioFocusChange(msg.arg1);
}
break;
case MSSG_RECORDING_CONFIG_CHANGE:
...
break;
default:
Log.e(TAG, "Unknown event " + msg.what);
}
}
};
} else {
mHandler = null;
}
}
Handler getHandler() {
return mHandler;
}
}
通过abandonAudioFocus()方法释放焦点。由于音频焦点是唯一的,故可以在需要播放音乐时申请音频焦点,如获取到了则播放,同时正在播放的音频在失去焦点时停止播放或做降音处理,以此协调
unregisterAudioFocusListener – remove掉mAudioFocusIdListenerMap中的OnAudioFocusChangeListener
- frameworks/base/media/java/com/android/media/AudioManager.java
/**
* @hide
* Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
* @param l the listener with which focus was requested.
* @param aa the {@link AudioAttributes} with which audio focus was requested
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
*/
@SystemApi
public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
int status = AUDIOFOCUS_REQUEST_FAILED;
unregisterAudioFocusListener(l);
IAudioService service = getService();
try {
status = service.abandonAudioFocus(mAudioFocusDispatcher,
getIdForAudioFocusListener(l), aa);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return status;
}
------------------------------------------------------------------------------
/**
* @hide
* Causes the specified listener to not be called anymore when focus is gained or lost.
* @param l the listener to unregister.
*/
public void unregisterAudioFocusListener(OnAudioFocusChangeListener l) {
// remove locally
synchronized(mFocusListenerLock) {
mAudioFocusIdListenerMap.remove(getIdForAudioFocusListener(l));
}
}
进入实际实现方法的AudioService.java的abandonAudioFocus方法
- frameworks/base/service/core/java/com/android/sever/audio/AudioService.java
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) {
return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa);
}
将参数传入MediaFocusControl类中的abandonAudioFocus方法
同以上一直移除栈中的当前需要移除的Audio焦点,mFocusStack.pop(),遍历FocusRequester实例对象,release相应的焦点
notifyExtPolicyFocusLoss_syncAf:发送失去焦点的通知操作
-frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
/**
* @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
* */
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) {
// AudioAttributes are currently ignored, to be used for zones
Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+ "/" + Binder.getCallingPid()
+ " clientId=" + clientId);
try {
// this will take care of notifying the new focus owner if needed
synchronized(mAudioFocusLock) {
removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
}
} catch (java.util.ConcurrentModificationException cme) {
// Catching this exception here is temporary. It is here just to prevent
// a crash seen when the "Silent" notification is played. This is believed to be fixed
// but this try catch block is left just to be safe.
Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
cme.printStackTrace();
}
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
protected void unregisterAudioFocusClient(String clientId) {
synchronized(mAudioFocusLock) {
removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
}
}
/**
* Helper function:
* Called synchronized on mAudioFocusLock
* Remove a focus listener from the focus stack.
* @param clientToRemove the focus listener
* @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
* focus, notify the next item in the stack it gained focus.
*/
private void removeFocusStackEntry(String clientToRemove, boolean signal,
boolean notifyFocusFollowers) {
// is the current top of the focus stack abandoning focus? (because of request, not death)
if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
{
//Log.i(TAG, " removeFocusStackEntry() removing top of stack");
FocusRequester fr = mFocusStack.pop();
fr.release();
if (notifyFocusFollowers) {
final AudioFocusInfo afi = fr.toAudioFocusInfo();
afi.clearLossReceived();
notifyExtPolicyFocusLoss_syncAf(afi, false);
}
if (signal) {
// notify the new top of the stack it gained focus
notifyTopOfAudioFocusStack();
}
} else {
// focus is abandoned by a client that's not at the top of the stack,
// no need to update focus.
// (using an iterator on the stack so we can safely remove an entry after having
// evaluated it, traversal order doesn't matter here)
Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
FocusRequester fr = stackIterator.next();
if(fr.hasSameClient(clientToRemove)) {
Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for "
+ clientToRemove);
stackIterator.remove();
// stack entry not used anymore, clear references
fr.release();
}
}
}
}