In the android framework, it is very easy to listen to the underlying uevent event. Just take the following steps and take usbdevicemanager. Java as an example.
First, create a ueventobserver class:
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(); } } };
In this class, you need to override the onuevent method to process the events you get in this method.
Next, call the startobserving method:
mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH);
Here:
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";
In this way, you can listen to the uevent event in the above path. Is it easy!
The implementation of the ueventobserver class and the implementation of the JNI layer and the Hal layer have a small amount of code and seem very concise.
First, let's look at the startobserving method:
public final synchronized void startObserving(String match) { ensureThreadStarted(); sThread.addObserver(match, this); }
First, make sure that the thread has been started. If this is the first time you come in, you must start the thread:
private static final synchronized void ensureThreadStarted() { if (sThreadStarted == false) { sThread = new UEventThread(); sThread.start(); sThreadStarted = true; } }
This thread is in singleton mode, and only one process can be started.
After obtaining the thread instance, call addobserver:
public void addObserver(String match, UEventObserver observer) { synchronized(mObservers) { mObservers.add(match); mObservers.add(observer); } }
Save the path and ueventobserver instance to arraylist, because a process may have multiple ueventobserver instances.
At this time, the uevent thread is running:
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 and next_event are the JNI methods. They call the uevent_init and uevent_next_event methods of the Hal layer respectively:
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;}
After listening to the uevent message in the kernel, call Recv and put the data in the buffer. As for the uevent_handler_list queue, It is null because the uevent_add_native_handler function is not called. What is the uevent data format of the kernel, such as the log received when the USB is inserted to the computer:
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
After the framework thread obtains the buffer data, it uses bufferstr. indexof checks whether the data contains the path you want to monitor. If yes, the onuevent method is called back and a new uevent object is added to parse the data so that onuevent can get data better and faster:
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(); } }
The data format in the hash table is:
ACTION changeDEVPATH /devices/virtual/android_usb/android0SUBSYSTEM android_usbUSB_STATE CONNECTEDSEQNUM 1249ACTION changeDEVPATH /devices/virtual/android_usb/android0SUBSYSTEM android_usbUSB_STATE CONFIGURED
In this way, you can easily retrieve the desired data based on the index. For example, in the previous onuevent:
Event. Get ("usb_state") to get connect
Event. Get ("accessory") is empty
End ~