Android Audio和耳機,藍牙耳機等音頻外設
做音頻開發,避免不了和藍牙打交道,尤其是做系統開發,又是不同的模塊,所以很多問題很難界定。因而,瞭解一下藍牙,對整體的系統架的理解會更加完善。同樣的,有線耳機和A2DP在Audio的處理有很多相似處。
藍牙連接處理
Audio這邊提提供了多種方式和藍牙進行交互~
廣播接收
AudioService定義了AudioServiceBroadcastReceiver,會接收藍牙的廣播:
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
... ...
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
... ...
intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
AudioManager接口
public void startBluetoothSco()
public void stopBluetoothSco()
public boolean isBluetoothScoOn()
public void setBluetoothScoOn(boolean on)
public boolean isBluetoothA2dpOn()
public void startBluetoothScoVirtualCall()
public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
Listener監聽
這裏主要是監聽BluetoothProfile.ServiceListener,監聽服務斷開和打開
/**
* An interface for notifying BluetoothProfile IPC clients when they have
* been connected or disconnected to the service.
*/
public interface ServiceListener {
/**
* Called to notify the client when the proxy object has been
* connected to the service.
* @param profile - One of {@link #HEALTH}, {@link #HEADSET} or
* {@link #A2DP}
* @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or
* {@link BluetoothA2dp}
*/
public void onServiceConnected(int profile, BluetoothProfile proxy);
/**
* Called to notify the client that this proxy object has been
* disconnected from the service.
* @param profile - One of {@link #HEALTH}, {@link #HEADSET} or
* {@link #A2DP}
*/
public void onServiceDisconnected(int profile);
}
藍牙耳機和AudioService的交互
這裏用來測試的藍牙耳機是Philips SHB5800。連接時,會上報A2DP和SCO兩個profile的連接事件。
藍牙的狀態
藍牙的狀態,主要有如下幾個狀態,定義在BluetoothProfile中:
* frameworks/base/core/java/android/bluetooth/BluetoothProfile.java
/** The profile is in disconnected state */
public static final int STATE_DISCONNECTED = 0;
/** The profile is in connecting state */
public static final int STATE_CONNECTING = 1;
/** The profile is in connected state */
public static final int STATE_CONNECTED = 2;
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
A2DP給AudioService上報連接狀態
A2DP是通過AudioManager接口和Audio模塊進行交互的,通過setBluetoothA2dpDeviceConnectionState接口,Bluetooth會調兩次,上報兩次狀態。
第一次,狀態爲state = 1
,STATE_CONNECTING 連接中狀態:
04-28 09:21:38.653 27417 27468 D AudioManager: setBluetoothA2dpDeviceConnectionState: state = 1
第二次,狀態爲state = 2
,STATE_CONNECTED 已連接狀態:
04-28 09:21:42.168 27417 27468 D AudioManager: setBluetoothA2dpDeviceConnectionState: state = 2
兩次狀態上報,都是Bluetooth應用上報的,調用棧如下:
04-28 09:21:38.653 27417 27468 W System.err: at android.media.AudioManager.setBluetoothA2dpDeviceConnectionState(AudioManager.java:3698)
04-28 09:21:38.653 27417 27468 W System.err: at com.android.bluetooth.a2dp.A2dpStateMachine.broadcastConnectionState(A2dpStateMachine.java:952)
04-28 09:21:38.653 27417 27468 W System.err: at com.android.bluetooth.a2dp.A2dpStateMachine.-wrap4(Unknown Source:0)
04-28 09:21:38.654 27417 27468 W System.err: at com.android.bluetooth.a2dp.A2dpStateMachine$Disconnected.processMessage(A2dpStateMachine.java:304)
04-28 09:21:38.654 27417 27468 W System.err: at com.android.internal.util.StateMachine$SmHandler.processMsg(StateMachine.java:992)
04-28 09:21:38.654 27417 27468 W System.err: at com.android.internal.util.StateMachine$SmHandler.handleMessage(StateMachine.java:809)
04-28 09:21:38.654 27417 27468 W System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
04-28 09:21:38.654 27417 27468 W System.err: at android.os.Looper.loop(Looper.java:175)
04-28 09:21:38.655 27417 27468 W System.err: at android.os.HandlerThread.run(HandlerThread.java:65)
AudioService的setBluetoothA2dpDeviceConnectionState實現如下:
* frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
{
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
return 0;
}
return setBluetoothA2dpDeviceConnectionStateInt(
device, state, profile, AudioSystem.DEVICE_NONE);
}
setBluetoothA2dpDeviceConnectionStateInt只處理A2DP和A2DP_SINK:
public int setBluetoothA2dpDeviceConnectionStateInt(
BluetoothDevice device, int state, int profile, int musicDevice)
{
int delay;
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
throw new IllegalArgumentException("invalid profile " + profile);
}
synchronized (mConnectedDevices) {
if (profile == BluetoothProfile.A2DP) {
int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
intState, musicDevice);
} else {
delay = 0;
}
queueMsgUnderWakeLock(mAudioHandler,
(profile == BluetoothProfile.A2DP ?
MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
state,
0 /* arg2 unused */,
device,
delay);
}
return delay;
}
對A2DP,MSG_SET_A2DP_SINK_CONNECTION_STATE處理如下:
case MSG_SET_A2DP_SINK_CONNECTION_STATE:
onSetA2dpSinkConnectionState((BluetoothDevice)msg.obj, msg.arg1);
mAudioEventWakeLock.release();
break;
具體處理在onSetA2dpSinkConnectionState中處理:
private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state)
{
... ...
synchronized (mConnectedDevices) {
final String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
btDevice.getAddress());
final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
boolean isConnected = deviceSpec != null;
if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
if (btDevice.isBluetoothDock()) {
if (state == BluetoothProfile.STATE_DISCONNECTED) {
// introduction of a delay for transient disconnections of docks when
// power is rapidly turned off/on, this message will be canceled if
// we reconnect the dock under a preset delay
makeA2dpDeviceUnavailableLater(address, BTA2DP_DOCK_TIMEOUT_MILLIS);
// the next time isConnected is evaluated, it will be false for the dock
}
} else {
makeA2dpDeviceUnavailableNow(address);
}
synchronized (mCurAudioRoutes) {
if (mCurAudioRoutes.bluetoothName != null) {
mCurAudioRoutes.bluetoothName = null;
sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
SENDMSG_NOOP, 0, 0, null, 0);
}
}
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
if (btDevice.isBluetoothDock()) {
// this could be a reconnection after a transient disconnection
cancelA2dpDeviceTimeout();
mDockAddress = address;
} else {
// this could be a connection of another A2DP device before the timeout of
// a dock: cancel the dock timeout, and make the dock unavailable now
if(hasScheduledA2dpDockTimeout()) {
cancelA2dpDeviceTimeout();
makeA2dpDeviceUnavailableNow(mDockAddress);
}
}
makeA2dpDeviceAvailable(address, btDevice.getName(),
"onSetA2dpSinkConnectionState");
synchronized (mCurAudioRoutes) {
String name = btDevice.getAliasName();
if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
mCurAudioRoutes.bluetoothName = name;
sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
SENDMSG_NOOP, 0, 0, null, 0);
}
}
}
}
}
- state 爲 1,STATE_CONNECTING時,什麼也沒有做,走不進調節判斷中去。
- state 爲 12,STATE_CONNECTED時,走到
else if
,關鍵的是調到了makeA2dpDeviceAvailable
makeA2dpDeviceAvailable 函數如下:
private void makeA2dpDeviceAvailable(String address, String name, String eventSource) {
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
setBluetoothA2dpOnInt(true, eventSource);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
// Reset A2DP suspend state each time a new sink is connected
AudioSystem.setParameters("A2dpSuspended=false");
mConnectedDevices.put(
makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
new DeviceListSpec(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
address));
sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, null, 0);
}
makeA2dpDeviceAvailable 時,主要做了以下幾件事:
- 發送消息MSG_SET_DEVICE_VOLUME, 設置新的A2DP的音量,各種類型的音量都會設置
04-28 11:01:47.274 823 1101 D AudioService: applyDeviceVolumemStreamType:3,device:128
04-28 11:01:47.275 823 1101 D AudioService: applyDeviceVolumemStreamType:10,device:128
04-28 11:01:47.277 823 1101 D AudioService: applyDeviceVolumemStreamType:2,device:128
04-28 11:01:47.277 823 1101 D AudioService: applyDeviceVolumemStreamType:8,device:128
04-28 11:01:47.280 823 1101 D AudioService: applyDeviceVolumemStreamType:1,device:128
- A2DP打開,setBluetoothA2dpOnInt,默認Media用A2DP
public void setBluetoothA2dpOnInt(boolean on, String eventSource) {
synchronized (mBluetoothA2dpEnabledLock) {
mBluetoothA2dpEnabled = on;
mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
setForceUseInt_SyncDevices(AudioSystem.FOR_MEDIA,
mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
eventSource);
}
}
強制使用A2DP
private void setForceUseInt_SyncDevices(int usage, int config, String eventSource) {
if (usage == AudioSystem.FOR_MEDIA) {
sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
SENDMSG_NOOP, 0, 0, null, 0);
}
mForceUseLogger.log(new ForceUseEvent(usage, config, eventSource));
if(LOGD){
Log.i(TAG,new StringBuilder("setForceUse(")
.append(AudioSystem.forceUseUsageToString(usage))
.append(", ").append(AudioSystem.forceUseConfigToString(config))
.append(") due to ").append(eventSource).toString());
}
AudioSystem.setForceUse(usage, config);
}
強制使用A2DP通過,AudioSystem的setForceUse接口完成的。
給native報A2DP連接的狀態,通過AudioSystem的setDeviceConnectionState接口完成
將連接上的A2DP添加到mConnectedDevices中,以DeviceListSpec進行封裝
回到onSetA2dpSinkConnectionState函數,一般會上報新的Audio線路MSG_REPORT_NEW_ROUTES。
SCO給AudioService上報連接狀態
相比A2DP的接口調用,SCO是通過Receiver監聽的。AudioServiceBroadcastReceiver在收到ACTION_CONNECTION_STATE_CHANGED時,Audio將同SCO的狀態。A2DP只能做音頻輸出,而SCO可以做音頻輸出也可以做音頻的輸入。
輸出: AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET(0x20)
輸入: AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET (0x8)
大概的棧如下:
04-28 14:13:19.599 805 805 W System.err: at com.android.server.audio.AudioService.handleDeviceConnection(AudioService.java:5604)
04-28 14:13:19.599 805 805 W System.err: at com.android.server.audio.AudioService.setBtScoDeviceConnectionState(AudioService.java:3481)
04-28 14:13:19.599 805 805 W System.err: at com.android.server.audio.AudioService$AudioServiceBroadcastReceiver.onReceive(AudioService.java:5971)
ACTION_CONNECTION_STATE_CHANGED是從Bluetooth發出來的。
private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
... ...
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
setBtScoDeviceConnectionState(btDevice, state);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
setBtScoDeviceConnectionState中,將處理SCO的input和output。
void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
... ...
// 類型轉換
String address = btDevice.getAddress();
BluetoothClass btClass = btDevice.getBluetoothClass();
int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
if (btClass != null) {
switch (btClass.getDeviceClass()) {
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
break;
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
break;
}
}
// 處理連接,outDevice和inDevice
boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
String btDeviceName = btDevice.getName();
boolean success =
handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
handleDeviceConnection(connected, inDevice, address, btDeviceName);
// 只要headset是連着的,就不斷開
if ((state == BluetoothProfile.STATE_DISCONNECTED ||
state == BluetoothProfile.STATE_DISCONNECTING) &&
mBluetoothHeadset != null &&
mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
Log.w(TAG, "SCO connected through another device, returning");
return;
}
// 賦值給mBluetoothHeadsetDevice ***BUG? 貌似有Bug,兩個設備的時候怎麼處理?***
synchronized (mScoClients) {
if (connected) {
mBluetoothHeadsetDevice = btDevice;
} else {
mBluetoothHeadsetDevice = null;
resetBluetoothSco();
}
}
}
setBtScoDeviceConnectionState大部分處理在handleDeviceConnection中完成
private boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
synchronized (mConnectedDevices) {
String deviceKey = makeDeviceListKey(device, address);
DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);
boolean isConnected = deviceSpec != null;
if (connect && !isConnected) { // connect現在的狀態,isConnected原來的狀態
final int res = AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);
if (res != AudioSystem.AUDIO_STATUS_OK) {
Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device) +
" due to command error " + res );
return false;
}
mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
device, 0, null, 0);
return true;
} else if (!connect && isConnected) {
AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName);
// always remove even if disconnection failed
mConnectedDevices.remove(deviceKey);
return true;
}
}
return false;
}
- 連接時,通過AudioSystem的setDeviceConnectionState接口,同步到native層。注意這裏的參數爲AudioSystem.DEVICE_STATE_AVAILABLE
- SCO的設備同樣添加到mConnectedDevices中
- 如果是unmute的設備,通過MSG_ACCESSORY_PLUG_MEDIA_UNMUTE,恢復音量
private void onAccessoryPlugMediaUnmute(int newDevice) {
if (DEBUG_VOL) {
Log.i(TAG, String.format("onAccessoryPlugMediaUnmute newDevice=%d [%s]",
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
}
synchronized (mConnectedDevices) {
if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
&& (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
&& (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0)
{
mStreamStates[AudioSystem.STREAM_MUSIC].mute(false);
}
}
}
有線耳機的連接狀態
耳機的連接和Bluetooth的連接很類似,耳機可以做輸出也可以做爲輸入:
輸出: AUDIO_DEVICE_OUT_WIRED_HEADSET(0x4)
輸入: AUDIO_DEVICE_IN_WIRED_HEADSET (0x10)
有線耳機基本都是通過AudioManager的setWiredDeviceConnectionState接口同步的狀態。從WiredAccessoryManager上報的狀態:
04-28 15:40:05.859 992 992 W System.err: at android.media.AudioManager.setWiredDeviceConnectionState(AudioManager.java:3670)
04-28 15:40:05.859 992 992 W System.err: at com.android.server.WiredAccessoryManager.setDeviceStateLocked(WiredAccessoryManager.java:293)
04-28 15:40:05.859 992 992 W System.err: at com.android.server.WiredAccessoryManager.setDevicesState(WiredAccessoryManager.java:249)
04-28 15:40:05.859 992 992 W System.err: at com.android.server.WiredAccessoryManager.-wrap1(Unknown Source:0)
04-28 15:40:05.859 992 992 W System.err: at com.android.server.WiredAccessoryManager$1.handleMessage(WiredAccessoryManager.java:232)
04-28 15:40:05.859 992 992 W System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
後續的流程也是通過handleDeviceConnection函數處理的,前面我們已經分析過。
04-28 15:16:51.860 805 1097 W System.err: at com.android.server.audio.AudioService.handleDeviceConnection(AudioService.java:5604)
04-28 15:16:51.860 805 1097 W System.err: at com.android.server.audio.AudioService.onSetWiredDeviceConnectionState(AudioService.java:5830
Audio添加音頻設備
不管是A2DP,SCO,還是有線耳機,都會通過AudioSystem的setDeviceConnectionState函數,將配件的狀態轉給底層。下面我來看看,底層是怎麼處理的。
AudioPolicy處理音頻設備狀態
setDeviceConnectionState是一個native函數,通過JNI,將調到了native的AudioSystem中。
status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
... ...
return aps->setDeviceConnectionState(device, state, address, name);
}
最終是在AudioPolicyManager.cpp中處理的:
status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
return setDeviceConnectionStateInt(device, state, device_address, device_name);
}
AudioPolicyManager用兩個DeviceVector,分別管理系統中的可用的音頻輸入輸出設備。
DeviceVector mAvailableOutputDevices; // all available output devices
DeviceVector mAvailableInputDevices; // all available input devices
在setDeviceConnectionStateInt函數中,對設備的狀態進行處理,根據狀態維護mAvailableOutputDevices
和mAvailableInputDevices
。
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
// connect/disconnect only 1 device at a time
if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE;
sp<DeviceDescriptor> devDesc =
mHwModules.getDeviceDescriptor(device, device_address, device_name);
先獲取狀態變化的設備getDeviceDescriptor。getDeviceDescriptor是從hw_mode中去獲取驅動支持的設備。
下面,分分別來看輸入,輸入的設備的狀態:
狀態值:
* system/media/audio/include/system/audio_policy.h
typedef enum {
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
AUDIO_POLICY_DEVICE_STATE_CNT,
AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1,
} audio_policy_dev_state_t;
音頻輸出設備處理
輸出設備,用mAvailableOutputDevices維護,主要是連接和斷開時的處理。聽筒,Speaker等手機自帶的設備,一般只有啓動的時候纔會處理一下,其他時間都是不需要處理的。所以主要是還是我們所說的,藍牙耳機,有線耳機等。
- 音頻設備連接
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
... ...
// handle output devices
if (audio_is_output_device(device)) {
SortedVector <audio_io_handle_t> outputs;
ssize_t index = mAvailableOutputDevices.indexOf(devDesc);
mPreviousOutputs = mOutputs;
switch (state)
{
// handle output device connection
case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {
... ...
// register new device as available
index = mAvailableOutputDevices.add(devDesc);
if (index >= 0) {
sp<HwModule> module = mHwModules.getModuleForDevice(device);
if (module == 0) {
ALOGD("setDeviceConnectionState() could not find HW module for device %08x",
device);
mAvailableOutputDevices.remove(devDesc);
return INVALID_OPERATION;
}
mAvailableOutputDevices[index]->attach(module);
} else {
return NO_MEMORY;
}
// Before checking outputs, broadcast connect event to allow HAL to retrieve dynamic
// parameters on newly connected devices (instead of opening the outputs...)
broadcastDeviceConnectionState(device, state, devDesc->mAddress);
if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) {
mAvailableOutputDevices.remove(devDesc);
broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
devDesc->mAddress);
return INVALID_OPERATION;
}
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
... ...
} break;
輸出設備連接時,主要做以下幾件事:
1. 將新連接的音頻設備,添加到mAvailableOutputDevices中,且讓設備和具體的HwModule關聯。
2. 廣播設備已連接狀態,broadcastDeviceConnectionState
3. checkOutputsForDevice,確認設備是連接是否正確
4. 將狀態傳給mEngine
- 音頻設備斷開
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
... ...
// handle output devices
if (audio_is_output_device(device)) {
.... ....
// handle output device disconnection
case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
... ...
// Send Disconnect to HALs
broadcastDeviceConnectionState(device, state, devDesc->mAddress);
// remove device from available output devices
mAvailableOutputDevices.remove(devDesc);
checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress);
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
} break;
default:
ALOGE("setDeviceConnectionState() invalid state: %x", state);
return BAD_VALUE;
}
斷開時,將設備從mAvailableOutputDevices中刪掉,其他處理和連接時類似。注意這裏的checkOutputsForDevice
,這裏將更新輸出設備mOutputs。
設備更新完成後,就需要更新策略了,切換通路,確保音頻的輸出通路可用且正確。
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
... ...
// handle output devices
if (audio_is_output_device(device)) {
.... ....
// checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP
// output is suspended before any tracks are moved to it
checkA2dpSuspend();
checkOutputForAllStrategies();
// outputs must be closed after checkOutputForAllStrategies() is executed
if (!outputs.isEmpty()) {
for (size_t i = 0; i < outputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
// close unused outputs after device disconnection or direct outputs that have been
// opened by checkOutputsForDevice() to query dynamic parameters
if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
(((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
(desc->mDirectOpenCount == 0))) {
closeOutput(outputs[i]);
}
}
// check again after closing A2DP output to reset mA2dpSuspended if needed
checkA2dpSuspend();
}
updateDevicesAndOutputs();
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
updateCallRouting(newDevice);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
bool force = !desc->isDuplicated()
&& (!device_distinguishes_on_address(device)
// always force when disconnecting (a non-duplicated device)
|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
newDevice = mpAudioPolicyMTKInterface->fm_correctDeviceFromSetDeviceConnectionStateInt(desc, newDevice, force);
setOutputDevice(desc, newDevice, force, 0);
}
}
if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
cleanUpForDevice(devDesc);
}
mpClientInterface->onAudioPortListUpdate();
return NO_ERROR;
} // end if is output device
這裏主要做了以下幾件事:
1. 阻塞式的檢查A2DP的狀態 checkA2dpSuspend。
2. 檢查所以策略的輸出 checkOutputForAllStrategies
3. 更新輸出設備 updateDevicesAndOutputs
4. 更新電話通路 updateCallRouting
5. 設置輸出設備 setOutputDevice,包括音量等。
6. 回調給Client,音頻斷開更新 onAudioPortListUpdate。
音頻輸入設備處理
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
... ...
// handle input devices
if (audio_is_input_device(device)) {
SortedVector <audio_io_handle_t> inputs;
ssize_t index = mAvailableInputDevices.indexOf(devDesc);
switch (state)
{
// handle input device connection
case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {
... ...
// Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic
// parameters on newly connected devices (instead of opening the inputs...)
broadcastDeviceConnectionState(device, state, devDesc->mAddress);
if (checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress) != NO_ERROR) {
broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
devDesc->mAddress);
return INVALID_OPERATION;
}
index = mAvailableInputDevices.add(devDesc);
if (index >= 0) {
mAvailableInputDevices[index]->attach(module);
} else {
return NO_MEMORY;
}
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
} break;
// handle input device disconnection
case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
... ...
// Set Disconnect to HALs
broadcastDeviceConnectionState(device, state, devDesc->mAddress);
checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress);
mAvailableInputDevices.remove(devDesc);
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
} break;
default:
ALOGE("setDeviceConnectionState() invalid state: %x", state);
return BAD_VALUE;
}
closeAllInputs();
// As the input device list can impact the output device selection, update
// getDeviceForStrategy() cache
updateDevicesAndOutputs();
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
updateCallRouting(newDevice);
}
if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
cleanUpForDevice(devDesc);
}
mpClientInterface->onAudioPortListUpdate();
return NO_ERROR;
} // end if is input device
ALOGW("setDeviceConnectionState() invalid device: %x", device);
return BAD_VALUE;
}
音頻輸入設備的連接和斷開處理,和輸出設備的類似,這裏就不多說,這裏維護的是mAvailableInputDevices。
選擇輸出設備
播放音頻時,將選擇具體的音頻設備~~
getDevicesForStream
getDeviceForStrategy
這裏選擇的設備就是我們添加到mAvailableOutputDevices中的可用的輸出設備。
錄音時,也爲輸入source選擇設備。
getDeviceForInputSource
這裏選擇的設備就是我們添加到mAvailableInputDevices中的可用的輸入設備。
這裏起個頭,我們後續再介紹~