4

 

Android java层音频相关的分析与理解(四)音频外设相关(转)

 

 

1 音频外设状态

 

要对音频外设进行管理,所以我们必须明确当前Andorid系统支持的外设设备有哪些。当前Andorid6.0是通过一个整型变量去针对不同的音频外设进行标志与表示。任何可用的音频外设在这个整型变量中用1个二进制的标志为去表示。具体的音频表示如下:

 

 

根据以上的标志,Andorid通过设置mHeadsetState的值去表示当前设备的状态。

 

2 监测外设的WiredAccessoryManager

当前Android主要通过WiredAccessoryManager去监测音频外设的插入和拔出。WiredAccessoryManager开机的时候已经通过SystemServer.java间接启动并分配了一个自由的线程,一旦监测到有音频外设的操作,这线程就会进行处理。WiredAccessoryManager刚开始时通过WiredAccessoryObserver去处理外设消息。WiredAccessoryObserver启动的流程如下:

 

 

WiredAccessoryObserver启动是蛮简单的。系统服务SystemServer通过systemRunning()启动输入管理服务InputManagerService,InputManagerService在此过程中通过callback机制

启动WiredAccessoryManager并使其去读取当前的外设状态。WiredAccessoryManager是通过WiredAccessoryObserver去管理音频外设状态的,所以最后WiredAccessoryManager启动WiredAccessoryObserver并通过startObserving()去针对每种类型的硬件启动一个线程,对当前的外设状态进行判断。

 

当Android连接或者断开连接设备的时候,此时底层通过Native层回掉InputManagerService的notifySwitch从而调用WiredAccessoryManager的notifyWiredAccessoryChanged()判断得出一个新的headset状态,再根据headset的状态去设置outDevice和inDevice的值,从而通过AudioManager设置状态。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. ……  
  2.           //根据headset状态设置outDevice和inDevice。  
  3. if (headset == BIT_HEADSET) {  
  4.                 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;  
  5.                 inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;  
  6.             } else if (headset == BIT_HEADSET_NO_MIC){  
  7.                 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;  
  8.             } else if (headset == BIT_LINEOUT){  
  9.                 outDevice = AudioManager.DEVICE_OUT_LINE;  
  10.             } else if (headset == BIT_USB_HEADSET_ANLG) {  
  11.                 outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;  
  12.             } else if (headset == BIT_USB_HEADSET_DGTL) {  
  13.                 outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;  
  14.             } else if (headset == BIT_HDMI_AUDIO) {  
  15.                 outDevice = AudioManager.DEVICE_OUT_HDMI;  
  16.             } else {  
  17.                 Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);  
  18.                 return;  
  19.             }  
  20.   
  21.             //真正设置到底层的是通过AudioManager调用AudioService去进行设置。  
  22.       if (outDevice != 0) {  
  23.        mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);  
  24.             }  
  25.       if (inDevice != 0) {  
  26.        mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);  
  27.             }  
  28. ……  

从上面可以看出,真正进行音频外设管理的是AudioManager。而AudioManager是AudioService的管理。所以更多的设置在AudioService中进行。


3 AudioService中的音频外设管理

 

在AudioService中,有一个ArrayMap的变量mConnectedDevices,在其中保存着当前的存在的音频外设信息。当连接或断开连接某个设备时,都需要更新这个ArrayMap的信息。

接下来我们接着5.2所说的内容,WiredAccessoryManager通过调用AudioManager的setWiredDeviceConnectionState()去进行设置状态。而在AudioManager的中,setWiredDeviceConnectionState()是直接调用AudioService的setWiredDeviceConnectionState()去进行设置的。

所以来对AudioService的setWiredDeviceConnectionState()进行一下简单的分析。

 

AudioService的setWiredDeviceConnectionState()代码如下:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1.    public void setWiredDeviceConnectionState(int type, int state, String address, String name,  
  2.             String caller) {  
  3.         synchronized (mConnectedDevices) {  
  4. ……  
  5. //在这里需要去检查是否应该发送becomingNoisy的Intent。从而我们可以发现,平时我们监测拔出耳机使用的becomingNoisy是在这里开始的。  
  6.             int delay = checkSendBecomingNoisyIntent(type, state);  
  7.  //接下来将信息通过queueMsgUnderWakeLock交给AudioHandler去处理  
  8. queueMsgUnderWakeLock(mAudioHandler,  
  9.                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,  
  10.                     0,  
  11.                     0,  
  12.                     new WiredDeviceConnectionState(type, state, address, name, caller),  
  13.                     delay);  
  14.         }  
  15.     }  

setWiredDeviceConnectionState()其实做的东西不多。第一,调用checkSendBecomingNoisyIntent()去判断是否需要发送拨出耳机的Intent。按照Andorid的设计理念,当我们拔出耳机时,需要将当前播放的音乐等等音频输出停掉,以免对周围造成不必要的影响(在这不针对这个详细说)。第二,新建一个WiredDeviceConnectionState对象,并将这个对象通过queueMsgUnderWakeLock()交给AudioHandler去处理。并且,在此过程中,需要按照队列的形式去处理。在此期间还会获取一个mAudioEventWakeLock,当handle处理完之后会release掉。

 

AudioHandler接收到发给它的信息,通过判断会调用onSetWiredDeviceConnectionState()进行进一步的处理。

 

AudioService.onSetWiredDeviceConnectionState()的关键代码如下:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private void onSetWiredDeviceConnectionState(int device, int state, String address,  
  2.             String deviceName, String caller) {  
  3.         ……  
  4.         synchronized (mConnectedDevices) {  
  5.             //根据状态判断,假如当前拔下普通耳机,就会将当前音频输出到蓝牙耳机(假如有的话)  
  6.             if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||  
  7.                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||  
  8.                     (device == AudioSystem.DEVICE_OUT_LINE))) {  
  9.                 setBluetoothA2dpOnInt(true);  
  10.             }  
  11.             ……  
  12.            //通过handleDeviceConnection去更新mConnectedDevices中的信息和调用AudioSystem去设置值。  
  13.             if (!handleDeviceConnection(state == 1, device, address, deviceName)) {  
  14.                 return;  
  15.             }  
  16.             if (state != 0) {  
  17.             ……  
  18.             if (!isUsb && device != AudioSystem.DEVICE_IN_WIRED_HEADSET) {  
  19.                 //发送Intent去通知外设状态变化  
  20.                 sendDeviceConnectionIntent(device, state, address, deviceName);  
  21.             }  
  22.         }  
  23.     }  

 

在onSetWiredDeviceConnectionState()中,其实最重要的就是进行2个操作:handleDeviceConnection()和sendDeviceConnectionIntent()。这两个操作一个负责对mConnectedDevices更新并通过AudioSystem去设置值进底层。一个发送Intent去通知音频外设的状态变化。所以,我们关注的重点应该是handleDeviceConnection()。

 

最后,我们来看一下handleDeviceConnection():

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private boolean handleDeviceConnection(boolean connect, int device, String address,  
  2.         String deviceName) {  
  3.     ……  
  4.     synchronized (mConnectedDevices) {  
  5.         String deviceKey = makeDeviceListKey(device, address);  
  6. 断当前设备是否在在mConnectedDevices保存的信息  
  7.         DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);  
  8.         boolean isConnected = deviceSpec != null;  
  9.         if (connect && !isConnected) {  
  10. 关设置通过AudioSystem设置进底层。  
  11.             final int res = AudioSystem.setDeviceConnectionState(device,  
  12.                     AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);  
  13.             if (res != AudioSystem.AUDIO_STATUS_OK) {  
  14.                 return false;  
  15.             }  
  16. mConnectedDevices的值  
  17.             mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));  
  18.             return true;  
  19.         } else if (!connect && isConnected) {  
  20.             AudioSystem.setDeviceConnectionState(device,  
  21.                     AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName);  
  22.             // always remove even if disconnection failed  
  23.             mConnectedDevices.remove(deviceKey);  
  24.             return true;  
  25.         }  
  26.     }  
  27.     return false;  
  28. }  

 

 

从上面我们可以看出,handleDeviceConnection()负责处理设备的连接和断开连接。他会将设备的信息更新,并通过AudioSystem设置进底层。

 

所以一个普通外设插入的序列图(简略)如下:

 

 

总结一下音频外设的拨插处理过程:

1)   InputManagerService监听到从底层的上报的设备状态变化消息。通过WiredAccessoryManager去处理。

2)   WiredAccessoryManager判断状态并将状态变化交给AudioService处理。

3)   AudioService得到通知,根据需要判断是否发送BECOMING_NOISY相关Intent,并新列表。

4)   AudioService将处理后的状态变化通过AudioSystem交给底层去处理。

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

导航