3

              Android java层音频相关的分析与理解(三)调用相关

 

 

Android中会有多个应用需要支持音频的播放。当同一时间内有多个应用需要输出音频的时候,到底是全部输出?还是输出其中一个?假如输出其中一个,那到底输出哪个?以什么标准去界定?为了处理这些关系。Android在2.3的时候引入了AudioFocus机制并沿用到现在。

 

1 AudioFocus简介

 

AudioFocus是一个没有优先级概念的抢占式的机制。一般情况下,最后申请使用AudioFoucs的应用或取得当前的AudioFocus从而将前面申请的应用给停掉。但是,有一种情况是特殊的,就是通话。通话作为手机的重要功能,从响铃到通话接受,Telephony都会申请AudioFocus,所有应用都不能从Telephony中获取音频焦点,但Telephony可以从任何应用中获取到AudioFocus。

 

当前Android6.0下AudioFocus有几种状态,分别为:

 

从上面AudioFocus的状态可以看出,其实AudioFocus的状态是对应的。当后一个应用去GainAudioFocus时,前一个应用就相对应地LossAudioFocus。后一个应用以什么形式去Gain,前一个应该就会受到相应Loss的通知。但是,当前一个应用收到这个Loss通知时,应用怎么处理是应用内部的事情。因为AudioFoucs机制是一个建议性而不是强制性的机制。应用使用这种机制只是使用户体验更加好,而不采用这套机制,甚至不遵循这套机制,对应用与系统本身来是,是没有什么影响的。

 

再补充说明一下,当应用通过AudioFocus机制去输出音频的时候,它可以因为其他应用通过获取AudioFocus去暂停输出。但是,应用也可以不通过AudioFocus去输出音频。此时该应用的一切音频输出逻辑由本身控制。不受AudioFocus机制影响。

 

 

2 AudioFocus的实现

 

AudioFocus机制其实相当于一个栈。

 

 

如上图所示,AudioFocus机制是以栈的形式实现,位于栈顶的应用能获取到AudioFocus从而能输出音频。非栈顶应用会暂时失去AudioFocus直至位于其上面的全部应用关闭或主动丢失AudioFocus。

AudioFocus的实现是从AudioManager.Java开始的。

一般来说,应用内部会通过调用AudioManager的requestAudioFocus()方法去获取AudioFocus。AudioManager这一次,与以往不同,不只是作为一个类似于传递者的角色直接获取AudioService的句柄然后将参数传递过去,而是先通过一系列的判断和封装,通过2次重载,将应用的相关消息保存并调用AudioSercvice中的requestAudioFocus(),进一步处理获取AudioFocus操作。

 

 

所以我们来看一下AudioManager的requestAudioFocus()的主要内容:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;"><span style="font-size:12px;">public int requestAudioFocus (OnAudioFocusChangeListener l,  
  2.             @NonNull AudioAttributes requestAttributes,  
  3.             int durationHint,  
  4.             int flags,  
  5.             AudioPolicy ap) throws IllegalArgumentException {  
  6.         ……//一系列的参数判断  
  7.         int status = AUDIOFOCUS_REQUEST_FAILED;  
  8.         //将申请AudioFocus的应用的listener放进mAudioFocusIdListenerMap中  
  9. registerAudioFocusListener(l);  
  10.         IAudioService service = getService();  
  11.         try {  
  12.             //调用AudioService的requestAudioFocus()  
  13.             status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,  
  14.                     mAudioFocusDispatcher, getIdForAudioFocusListener(l),  
  15.                     getContext().getOpPackageName() /* package name */, flags,  
  16.                     ap != null ? ap.cb() : null);  
  17.         } catch (RemoteException e) {  
  18.             Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);  
  19.         }  
  20.         return status;  
  21.     }  
  22. </span></span>  

 

在requestAudioFocus()中,与以往的稍有不同的是,requestAudioFocus()先一步对传入的参数进行处理,并将申请应用的listener放进了mAudioFocusIdListenerMap中保存。而传入的参数中,多了mAudioFocusDispatcher和getIdForAudioFocusListener(l)。getIdForAudioFocusListener(l)很明显,就是将这个listener的名字传进去。那mAudioFocusDispatcher到底是干什么的呢?

 

接下来,我们先来关注一下mAudioFocusDispatcher:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;"><span style="font-size:12px;">    private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {  
  2.   
  3.         public void dispatchAudioFocusChange(int focusChange, String id) {  
  4.             Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);  
  5.             mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m);  
  6.         }  
  7.     };  
  8. </span></span>  

 

从上面我们可以看出,mAudioFocusDispatcher是IaudioFocusDispatcher的一个实现。他里面有一个方法,负责调用mAudioFocusEventHandlerDelegate去处理消息的内容。那mAudioFocusEventHandlerDelegate又是干什么呢?我们继续往下看。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;"><span style="font-size:12px;">    private class FocusEventHandlerDelegate {  
  2.         private final Handler mHandler;  
  3.         FocusEventHandlerDelegate() {  
  4.             Looper looper;  
  5.             if ((looper = Looper.myLooper()) == null) {  
  6.                 looper = Looper.getMainLooper();  
  7.             }  
  8.             if (looper != null) {  
  9.                 // implement the event handler delegate to receive audio focus events  
  10.                 mHandler = new Handler(looper) {  
  11.                     @Override  
  12.                     public void handleMessage(Message msg) {  
  13.                         OnAudioFocusChangeListener listener = null;  
  14.                         synchronized(mFocusListenerLock) {  
  15.                             listener = findFocusListener((String)msg.obj);  
  16.                         }  
  17.                         if (listener != null) {  
  18.                             Log.d(TAG, "AudioManager dispatching onAudioFocusChange("  
  19.                                     + msg.what + ") for " + msg.obj);  
  20.                             listener.onAudioFocusChange(msg.what);  
  21.                         }  
  22.                     }  
  23.                 };  
  24.             } else {  
  25.                 mHandler = null;  
  26.             }  
  27.         }  
  28.         Handler getHandler() {  
  29.             return mHandler;  
  30.         }  
  31.     }  
  32. </span></span>  

从上面我们可以看出,调用mAudioFocusEventHandlerDelegate就是为了回调传入的listener的onAudioFocusChange方法去处理相关的内容。那明明可以直接通过l.onAudioFocusChange(focusChange)去处理这些信息,为什么要多此一举专门用一个handle去处理了?于是我去查了一下啊,原因如下:因为这个回调尚在Binder的调用线程中,如果在这里因为用户传入的listener的代码有问题而爆出异常或阻塞甚至恶意拖延,则会导致Binder的另一端因异常而崩溃或阻塞。所以到此为止,AudioService已经尽到了通知的义务,应该通过Handler将后续的操作发往另外一个线程,使AudioService尽可能远离回调现实的影响。

 

所以,现在我们可以进入AudioService去看一下到底AudioService是怎样处理的。

AudioService. requestAudioFocus()如下:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;"><span style="font-size:12px;">public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,  
  2.             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,  
  3.             IAudioPolicyCallback pcb) {  
  4.         // 检测权限  
  5.         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {     //奇怪的地方1  
  6.             if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {  
  7.                 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(  
  8.                             android.Manifest.permission.MODIFY_PHONE_STATE)) {  
  9.                     Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());  
  10.                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  11.                 }  
  12.             } else {  
  13.                 //注册一个AudioPolicy去获取AudioFocus  
  14.                 synchronized (mAudioPolicies) {    //奇怪的地方2  
  15.                     if (!mAudioPolicies.containsKey(pcb.asBinder())) {  
  16.                         Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus");  
  17.                         return AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  18.                     }  
  19.                 }  
  20.             }  
  21.         }  
  22.   
  23.         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,  
  24.                 clientId, callingPackageName, flags);  
  25.     }  
  26. </span></span>  

在上面假如仔细看的话,有2个地方会觉得很奇怪。第一个,AudioManager.AUDIOFOCUS_FLAG_LOCK ,第二个mAudioPolicies。AudioManager.AUDIOFOCUS_FLAG_LOCK代表着AudioFocus被锁定的状态。处于这种状态下假如应用需要获取AudioFoucs,首先得去registerAudioPolicy。这就和AudioPolicy相关。

 

在这里AudioPolicy与AudioPolicyService一点关系的没有。前者在java层,只是负责提供简单的音频路由和音频焦点的管理;后者处于C++层负责处理音频相关的路由策略。在这里,由于我们一般不会用到AudioPoluicy和AudioManager.AUDIOFOCUS_FLAG_LOCK,所以这个会在4.3稍作解析就过了。

 

回到AudioService. requestAudioFocus(),我们其实可以发现,真正处理AudioFocus的,是MediaFocusControl……


所以现在就来看一下MediaFocusControl是怎么处理的。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;"><span style="font-size:12px;">   protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,  
  2.             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {  
  3.          ……  
  4.         //跳过各种判断  
  5.         synchronized(mAudioFocusLock) {  
  6.           ……  
  7.             //通过AudioFocusDeathHandler对申请AudioFocus的应用进行监听  
  8.             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);  
  9.             try {  
  10.                 cb.linkToDeath(afdh, 0);  
  11.             } catch (RemoteException e) {  
  12.                 ……  
  13.                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  14.             }  
  15.               
  16. // mFocusStack就是AudioFocus机制所在的栈  
  17. // 如果申请AudioFocus的应用已经获取的AudioFocus,不作任何处理  
  18.             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {  
  19.                 ……  
  20.             }  
  21.   
  22.             // 如果已经在Stack中已经存在去这个应用的请求,就先remove掉它  
  23.             removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);  
  24.               
  25.             //新建一个FocusRequester对象  
  26.             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,  
  27.                     clientId, afdh, callingPackageName, Binder.getCallingUid(), this);  
  28.             if (focusGrantDelayed) {  
  29.                 //处于可延迟获取AudioFocus状态(正在通话)下,先把它放进Stack中  
  30.                 final int requestResult = pushBelowLockedFocusOwners(nfr);  
  31.                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {  
  32.                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);  
  33.                 }  
  34.                 return requestResult;  
  35.             } else {  
  36.                   
  37.                 if (!mFocusStack.empty()) {  
  38.                     //使上一个占有AudioFocus的应用失去AudioFocus  
  39.                     propagateFocusLossFromGain_syncAf(focusChangeHint);  
  40.                 }  
  41.   
  42.                 //将当前的request放在栈顶  
  43.                 mFocusStack.push(nfr);  
  44.             }  
  45.             //通知系统该应用获取AudioFocus  
  46.             notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),  
  47.                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);  
  48.   
  49.         }//synchronized(mAudioFocusLock)  
  50.   
  51.         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;  
  52.     }  
  53. </span></span>  

 

在这里我们可以看到真正的requestAudioFocus()处理。其实主要的流程就是:

1) 判断是否需要延迟获取AudioFocus(通话状态),判断应用是否已经拥有AudioFocus等等。

2) 让上一个占有AudioFocus的应用失去AudioFocus,并通过一系列的操作回调它的onAudioFocusChange方法。(流程较简单,不详细叙述了)

3) 将当前应用FocusRequester放到栈顶,并通知系统当前应用获取AudioFocus

 

失去AudioFocus的操作(abandonAudioFocus)和requestAudioFocus是大同小异的。所以在这里也不详细说明了。

 

3 AudioPolicy的使用

最后在这里简单说一下java层的AudioPolicy的理解。AudioService会有一个HashMap的对象mAudioPolicies,里面保存着所有registerAudioPolicy()过的应用的信息。通过registerAudioPolicy()我们可以对AudioFocus进行锁定并不允许不注册AudioPolicy的应用修改。但是这个功能其实我们大部分时候都不会用到。

音频调用相关就简单分析到这。再重申一下,AudioFocus是一个建议非强制机制,我们不适用AudioFocus都不会影响应用的音频输出与输入。

posted on 2017-02-09 00:27  信假名如  阅读(304)  评论(0编辑  收藏  举报

导航