android系统在播放音乐的时候拔掉耳机通常音乐会自动暂停播放。遇到这样的需求,客户使用的是一个usb audio设备,希望拔掉后音乐不会自动暂停,而是继续播放,声音从speaker出来。以5.1的code为例,usb audio设备(usb 耳机等)拔出时\frameworks\base\services\usb\java\com\android\server\usbAudioService.java中会发出一个断开的广播:
private void sendDeviceNotification(AudioDevice audioDevice, boolean enabled) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(AudioManager.ACTION_USB_AUDIO_DEVICE_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra("state", enabled ? 1 : 0);
intent.putExtra("card", audioDevice.mCard);
intent.putExtra("device", audioDevice.mDevice);
intent.putExtra("hasPlayback", audioDevice.mHasPlayback);
intent.putExtra("hasCapture", audioDevice.mHasCapture);
intent.putExtra("hasMIDI", audioDevice.mHasMIDI);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
AudioService接受并处理这个广播,具体代码在frameworks\base\media\java\android\media\AudioService.java中onReceive方法中:
} else if (action.equals(AudioManager.ACTION_USB_AUDIO_DEVICE_PLUG)) {
// FIXME Does not yet handle the case where the setting is changed
// after device connection. Ideally we should handle the settings change
// in SettingsObserver. Here we should log that a USB device is connected
// and disconnected with its address (card , device) and force the
// connection or disconnection when the setting changes.
int isDisabled = Settings.Secure.getInt(mContentResolver,
Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, 0);
if (isDisabled != 0) {
return;
}
state = intent.getIntExtra("state", 0);
int alsaCard = intent.getIntExtra("card", -1);
int alsaDevice = intent.getIntExtra("device", -1);
boolean hasPlayback = intent.getBooleanExtra("hasPlayback", false);
boolean hasCapture = intent.getBooleanExtra("hasCapture", false);
boolean hasMIDI = intent.getBooleanExtra("hasMIDI", false);
String ID = intent.getStringExtra("ID");
String usbHeadSetID = "1a1d00e0";
String params = (alsaCard == -1 && alsaDevice == -1 ? ""
: "card=" + alsaCard + ";device=" + alsaDevice);
// Playback Device
if (hasPlayback) {
outDevice = AudioSystem.DEVICE_OUT_USB_DEVICE;
setWiredDeviceConnectionState(outDevice, state, params);
}
// Capture Device
if (hasCapture) {
inDevice = AudioSystem.DEVICE_IN_USB_DEVICE;
setWiredDeviceConnectionState(inDevice, state, params);
}
再来看setWiredDeviceConnectionState方法:
public void setWiredDeviceConnectionState(int device, int state, String name) {
synchronized (mConnectedDevices) {
int delay = checkSendBecomingNoisyIntent(device, state);
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
device,
state,
name,
delay);
}
}
其中checkSendBecomingNoisyIntent又发个一个消息通知给到上层:
private int checkSendBecomingNoisyIntent(int device, int state) {
int delay = 0;
if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
int devices = 0;
for (int dev : mConnectedDevices.keySet()) {
if (((dev & AudioSystem.DEVICE_BIT_IN) == 0) &&
((dev & mBecomingNoisyIntentDevices) != 0)) {
devices |= dev;
}
}
if (devices == device) {
sendMsg(mAudioHandler,
MSG_BROADCAST_AUDIO_BECOMING_NOISY,
SENDMSG_REPLACE,
0,
0,
null,
0);
delay = 1000;
}
}
MSG_BROADCAST_AUDIO_BECOMING_NOISY
这个消息对应audiomanager中的这个类型:ACTION_AUDIO_BECOMING_NOISY
应用收到这个通知后会做一个暂停的处理,
在底层可以直接不发这个通知,只针对usb audio设备可以这样修改,判断是否是usb audio设备 ,是就不发好了:
private int checkSendBecomingNoisyIntent(int device, int state) {
int delay = 0;
if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
int devices = 0;
for (int dev : mConnectedDevices.keySet()) {
if (((dev & AudioSystem.DEVICE_BIT_IN) == 0) &&
((dev & mBecomingNoisyIntentDevices) != 0)) {
devices |= dev;
}
}
if (devices == device && device != AudioSystem.DEVICE_OUT_USB_DEVICE && device !=AudioSystem.DEVICE_IN_USB_DEVICE) {
sendMsg(mAudioHandler,
MSG_BROADCAST_AUDIO_BECOMING_NOISY,
SENDMSG_REPLACE,
0,
0,
null,
0);
delay = 1000;
}
}
这样,usb audio设备拔出来音乐就不会暂停而是从扬声器输出继续播放了。