Android java層音頻相關的分析與理解(四)音頻外設相關



轉載自http://blog.csdn.net/u012440406/article/details/51147200

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
  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
  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
  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
  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交給底層去處理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章