1 音频外设状态
要对音频外设进行管理,所以我们必须明确当前Andorid系统支持的外设设备有哪些。当前是通过一个整型变量去针对不同的音频外设进行标志与表示。任何可用的音频外设在这个整型变量中用1个二进制的标志为去表示。具体的音频表示如下:
WiredAccessoryManager: newName=h2w newState=2 headsetState=2 prev headsetState=0
01-19 05:40:41.395 1036 1370 V WiredAccessoryManager: notifyWiredAccessoryChanged: when=673633706000 bits=SW_HEADPHONE_INSERT SW_MICROPHONE_INSERT mask=94
01-19 05:40:41.395 1036 1370 V WiredAccessoryManager: newName=h2w newState=1 headsetState=1 prev headsetState=0
01-19 05:40:41.396 1036 1370 I WiredAccessoryManager: MSG_NEW_DEVICE_STATE
01-19 05:40:41.396 1036 1036 V WiredAccessoryManager: headsetName: connected
当前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设置状态。
/frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java
public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
123 if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos
124 + " bits=" + switchCodeToString(switchValues, switchMask)
125 + " mask=" + Integer.toHexString(switchMask));
126
127 synchronized (mLock) {
128 int headset;
129 mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
130 switch (mSwitchValues &
131 (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) {
132 case 0:
133 headset = 0;
134 break;
135
136 case SW_HEADPHONE_INSERT_BIT:
137 headset = BIT_HEADSET_NO_MIC;
138 break;
139
140 case SW_LINEOUT_INSERT_BIT:
141 headset = BIT_LINEOUT;
142 break;
143
144 case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:
145 headset = BIT_HEADSET;
146 break;
147
148 case SW_MICROPHONE_INSERT_BIT:
149 headset = BIT_HEADSET;
150 break;
151
152 default:
153 headset = 0;
154 break;
155 }
156
157 updateLocked(NAME_H2W,
158 (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
159 }
160 }
private void setDeviceStateLocked(int headset,
257 int headsetState, int prevHeadsetState, String headsetName) {
258 if ((headsetState & headset) != (prevHeadsetState & headset)) {
259 int outDevice = 0;
260 int inDevice = 0;
261 int state;
262
263 if ((headsetState & headset) != 0) {
264 state = 1;
265 } else {
266 state = 0;
267 }
268
269 if (headset == BIT_HEADSET) {
270 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
271 inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
272 } else if (headset == BIT_HEADSET_NO_MIC){
273 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
274 } else if (headset == BIT_LINEOUT){
275 outDevice = AudioManager.DEVICE_OUT_LINE;
276 } else if (headset == BIT_USB_HEADSET_ANLG) {
277 outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
278 } else if (headset == BIT_USB_HEADSET_DGTL) {
279 outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
280 } else if (headset == BIT_HDMI_AUDIO) {
281 outDevice = AudioManager.DEVICE_OUT_HDMI;
282 } else {
283 Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
284 return;
285 }
286
287 if (LOG) {
288 Slog.v(TAG, "headsetName: " + headsetName +
289 (state == 1 ? " connected" : " disconnected"));
290 }
291
292 if (outDevice != 0) {
293 mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);
294 }
295 if (inDevice != 0) {
296 mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);
297 }
298 }
299 }
常见的android的优化MIC耳机识别的方法有:
private static final int BIT_HEADSET = (1 << 1);// 0010
private static final int BIT_HEADSET_NO_MIC = (1 << 0);// 0001
其实解决的只是耳机慢插时的MIC 事件上报delay导致HEADSET出现被移除的问题。
原理就是
SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:
headset = BIT_HEADSET;
SW_MICROPHONE_INSERT_BIT:
headset = BIT_HEADSET;
(mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset)
如果上次是BIT_HEADSET 这次又是BIT_HEADSET 那么是不是就正好unstall了。
从上面可以看出,真正进行音频外设管理的是AudioManager。而AudioManager是AudioService的管理。所以更多的设置在AudioService中进行。
/frameworks/base/media/java/android/media/
H A D AudioManager.java 3300 public void setWiredDeviceConnectionState(int type, int state, String address, String name) { in setWiredDeviceConnectionState() method in AudioManager
/frameworks/base/services/core/java/com/android/server/audio/
H A D AudioService.java 4042 private class WiredDeviceConnectionState { class in AudioService
4049 public WiredDeviceConnectionState(int type, int state, String address, String name, in WiredDeviceConnectionState() method in AudioService.WiredDeviceConnectionState
public void setWiredDeviceConnectionState(int type, int state, String address, String name,
4060 String caller) {
4061 synchronized (mConnectedDevices) {
4062 if (DEBUG_DEVICES) {
4063 Slog.i(TAG, "setWiredDeviceConnectionState(" + state + " nm: " + name + " addr:"
4064 + address + ")");
4065 }
4066 int delay = checkSendBecomingNoisyIntent(type, state);
//在这里需要去检查是否应该发送becomingNoisy的Intent。从而我们可以发现,平时我们监测拔出耳机使用的becomingNoisy是在这里开始的。
//接下来将信息通过queueMsgUnderWakeLock交给AudioHandler去处理
4067 queueMsgUnderWakeLock(mAudioHandler,
4068 MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
4069 0,
4070 0,
4071 new WiredDeviceConnectionState(type, state, address, name, caller),
4072 delay);
4073 }
4074 }
这里有个关于客制化的问题:
需求:拔出某些设备直接speaker播放,不暂停。
5367 // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
5368 // sent if none of these devices is connected.
5369 // Access synchronized on mConnectedDevices
5370 int mBecomingNoisyIntentDevices =
5371 AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
5372 AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI |
5373 AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
5374 AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE;
5375
5376 // must be called before removing the device from mConnectedDevices
//其中checkSendBecomingNoisyIntent又发个一个消息通知给到上层:
5377 // Called synchronized on mConnectedDevices
5378 private int checkSendBecomingNoisyIntent(int device, int state) {
5379 int delay = 0;
5380 if (mConnectedBTDevicesList.size() > 1) {
5381 Log.d(TAG, "checkSendBecomingNoisyIntent on state: " + state);
5382 return delay;
5383 }
5384
5385 if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
5386 int devices = 0;
5387 Log.d(TAG, "checkSendBecomingNoisyIntent update the noise");
5388 for (int i = 0; i < mConnectedDevices.size(); i++) {
5389 int dev = mConnectedDevices.valueAt(i).mDeviceType;
5390 if (((dev & AudioSystem.DEVICE_BIT_IN) == 0)
5391 && ((dev & mBecomingNoisyIntentDevices) != 0)) {
5392 devices |= dev;
5393 }
5394 }
5395 if (devices == device && device != AudioSystem.DEVICE_OUT_USB_DEVICE && device !=AudioSystem.DEVICE_IN_USB_DEVICE) {
//将usb的audio设备过滤掉,不发送MSG_BROADCAST_AUDIO_BECOMING_NOISY,
5396 sendMsg(mAudioHandler,
5397 MSG_BROADCAST_AUDIO_BECOMING_NOISY,
5398 SENDMSG_REPLACE,
5399 0,
5400 0,
5401 null,
5402 0);
5403 delay = SystemProperties.getInt("audio.noisy.broadcast.delay", 700);
5404 }
5405 }
5406
//MSG_BROADCAST_AUDIO_BECOMING_NOISY
//这个消息对应audiomanager中的这个类型:ACTION_AUDIO_BECOMING_NOISY
//应用收到这个通知后会做一个暂停的处理,
5407 if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) ||
5408 mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) ||
5409 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
5410 synchronized (mLastDeviceConnectMsgTime) {
5411 long time = SystemClock.uptimeMillis();
5412 if (mLastDeviceConnectMsgTime > time) {
5413 delay = (int)(mLastDeviceConnectMsgTime - time) + 30;
5414 }
5415 }
5416 }
5417 return delay;
5418 }
第二,新建一个WiredDeviceConnectionState对象,并将这个对象通过queueMsgUnderWakeLock()交给AudioHandler去处理。并且,在此过程中,需要按照队列的形式去处理。在此期间还会获取一个mAudioEventWakeLock,当handle处理完之后会release掉。
AudioHandler接收到发给它的信息,通过判断会调用onSetWiredDeviceConnectionState()进行进一步的处理。
private void onSetWiredDeviceConnectionState(int device, int state, String address,
String deviceName, String caller) {
……
synchronized (mConnectedDevices) {
//根据状态判断,假如当前拔下普通耳机,就会将当前音频输出到蓝牙耳机(假如有的话)
if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
(device == AudioSystem.DEVICE_OUT_LINE))) {
setBluetoothA2dpOnInt(true);
}
……
//通过handleDeviceConnection去更新mConnectedDevices中的信息和调用AudioSystem去设置值。
if (!handleDeviceConnection(state == 1, device, address, deviceName)) {
return;
}
if (state != 0) {
……
if (!isUsb && device != AudioSystem.DEVICE_IN_WIRED_HEADSET) {
//发送Intent去通知外设状态变化
sendDeviceConnectionIntent(device, state, address, deviceName);
}
}
}
在onSetWiredDeviceConnectionState()中,其实最重要的就是进行2个操作:handleDeviceConnection()和sendDeviceConnectionIntent()。这两个操作一个负责对mConnectedDevices更新并通过AudioSystem去设置值进底层。一个发送Intent去通知音频外设的状态变化。所以,我们关注的重点应该是handleDeviceConnection()。
5330 private boolean handleDeviceConnection(boolean connect, int device, String address,
5331 String deviceName) {
5332 if (DEBUG_DEVICES) {
5333 Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" + Integer.toHexString(device)
5334 + " address:" + address + " name:" + deviceName + ")");
5335 }
5336 synchronized (mConnectedDevices) {
5337 String deviceKey = makeDeviceListKey(device, address);
5338 if (DEBUG_DEVICES) {
5339 Slog.i(TAG, "deviceKey:" + deviceKey);
5340 }
5341 DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);
5342 boolean isConnected = deviceSpec != null;
5343 if (DEBUG_DEVICES) {
5344 Slog.i(TAG, "deviceSpec:" + deviceSpec + " is(already)Connected:" + isConnected);
5345 }
5346 if (connect && !isConnected) {
5347 final int res = AudioSystem.setDeviceConnectionState(device,
5348 AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);
5349 if (res != AudioSystem.AUDIO_STATUS_OK) {
5350 Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device) +
5351 " due to command error " + res );
5352 return false;
5353 }
5354 mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
5355 return true;
5356 } else if (!connect && isConnected) {
5357 AudioSystem.setDeviceConnectionState(device,
5358 AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName);
5359 // always remove even if disconnection failed
5360 mConnectedDevices.remove(deviceKey);
5361 return true;
5362 }
5363 }
5364 return false;
5365 }
从上面我们可以看出,handleDeviceConnection()负责处理设备的连接和断开连接。他会将设备的信息更新,并通过AudioSystem设置进底层。
总结一下音频外设的拨插处理过程:
1) InputManagerService监听到从底层的上报的设备状态变化消息。通过WiredAccessoryManager去处理。
2) WiredAccessoryManager判断状态并将状态变化交给AudioService处理。
3) AudioService得到通知,根据需要判断是否发送BECOMING_NOISY相关Intent,并新列表。
4) AudioService将处理后的状态变化通过AudioSystem交给底层去处理。
。。。。。。AudioSystem.setDeviceConnectionState