在android的framework中想要监听底层的uevent事件是一件很简单的事情,只要以下几个步骤即可,拿UsbDeviceManager.java为例子。
首先,创建一个UEventObserver类:
- private final UEventObserver mUEventObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
- String state = event.get("USB_STATE");
- String accessory = event.get("ACCESSORY");
- if (state != null) {
- mHandler.updateState(state);
- } else if ("START".equals(accessory)) {
- if (DEBUG) Slog.d(TAG, "got accessory start");
- startAccessoryMode();
- }
- }
- };
在这个类中要重写onUevent方法,在该方法中处理你得到的事件。
接着,调用startObserving方法即可:
- mUEventObserver.startObserving(USB_STATE_MATCH);
- mUEventObserver.startObserving(ACCESSORY_START_MATCH);
这里:
- private static final String USB_STATE_MATCH =
- "DEVPATH=/devices/virtual/android_usb/android0";
- private static final String ACCESSORY_START_MATCH =
- "DEVPATH=/devices/virtual/misc/usb_accessory";
这样就可以监听上述路径下的uevent事件了。是不是很简单!
而UEventObserver类的实现以及JNI层和HAL层的实现,代码量也很少,看起来很简洁。
先看startObserving方法:
- public final synchronized void startObserving(String match) {
- ensureThreadStarted();
- sThread.addObserver(match, this);
- }
首先确保线程已经启动起来了,如果是第一次进来,肯定要启动线程了:
- private static final synchronized void ensureThreadStarted() {
- if (sThreadStarted == false) {
- sThread = new UEventThread();
- sThread.start();
- sThreadStarted = true;
- }
- }
这个线程是单例模式,一个进程只启动一个就行了。
得到线程实例后,调用addObserver:
- public void addObserver(String match, UEventObserver observer) {
- synchronized(mObservers) {
- mObservers.add(match);
- mObservers.add(observer);
- }
- }
把路径和UEventObserver实例保存到ArrayList中,因为一个进程可能有多个UEventObserver实例的。
这时候uevent线程已经运行起来了:
- public void run() {
- native_setup();
- byte[] buffer = new byte[1024];
- int len;
- while (true) {
- len = next_event(buffer);
- if (len > 0) {
- String bufferStr = new String(buffer, 0, len); // easier to search a String
- synchronized (mObservers) {
- for (int i = 0; i < mObservers.size(); i += 2) {
- if (bufferStr.indexOf((String)mObservers.get(i)) != -1) {
- ((UEventObserver)mObservers.get(i+1))
- .onUEvent(new UEvent(bufferStr));
- }
- }
- }
- }
- }
- }
native_setup和next_event是JNI方法,他们分别调用HAL层的uevent_init和uevent_next_event方法:
- int uevent_init()
- {
- struct sockaddr_nl addr;
- int sz = 64*1024;
- int s;
- memset(&addr, 0, sizeof(addr));
- addr.nl_family = AF_NETLINK;
- addr.nl_pid = getpid();
- addr.nl_groups = 0xffffffff;
- s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
- if(s < 0)
- return 0;
- setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(s);
- return 0;
- }
- fd = s;
- return (fd > 0);
- }
- int uevent_next_event(char* buffer, int buffer_length)
- {
- while (1) {
- struct pollfd fds;
- int nr;
- fds.fd = fd;
- fds.events = POLLIN;
- fds.revents = 0;
- nr = poll(&fds, 1, -1);
- if(nr > 0 && fds.revents == POLLIN) {
- int count = recv(fd, buffer, buffer_length, 0);
- if (count > 0) {
- struct uevent_handler *h;
- pthread_mutex_lock(&uevent_handler_list_lock);
- LIST_FOREACH(h, &uevent_handler_list, list)
- h->handler(h->handler_data, buffer, buffer_length);
- pthread_mutex_unlock(&uevent_handler_list_lock);
- return count;
- }
- }
- }
- // won't get here
- return 0;
- }
监听到内核有uevent消息后,调用recv,把数据放到buffer中,至于uevent_handler_list队列,是为空的,因为没有调用到uevent_add_native_handler函数。内核的uevent数据格式是怎样的呢,比如插入usb连接电脑时候收到的log:
- ACTION=change
- DEVPATH=/devices/virtual/android_usb/android0
- SUBSYSTEM=android_usb
- USB_STATE=CONNECTED
- SEQNUM=1249
- change@/devices/virtual/android_usb/android0
- ACTION=change
- DEVPATH=/devices/virtual/android_usb/android0
- SUBSYSTEM=android_usb
- USB_STATE=CONNECTED
- SEQNUM=1249
- change@/devices/virtual/android_usb/android0
- ACTION=change
- DEVPATH=/devices/virtual/android_usb/android0
- SUBSYSTEM=android_usb
- USB_STATE=CONFIGURED
- SEQNUM=1250
- change@/devices/virtual/android_usb/android0
- ACTION=change
- DEVPATH=/devices/virtual/android_usb/android0
- SUBSYSTEM=android_usb
- USB_STATE=CONFIGURED
- SEQNUM=1250
framework的线程得到buffer数据后,用bufferStr.indexOf判断数据是否包含自己想要监控的路径,如果有,则回调onUEvent方法,并new一个自己的UEvent对象,把数据解析好,以便onUEvent更好更快的得到数据:
- static public class UEvent {
- // collection of key=value pairs parsed from the uevent message
- public HashMap<String,String> mMap = new HashMap<String,String>();
- public UEvent(String message) {
- int offset = 0;
- int length = message.length();
- while (offset < length) {
- int equals = message.indexOf('=', offset);
- int at = message.indexOf(0, offset);
- if (at < 0) break;
- if (equals > offset && equals < at) {
- // key is before the equals sign, and value is after
- mMap.put(message.substring(offset, equals),
- message.substring(equals + 1, at));
- }
- offset = at + 1;
- }
- }
- public String get(String key) {
- return mMap.get(key);
- }
- public String get(String key, String defaultValue) {
- String result = mMap.get(key);
- return (result == null ? defaultValue : result);
- }
- public String toString() {
- return mMap.toString();
- }
- }
放到哈希表中的数据格式为:
- ACTION change
- DEVPATH /devices/virtual/android_usb/android0
- SUBSYSTEM android_usb
- USB_STATE CONNECTED
- SEQNUM 1249
- ACTION change
- DEVPATH /devices/virtual/android_usb/android0
- SUBSYSTEM android_usb
- USB_STATE CONFIGURED
这样根据索引很容易取出想要的数据。比如前面的onUEvent中:
event.get("USB_STATE") 得到CONNECT
event.get("ACCESSORY") 为空
结束~