文章目录

  • 简介-AudioFocus
    • AudioFocus机制
    • 实现流程
      • requestAudioFocus方法:
        • registerAudioFocusListener()
          • OnAudioFocusChangeListener:
      • AudioManager中还有一个电话相关的调用
        • requestAudioFocus方法实际的实现在AudioService.java
      • MediaFocusControl.java中的requestAudioFocus方法过程
      • FocusRequester处理FocusRequester对象:
        • handleFocusLoss()
          • dispatchAudioFocusChange()
      • 释放焦点
        • abandonAudioFocus()
          • unregisterAudioFocusListener()
          • removeFocusStackEntry

简介-AudioFocus

AudioFocus是一个Audio协调机制,当多方需要使用Audio资源时可以通过AudioFocus机制来协调配合。

AudioFocus机制

使用AudioFocus机制主要通过android.media.AudioManager中的requestAudioFocus方法请求获取焦点,若获取成功返回int值AudioManager.AUDIOFOCUS_REQUEST_GRANTED,失败则返回AudioManager.AUDIOFOCUS_REQUEST_FAILED

实现流程

requestAudioFocus方法:

  1. OnAudioFocusChangeListener: 是一个接口,仅定义了一个方法onAudioFocusChange,具体实现如下文
  2. requestAttributes: 根据传进来的streamType构造AudioAttributes对象向下传递,该对象存储了一些音频流信息的属性,对flag进行&操作,(由于之前传入为0,则转换后还是0)
  3. 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 checkingif (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 checksif ((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 focussynchronized (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);
}

MediaFocusControl.java中的requestAudioFocus方法过程

AudioService.java相当于中转站将参数传入MediaFocusControl类中的requestAudioFocus方法,返回结果

  1. 判断mFocusStack值的size不能超过100
  2. 检查当前栈顶的元素是否是Phone应用占用状态,若Phone处于占用状态,那么focusGrantDelayed = true
  3. 压栈之前,需要检查当前栈中是否已经有这个应用的记录,如含有则删除。
    若mFocusStrack不为null(!mFocusStack.empty()),且栈顶的clientId与需要申请焦点的clientId相同(mFocusStack.peek().hasSameClient(clientId)),得到栈顶元素即FocusRequester对象。若申请的时长(focusChangeHint)及flag相同,则表示申请重复,直接返回AUDIOFOCUS_REQUEST_GRANTED;若二者有一个不同则表示需要重新申请,同时此时focusGrantDelayed为false需要将栈顶元素出栈并将其释放
  4. removeFocusStackEntry-- 移除可能在栈中(栈顶或栈中)其他位置存在着相同的clientId的元素
    • 栈顶:释放后通知栈顶应用,使其获得audioFocus
    • 非栈顶:只是移除该记录,无需更改当前audioFocus的占有情况
  5. 创建FocusRequester实例将其请求包含的各种信息传入
  6. 如果focusGrantDelayed = true会延迟申请,进入pushBelowLockedFocusOwners方法,在方法中把此次请求FocusRequester实例入栈,而非被压在栈顶,放在lastLockedFocusOwnerIndex位置,即电话记录之后,则电话的焦点释放后则该请求开始申请; 如果focusGrantDelayed = false则无需延迟获取焦点,同样创建FocusRequester实例,但需先通知栈中其他记录失去焦点,然后入栈,最后通知自己获得焦点
  7. 继续会进入propagateFocusLossFromGain_syncAf方法,遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法,通知栈中其他元素丢失焦点流程。通过while(stackIterator.hasNext())得到FocusRequester对象,进入handleExternalFocusGain方法
  8. 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 clientsif (!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 laterfocusGrantDelayed = true;}}// handle the potential premature death of the new holder of the focus// (premature death == death before abandoning focus)// Register for client death notificationAudioFocusDeathHandler 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;}// 注解3if (!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 anythingfinal 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 ownerif (!focusGrantDelayed) {mFocusStack.pop();// the entry that was "popped" is the same that was "peeked" abovefr.release();}}// focus requester might already be somewhere below in the stack, remove it// 注解4removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);// 注解5final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,clientId, afdh, callingPackageName, Binder.getCallingUid(), this);// 注解6if (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 stackif (!mFocusStack.empty()) {propagateFocusLossFromGain_syncAf(focusChangeHint);}// push focus requester at the top of the audio focus stackmFocusStack.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 errorLog.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",new Exception());// no exclusive owner, push at top of stack, focus is granted, propagate changepropagateFocusLossFromGain_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 gainIterator<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 {// onewaypcb.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 {// onewaypcb.notifyAudioFocusLoss(afi, wasDispatched);} catch (RemoteException e) {Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "+ pcb.asBinder(), e);}}
}

FocusRequester处理FocusRequester对象:

根据MediaFocusControl.java中propagateFocusLossFromGain_syncAf传递来的nfr.getGainRequest()值做焦点的获取/丢失处理

变量:

  1. gainRequest: 根据传入值做对应的返回值
  • 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN,return AudioManager.AUDIOFOCUS_LOSS
  • 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
  1. 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;}
}
handleFocusLoss()

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 loserif (!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);}
}-----------------------------------------------------------------------
dispatchAudioFocusChange()
- 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 servicemHandler = new Handler(looper) {@Overridepublic 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()方法释放焦点。由于音频焦点是唯一的,故可以在需要播放音乐时申请音频焦点,如获取到了则播放,同时正在播放的音频在失去焦点时停止播放或做降音处理,以此协调

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;
}
------------------------------------------------------------------------------
unregisterAudioFocusListener()
/*** @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 locallysynchronized(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方法

removeFocusStackEntry

同以上一直移除栈中的当前需要移除的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 zonesLog.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 neededsynchronized(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 focusnotifyTopOfAudioFocusStack();}} 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 referencesfr.release();}}}
}

音频焦点AudioFocus相关推荐

  1. 音频焦点(AudioFocus)应用与源码解析

    音频焦点(AudioFocus) 基础介绍 在我们的Android设备中会安装不止一个多媒体应用,如果不制定一个有效合理的规则,应用程序各自为政,那么可能就会出现各种音视频软件的混音,这是非常影响用户 ...

  2. Android音频焦点AudioFocus使用

    Android开发中免不了需要播放视频,音频文件,但是手机上可能有其他的一些软件,在后台播放音频的时候,这个时候另外的软件也需要播放音频,这个时候就会出现俩个音频同时播放的问题,在Android2.2 ...

  3. Android 8.0 后使用AudioFocus音频焦点处理音频抢占问题

    今天写了一个关于播放audio的简单demo,发现程序无法抢占其他音频,具体表现在当有其他播放器在播放audio的时候,如果demo播放audio的话,那么会有两个audio同时播放:而且其他播放器也 ...

  4. Android音频焦点申请处理

    为了便于理解,我们以android的8.0以前的版本为例,8.0以后有一定改动,但是基本思路一样. 关于管理音频焦点(8.0以前和更高版本)的官方文档:https://developer.androi ...

  5. Android官方开发文档Training系列课程中文版:管理音频播放之管理音频焦点

    原文地址:http://android.xsoftlab.net/training/managing-audio/audio-focus.html 因为可能会存在多个APP播放音频,所以考虑它们之间的 ...

  6. Android 音频焦点(Audio Focus)

    原址 CONTENTS 引子 音频焦点 一个简单的示例 注意: 引子 说 Audio Focus 前先说个很简单需求:来电时暂停正在播放的音乐,电话结束时恢复播放. 看到这个需求,第一反应肯定是:监听 ...

  7. Android音频焦点

    因为系统中可能会有多个应用程序会播放音频,所以需要考虑他们之间该如何交互,为了避免多个应用程序同时播放 音乐,Android 系统使用音频焦点来进行统一管理,即只有获得了音频焦点的应用程序才可以播放音 ...

  8. android音频焦点Audio Focus

    为了便于理解,我们以android的8.0以前的版本为例,8.0以后有一定改动,但是基本思路一样. 关于管理音频焦点(8.0以前和更高版本)的官方文档:https://developer.androi ...

  9. 【安卓R 源码】获取音频焦点和释放音频焦点

    一. 获取焦点流程 1. 电话焦点只有系统可以申请,如果是电话焦点,系统会把所有多媒体和游戏的音频流实例全部mute.同理电话焦点释放会解除mute操作 2. 系统管理的焦点栈有大小限制限制为100. ...

最新文章

  1. android values-v21 style 报错,Android 4.4 以上实现透明导航栏和状态栏 Translucent system bar...
  2. .NET Mvc Razor也可以这样玩!
  3. Linux期末复习编程题
  4. 【转】如何更改VS2010的[默认开发语言]默认环境设置 .
  5. 分类和目标检测的性能评价指标【转载】
  6. unity android屏幕自适应,Android应用开发之unity打开移动摄像头,并自适应屏幕显示摄像头数据。兼容android和ios...
  7. python读取pcap获得端口_Python处理网络数据包示例(pcapy读pcap文件)
  8. iometer使用教程linux,Iometer磁盘测试工具中文使用说明手册 二
  9. VS2015下安装Visual_Assist_x破解版
  10. 【附干货】卸载CAD后将注册表清理干净的方法及步骤
  11. 【详细教程】阿里云ECS服务器搭建
  12. chrome 插件开发入门
  13. 关闭Dell笔记本触摸板
  14. 挂载iphone ipsw升级包
  15. 怎么让面试官喜欢你?
  16. Vim 编辑器真的 yyds
  17. 使用ccle下载细胞系IC50数据
  18. 豆瓣社区:《如何高效学习》应用与心得
  19. 电脑网络连接为什么常常连接不上
  20. ROS2极简总结-Nav2-行为树

热门文章

  1. Python正则表达式嵌套组
  2. 他是阿里p11,靠写代码写成合伙人
  3. 修改网页服务器数据,如何修改网页服务器穿的数据库
  4. 微信小程序商城如何快速对接物流模块
  5. 贵金属跌跌不休竞相比惨黄金跌去二位数钯金跌去三位数
  6. C语言分阶段求利润问题
  7. 4k视频编辑台式计算机,字节跳动剪辑工具 “剪映”PC 版体验:支持 4K 视频分辨率...
  8. 为什么有些共同生活在一起很多年的夫妻,遇到男方破产或者有负债的情况时女方会离开男方?...
  9. 用软件创建一个mysql的数据库_JSP中的数据库操作(1):MySQL数据库创建及管理...
  10. python做接口外部调用_python使用suds调用外部接口