Uevent是内核通知android有状态变化的一种方法,比如USB线插入、拔出,电池电量变化等等。其本质是内核发送(可以通过socket)一个字符串,应用层(android)接收并解释该字符串,获取相应信息。


一、Kernel 部分:

UEVENT的发起在Kernel端,主要是通过函数

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,

char *envp_ext[])

该函数的主要功能是根据参数组合一个字符串并发送。


首先,准备各个字符串:

1.准备字符串

1)获取action字符串

*action_string =kobject_actions[action];

Action为KOBJ_ADD等,kobject_actions的定义如下:

static const char *kobject_actions[] = {

[KOBJ_ADD] = "add",

[KOBJ_REMOVE] = "remove",

[KOBJ_CHANGE] = "change",

[KOBJ_MOVE] = "move",

[KOBJ_ONLINE] = "online",

[KOBJ_OFFLINE] = "offline",

};


2)获取subsystem字符串

subsystem = kobject_name(&kset->kobj);

static inline const char *kobject_name(const struct kobject *kobj)

{

return kobj->name;

}

这里主要获取kobj的名字。

以“power_supply”为例,在power_supply_core.c中注册class power_supply:

power_supply_class =class_create(THIS_MODULE, "power_supply");

将调用以下函数class_createà __class_createà __class_registerà kobject_set_name:

error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);

其中的cls->name就是“power_class”最终在kobject_set_name_vargs中赋值给kobject->name

3)devpath字符串,是改变了的uevent所在的sysfs中的位置

devpath =kobject_get_path(kobj, GFP_KERNEL);



2.填充字符串

然后分配一个env空间存储字符串,

env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);将上面这些字符串填充到其中去,

retval = add_uevent_var(env, "ACTION=%s", action_string);

retval = add_uevent_var(env, "DEVPATH=%s", devpath);

retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);

接着加入不同class的附加的字符串

retval = add_uevent_var(env, "%s", envp_ext[i]);

retval = uevent_ops->uevent(kset, kobj, env);

然后加上该Uenvent的序号,该序号是不断递增的。

add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq)。

3.发送

字符串准备完毕,就要准备发送了,由于Android的CONFIG_NET选项是选上的,因此可以通过socket发送:

首先分配一个skb用于存储网络发送的数据

scratch = skb_put(skb, len);

sprintf(scratch, "%[email protected]%s", action_string, devpath);

此时scratch中就增加了[email protected]/devices/platform/msm-battery/power_supply/usb的字符,然后将之前准备好的各个字符传加在后面

for (i = 0; i < env->envp_idx; i++) {

len = strlen(env->envp[i]) + 1;

scratch = skb_put(skb, len);

strcpy(scratch, env->envp[i]);

}

最后发送调用retval = netlink_broadcast_filtered发送就OK了。

二、Android侧:

1.启动监视

private UEventObserver mPowerSupplyObserver = new UEventObserver()

{

@Override

public void onUEvent(UEventObserver.UEvent event) {

update();

}
}

申明一个observer对象,然后调用startObserving启动对该对象的监视。

mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");

最终会调用到UEventObserver的addObserver:

private ArrayList<Object> mObservers = new ArrayList<Object>();

public void addObserver(String match, UEventObserver observer) {

synchronized(mObservers) {

mObservers.add(match);

mObservers.add(observer);

}

}

该函数最终会将”SUBSYSTEM=power_supply”增加到匹配序列中,当kernel发送具有该字符串的数据时,就返回匹配成功,然后调用mPowerSupplyObserver的onUEvent函数;

public void run() {

………………….

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));

}

}

}

}

}

}

next_event(buffer)从底层接收数据,然后在for循环中比较,如果符合,则调用onUevent。之所以for循环时要加2,是因为一次startObserving是调用了两次mObservers.add,其中第一次的是匹配字符串。

2.JNI函数

其中next_event是一个JNI函数(android_os_UEventObserver.c):

private static native int next_event(byte[] buffer);

static JNINativeMethod gMethods[] = {

{"native_setup", "()V", (void *)android_os_UEventObserver_native_setup},

{"next_event", "([B)I", (void *)android_os_UEventObserver_next_event},

};

android_os_UEventObserver_next_event会调用到uevent_next_event,

3.Socket接口

在hardware/libhardware_legacy/uevent/vim uevent.c中,

int uevent_next_event(char* buffer, int buffer_length)

该函数监听socket,并将socket收到的数据保存到buffer中

nr = poll(&fds, 1, -1);

if(nr > 0 && fds.revents == POLLIN) {

int count = recv(fd, buffer, buffer_length, 0);

if (count > 0) {

………………………………..

}

该socket是在int uevent_init()中创建的

s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

fd=s;

更多相关文章

  1. Android 监听ContentProvider中数据的变化
  2. Android 学习笔记7---数据存储与访问
  3. Android提交数据到服务的四种方法!!!
  4. 【android】Cursor记录集游标、ListView和SimpleCursorAdapter、
  5. 【Android开发学习07】存储简单数据的利器--Preferences
  6. android 数据库建立以及自定义ContentProvider
  7. android 客户端与服务端的通信 发送get和post请求并获取数据
  8. Android SQLite 数据库 增删改查操作

随机推荐

  1. 自定义菜单
  2. Android之AIDL跨进程抛异常的原理
  3. android耳机插入\拔出事件上报流程
  4. Android(安卓)GPS ——AGPS源码分析及配
  5. RecyclerView调用smoothScrollToPosition
  6. Android不同层次开启硬件加速的方式
  7. android lcd横竖屏幕配置
  8. Android(安卓)自定义View 画圆 画线
  9. android 中滑动九宫格开机密码
  10. Android(安卓)事件总线OTTO源码解析