要想在状态栏添加耳机插入的图标,在SystemUI里面添加相关逻辑。耳机拔出通知也是一样的流程。
因为需要加一套新的耳机设备accessory并且展示耳机图标在状态栏,所以需要去看一下android上层的处理流程。
其实整个耳机插入的流程是:当硬件检测到耳机的插入,kernel上报UEvent到上层,上层接收到event后,调用硬件去切audio path, 硬件切换成功后,kernel发送UEvent给framework, 然后做show图标的动作。
测试过程中,使用ADB命令发送UEvent模拟耳机的插入,kernel接收uevent。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private static final int SW_HEADPHONE_INSERT = 0x02;
private static final int SW_MICROPHONE_INSERT = 0x04;
private static final int SW_LINEOUT_INSERT = 0x06;
0x02&&0x04代表标准3.5mm耳机
0x06&&0x04代表accessory
使用ADB模拟耳机插入事件,如下:
第一个参数代表动作类型(path switch),第二个参数代表“键”,第四个参数代表“值”(1:插入;0:拔出。)。
Accessory 插入
sendevent /dev/input/event5 5 4 1
sendevent /dev/input/event5 5 6 1
sendevent /dev/input/event5 0 0 0
Accessory 拔出
sendevent /dev/input/event5 5 4 0
sendevent /dev/input/event5 5 6 0
sendevent /dev/input/event5 0 0 0
标准3.5耳机插入
sendevent /dev/input/event5 5 4 1
sendevent /dev/input/event5 5 2 1
sendevent /dev/input/event5 0 0 0
标准3.5耳机拔出
sendevent /dev/input/event5 5 4 0
sendevent /dev/input/event5 5 2 0
sendevent /dev/input/event5 0 0 0
|
1、在系统启动过程中,在SystemService中会启动很多服务,包括一个WiredAccessoryObserver。
1 2 3 4 5 6 7 8 |
SystemService.java
try {
Slog.i(TAG, "Wired Accessory Observer");
// Listen for wired headset changes
new WiredAccessoryObserver(context);
} catch (Throwable e) {
reportWtf("starting WiredAccessoryObserver", e);
}
|
在WiredAccessoryObserver中,注册了开机广播,开机后,会开始所有相关的UEvent,并且开始监听。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
WiredAccessoryObserver.java
//所有要监听的UEvent
private static List<UEventInfo> makeObservedUEventList() {
List<UEventInfo> retVal = new ArrayList<UEventInfo>();
UEventInfo uei;
// Monitor h2w
uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have wired headset support");
}
// Monitor USB
uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have usb audio support");
}
// Monitor HDMI
//
// If the kernel has support for the "hdmi_audio" switch, use that. It will be signalled
// only when the HDMI driver has a video mode configured, and the downstream sink indicates
// support for audio in its EDID.
//
// If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi"
// switch instead.
uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have HDMI audio support");
}
}
return retVal;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//开始监听
private final class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// At any given time accessories could be inserted
// one on the board, one on the dock and one on HDMI:
// observe three UEVENTs
init(); // set initial status
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
startObserving("DEVPATH="+uei.getDevPath());
}
}
}
|
在OnEvent中,实时监听UEvent事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
try {
String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
int state = Integer.parseInt(event.get("SWITCH_STATE"));
updateState(devPath, name, state);
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
|
其中这些状态是从
root@android:/sys/class/switch # ls -l
ls -l
lrwxrwxrwx root root 2013-03-01 14:15 hdmi -> ../../devices/virtual/switch/hdmi
lrwxrwxrwx root root 2013-03-01 14:16 hdmi_audio -> ../../devices/virtual/switch/hdmi_audio
lrwxrwxrwx root root 2013-03-01 14:16 usb_audio -> ../../devices/virtual/switch/usb_audio
这些文件中读取的。
设备是定义在/devices/virtual/switch/下的。
最终会调用到AudioManager
1 |
mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
|
AudioManager的setWiredDeviceConnectionState实际是调用AudioService的setWiredDeviceConnectionState方法。
1 2 3 4 5 6 7 8 9 10 11 |
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);
}
}
|
最终会发送到上层一个广播:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
private void sendDeviceConnectionIntent(int device, int state, String name)
{
Intent intent = new Intent();
intent.putExtra("state", state);
intent.putExtra("name", name);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
int connType = 0;
if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
connType = AudioRoutesInfo.MAIN_HEADSET;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
} else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
connType = AudioRoutesInfo.MAIN_HEADPHONES;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 0);
} else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
} else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
} else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
connType = AudioRoutesInfo.MAIN_HDMI;
intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
} else if (device == AudioSystem.DEVICE_OUT_ANC_HEADSET) {
connType = AudioRoutesInfo.MAIN_HEADSET;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
} else if (device == AudioSystem.DEVICE_OUT_ANC_HEADPHONE) {
connType = AudioRoutesInfo.MAIN_HEADPHONES;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 0);
}
synchronized (mCurAudioRoutes) {
if (connType != 0) {
int newConn = mCurAudioRoutes.mMainType;
if (state != 0) {
newConn |= connType;
} else {
newConn &= ~connType;
}
if (newConn != mCurAudioRoutes.mMainType) {
mCurAudioRoutes.mMainType = newConn;
sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
SENDMSG_NOOP, 0, 0, null, 0);
}
}
}
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
|