在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") 为空
结束~