其实整个耳机插入的流程是:当硬件检测到耳机的插入,kernel上报UEvent到上层,上层接收到event后,调用硬件去切audio path, 硬件切换成功后,kernel发送UEvent给framework, 然后做show图标的动作。
想了解UEvent的流程,可以参看:
http://blog.youkuaiyun.com/piaozhiye/article/details/25098435
1、在系统启动过程中,在SystemService中会启动很多服务,包括一个WiredAccessoryObserver。
在WiredAccessoryObserver中,注册了开机广播,开机后,会开始所有相关的UEvent,并且开始监听。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.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; }
开始监听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事件。@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); } }
其中这些状态是从
shell@hammerhead:/ $ ls sys/class/switch/ -l
lrwxrwxrwx root root 2014-05-06 09:44 h2w -> ../../devices/virtual/switch/h2w
lrwxrwxrwx root root 2014-05-06 09:44 hdmi -> ../../devices/virtual/switch/hdmi
lrwxrwxrwx root root 2014-05-06 09:44 hdmi_audio -> ../../devices/virtual/switch/hdmi_audio
lrwxrwxrwx root root 2014-05-06 09:44 usb_audio -> ../../devices/virtual/switch/usb_audio
lrwxrwxrwx root root 2014-05-06 09:44 wfd -> ../../devices/virtual/switch/wfd
这些文件中读取的。
设备是定义在/devices/virtual/switch/下的。最终会调用到AudioManager
mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
AudioManager的setWiredDeviceConnectionState实际是调用AudioService的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); } }
最终会发送到上层一个广播: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); }
原文作者: cnhua5