linux/android uevent

http://wenku.baidu.com/view/3f08de275901020207409cd4.html linux uevent

http://blog.csdn.net/fengkehuan/article/details/6200210

http://dongyulong.blog.51cto.com/1451604/389159

 http://blog.163.com/ac952_hmz/blog/static/9479151320120364719795/   android ueventobserver

 http://blog.chinaunix.net/space.php?uid=24605155&do=blog&cuid=2363481 abdroid uevent

http://blog.csdn.net/datangsoc/article/details/5928132: android vold, 代替udev

http://wenku.baidu.com/view/cde97ff9941ea76e58fa0414.html: vold

http://blog.csdn.net/sustzombie/article/details/6118256: vold

 http://blog.csdn.net/magicyu2/article/details/6974074: 这篇讲vold还是比较清楚的。从这篇来看android中的phonewin manager的ueventobserver不是来自vold, 而是直接监听kernel来的uevent的socket(NETLINK_KOBJECT_UEVENT), 来自hardware_legacy的uevent。 vold支持    DhcpListener(system\core\nexus)、FrameworkListener(system\core\libsysutils\src)、NetlinkListener(system\core\libsysutils\src)、SupplicantListener(system\core\nexus)、TiwlanEventListener(system\core\nexus);(这里也有netlink, 这个实际上是vold里调用, 作为uevent的client端处理onevent的, netlinklistener使用侦听NETLINK_KOBJECT_UEVENT的socket读取kernel来的uevent消息, 进过netlinkevent decode, 再调用netlinkhandler提供的onevent根据volumn manager的block, switch等处理event,  之后volumn manager会触发注册进vold的其它service的socket(服务器端)的处理(流程是sendbroadcaster调用socket写, 服务侧的listner的ondataavailable会处理相应的消息, 这些消息由commandlistner的runcomd处理, 不同的runcmd将调用volumemanger的具体的处理函数), 对于其中的framework, vold注册了.  block: 通常指mount/umont,  switch指connect/disconnect

    registerCmd(new DumpCmd());

    registerCmd(new VolumeCmd());

    registerCmd(new AsecCmd());

    registerCmd(new ObbCmd());

    registerCmd(new ShareCmd());

    registerCmd(new StorageCmd());

    registerCmd(new XwarpCmd());

 

几个listner。


 http://www.kuqin.com/networkprog/20080512/8361.html: socket,

Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。
Send()和recv()这两个函数用于面向连接的socket上进行数据传输。

 

http://hi.baidu.com/leoispace/blog/item/8aa5f41cc8609d04304e15bf.html: android socket


 

 

用于内核空间向用户空间通知消息, 比如hotplug。 内核空间的uevent通过socket向用户空间的uevent helper发送消息。这个用户空间的侦听者在pc上用udevd, 在嵌入式设备上用mdev。 侦听者的作用扫描注册的device, 向sysfs的device的uevent的属性文件写入add, 触发uevent事件, 这样用户空间就有机会处理这些事件,根据匹配规则作一定的处理, 比如生成设备节点, 使用modprobe加载驱动等hotplug事件。

 

 

1.kobject, ktype, kset

kobject代表sysfs中的目录。

ktype代表kobject的类型,主要包含release函数和attr的读写函数。比如,所有的bus都有同一个bus_type;所有的class都有同一个class_type。

kset包含了subsystem概念,kset本身也是一个kobject,所以里面包含了一个kobject对象。另外,kset中包含kset_uevent_ops,里面主要定义了三个函数

       int (*filter)(struct kset *kset, struct kobject *kobj);

       const char *(*name)(struct kset *kset, struct kobject *kobj);

       int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);

这三个函数都与uevent相关。filter用于判断uevent是否要发出去。name用于得到subsystem的名字。uevent用于填充env变量。

2.uevent内核部分

uevent是sysfs向用户空间发出的消息。比如,device_add函数中,会调用kobject_uevent(&dev->kobj, KOBJ_ADD); 这里kobj是发消息的kobj,KOBJ_ADD是发出的事件。uevent的事件在kobject_action中定义:

enum kobject_action {

       KOBJ_ADD,

       KOBJ_REMOVE,

       KOBJ_CHANGE,

       KOBJ_MOVE,

       KOBJ_ONLINE,

       KOBJ_OFFLINE,

       KOBJ_MAX

};

 

int kobject_uevent(struct kobject *kobj, enum kobject_action action)

{

       return kobject_uevent_env(kobj, action, NULL);

}

 

kobject_uevent_env:

       由kobject的parent向上查找,直到找到一个kobject包含kset。

       如果kset中有filter函数,调用filter函数,看看是否需要过滤uevent消息。

       如果kset中有name函数,调用name函数得到subsystem的名字;否则,subsystem的名字是kset中kobject的名字。

       分配一个kobj_uevent_env,并开始填充env环境变量:

       增加环境变量ACTION=

       增加环境变量DEVPATH=

       增加环境变量SUBSYSTEM=

       增加环境变量kobject_uevent_env中参数envp_ext指定的环境变量。

       调用kset的uevent函数,这个函数会继续填充环境变量。

       增加环境变量SEQNUM=,这里seq是静态变量,每次累加。

       调用netlink发送uevent消息。

       调用uevent_helper,最终转换成对用户空间sbin/mdev的调用。

3.uevent用户空间部分

uevent的用户空间程序有两个,一个是udev,一个是mdev。

udev通过netlink监听uevent消息,它能完成两个功能:

       1.自动加载模块

       2.根据uevent消息在dev目录下添加、删除设备节点。

另一个是mdev,mdev在busybox的代码包中能找到,它通过上节提到的uevent_helper函数被调用。

 

下面简要介绍udev的模块自动加载过程:

etc目录下有一个uevent规则文件/etc/udev/rules.d/50-udev.rules

udev程序收到uevent消息后,在这个规则文件里匹配,如果匹配成功,则执行这个匹配定义的shell命令。例如,规则文件里有这么一行:

ACTION=="add", SUBSYSTEM=="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"

所以,当收到uevent的add事件后,shell能自动加载在MODALIAS中定义的模块。

 

mdev的模块自动加载过程与之类似,它的配置文件在/etc/mdev.conf中。例如:

$MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"

这条规则指的是:当收到的环境变量中含有MODALIAS,那么加载MODALIAS代表的模块。

mdev的详细说明在busybox的docs/mdev.txt中。

4.uevent在设备驱动模型中的应用

在sys目录下有一个子目录devices,代表一个kset。

创建设备时,调用的device_initialize函数中,默认会把kset设置成devices_kset,即devices子目录代表的kset。

devices_kset中设置了uevent操作集device_uevent_ops。

static struct kset_uevent_ops device_uevent_ops = {

       .filter =    dev_uevent_filter,

       .name =   dev_uevent_name,

       .uevent = dev_uevent,

};

 

dev_uevent_filter中,主要是规定了要想发送uevent,dev必须有class或者bus。

dev_uevent_name中,返回dev的class或者bus的名字。

dev_uevent函数:

       如果dev有设备号,添加环境变量MAJOR与MINOR。

       如果dev->type有值,设置DEVTYPE=<dev->type->name>。

       如果dev->driver,设置DRIVER=<dev->driver->name>。

       如果有bus,调用bus的uevent函数。

       如果有class,调用class的uevent函数。

如果有dev->type,调用dev->type->uevent函数。

 

一般在bus的uevent函数中,都会添加MODALIAS环境变量,设置成dev的名字。这样,uevent传到用户空间后,就可以通过对MODALIAS的匹配自动加载模块。这样的bus例子有platform和I2C等等。

 

 

 

android的vold:

现在可能很少有人会用mknod这个命令了,也很少有使用它的机会,但就在几年前,这还是一项linux工程师的必备技能,在制作文件系统前或加载新的驱动前,我们必须小心翼翼的创建设备节点。

不需要使用mknod并不是他消失了,而是我们有了更好更智能的方法。

linux对于热插拔的支持并不是生来就有的,而是经历了一个复杂而有戏剧性的过程,全球linux爱好者用脚投出了他们保贵的一票,udev最终成为事实上的标准。

在android中,取代udev的是vold,我们这里不去过多的讨论为什么android不继续使用udev,但要知道vold的机制和udev是一样的,理解了udev,也就理解了vold。android一出生就没有尊守传统linux的许多标准,当然也不能指望udev能很好的服务于android。android社区的选择是别起炉灶,为android定做一套udev,这就是vold了。

无论是udev还是vold,都是基于sysfs的,sysfs为内核与用户层的通讯提供了一种全新的方式,并将这种方式加以规范。

kernel层能检测到有新的设备接入,并能为之加载相应的驱动,但如何通知用户层呢?这就是sysfs的工作,内核中的sysfs机制要求当有新的驱动加载时给用户层发送相应的event.但这些event只尽告知的义务,具体怎么处理,这就是vold(或者udev)的事了。

对于用户层而言,我们无需关心sysfs的细节,只要知道sysfs能向用户层提供什么就行了。

首先,我们要知道如何接收来自内核的event.

Netlink socket大家应该不会陌生吧,socket这套东西不仅能用于网络间的通讯,也用能用于进程间的通讯,像这种内核态与用户沟通的活,自然也少不了它。

下面的内容摘自vold(NetlinkManager.cpp)

if ((mSock = socket(PF_NETLINK,

SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {

SLOGE("Unable to create uevent socket: %s", strerror(errno));

return -1;

}

if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

SLOGE("Unable to set uevent socket options: %s", strerror(errno));

return -1;

}

if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {

SLOGE("Unable to bind uevent socket: %s", strerror(errno));

return -1;

}

 

这就是监听sysfs的uevent的socket的关键设置,有网络编程背影的人很容易理解上面这段代码。

下面紧接着的问题就是这个socket通路会给我们什么消息:

我们进入/sys/block/mmcblk0(也可以是/sys/block下的其它目录),执行:

cat *

MAJOR=179

MINOR=0

DEVNAME=mmcblk0

DEVTYPE=disk

PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234

PHYSDEVBUS=mmc

PHYSDEVDRIVER=mmcblk

NPARTS=1

......

 

我们通过socket从内核处到的envent中所包含的信息也与此相似,是一个包含这些信息的文本,可能是如下格式的

add@/block/PHYSDEVDRIVER=mmcblk

PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234

DEVNAME=mmcblk0

MAJOR=179

MINOR=0

PHYSDEVDRIVER=mmcblk

......

 

sysfs传上来的是一个多行的文本(这点要特别注意,并不只是add@/block/PHYSDEVDRIVER=mmcblk

这一行),vold要对这个多行文档进行解析,然后决定怎么做。vold会把解协出来的消息再通可别一个vold的socket传到其它的进程,同时接收其它进程的反馈。

向sysfs目录(或子目录)下面的uevent文件写入”add/n”字符也会触发内核上发这些uevent,相当于重新执行了一次热插拔。

例:echo "add" > /sys/block/mmcblk0/uevent

系统启动时vold错过了的消息可以用这个特性重新触发。

clip_image002

分析vold的源码,要有一定的C++的基础和设计模式的知识,习惯过程式设计的程序员在读vold时会有很大的困难,不过幸好vold代码不多。另外,与vold相关的大量机密都在libsysutils中,千万不要漏掉这个库。

先看看下图SocketListener这套架构,监听sysfs与其它进程的消息,全仰仗这套框架。

clip_image004

在netLinkListener中,VOLD的重点是OnEvent这个虚接口的实现,而CommandSistener中,VOLD处理的重点则是分发VoldCommand类。VoldCommand是由FrameworkCommand派生出的,而VolumeCmdShareCmd等子类则是各种操作的封装。

先看看对NetlinkHandler::onEvent的处理

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

VolumeManager *vm = VolumeManager::Instance();

const char *subsys = evt->getSubsystem();

if (!subsys) {

SLOGW("No subsystem found in netlink event");

return;

}

if (!strcmp(subsys, "block")) {

vm->handleBlockEvent(evt);

} else if (!strcmp(subsys, "switch")) {

vm->handleSwitchEvent(evt);

} else if (!strcmp(subsys, "battery")) {

} else if (!strcmp(subsys, "power_supply")) {

}

}

 

这里主要是通过基类解析的uevent消息分别调用不同的处理。如果看了上面的图还有人问NetlinkHandler::onEvent是在什么时候调用的,那就要补一下C++了,这不是一两句话能说得清楚的。

 

VoldCommand主要是实现对runcommand动作的封装,在FrameworkListener会根据收到的消息选择相应的派生类。

bool FrameworkListener::onDataAvailable(SocketClient *c) {

char buffer[255];

int len;

if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {

SLOGE("read() failed (%s)", strerror(errno));

return errno;

} else if (!len)

return false;

int offset = 0;

int i;

for (i = 0; i < len; i++) {

if (buffer[i] == '/0') {

dispatchCommand(c, buffer + offset);

offset = i + 1;

}

}

return true;

}

 

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {

FrameworkCommandCollection::iterator i;

int argc = 0;

char *argv[FrameworkListener::CMD_ARGS_MAX];

char tmp[255];

char *p = data;

char *q = tmp;

bool esc = false;

bool quote = false;

int k;

memset(argv, 0, sizeof(argv));

memset(tmp, 0, sizeof(tmp));

while(*p) {

if (*p == '//') {

if (esc) {

*q++ = '//';

esc = false;

} else

esc = true;

p++;

continue;

} else if (esc) {

if (*p == '"')

*q++ = '"';

else if (*p == '//')

*q++ = '//';

else {

cli->sendMsg(500, "Unsupported escape sequence", false);

goto out;

}

p++;

esc = false;

continue;

}

if (*p == '"') {

if (quote)

quote = false;

else

quote = true;

p++;

continue;

}

*q = *p++;

if (!quote && *q == ' ') {

*q = '/0';

argv[argc++] = strdup(tmp);

memset(tmp, 0, sizeof(tmp));

q = tmp;

continue;

}

q++;

}

argv[argc++] = strdup(tmp);

#if 0

for (k = 0; k < argc; k++) {

SLOGD("arg[%d] = '%s'", k, argv[k]);

}

#endif

if (quote) {

cli->sendMsg(500, "Unclosed quotes error", false);

goto out;

}

for (i = mCommands->begin(); i != mCommands->end(); ++i) {

FrameworkCommand *c = *i;

if (!strcmp(argv[0], c->getCommand())) {

if (c->runCommand(cli, argc, argv)) {//调用派生类的接口。

SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));

}

goto out;

}

}

cli->sendMsg(500, "Command not recognized", false);

out:

int j;

for (j = 0; j < argc; j++)

free(argv[j]);

return;

}

 
 
 

clip_image006

Volume定义了各种磁盘的操作,属于工具类。

clip_image008

要将这些类是如何组织在一起的呢,关键是下面两个工厂类。

 
 

clip_image010

 
 

clip_image012

从上图可以看出,VolumeManager和NetlinkManager将整 个系统组织在一 起,NetlinkManager负责翻译sysfs的uevent事件并传递给其它的进程,VolumeManager则负责接收其它进程反馈的消息,并分发给VoldCommand类作相应的处理。

最后,我们分析一下vold是如何初始化这些类的:

int main() {

VolumeManager *vm;

CommandListener *cl;

NetlinkManager *nm;

SLOGI("Vold 2.1 (the revenge) firing up");

mkdir("/dev/block/vold", 0755);

/* Create our singleton managers */

if (!(vm = VolumeManager::Instance())) {//实例化

SLOGE("Unable to create VolumeManager");

exit(1);

};

if (!(nm = NetlinkManager::Instance())) {//实例化

SLOGE("Unable to create NetlinkManager");

exit(1);

};

cl = new CommandListener(); //创建vold socket,用于向其它进程转发解析的sysfs event,并接收其进程的命令。

vm->setBroadcaster((SocketListener *) cl);

nm->setBroadcaster((SocketListener *) cl);

if (vm->start()) {

SLOGE("Unable to start VolumeManager (%s)", strerror(errno));

exit(1);

}

if (process_config(vm)) { //解析配置

SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));

}

if (nm->start()) {//创建监听sysfs的socket

SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));

exit(1);

}

coldboot("/sys/block"); //冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add/n” 字符也可以触发sysfs事件,相当执行了一次热插拔。

/*

* Switch uevents are broken.

* For now we manually bootstrap

* the ums switch

*/

{

FILE *fp;

char state[255];

/*

* Now that we're up, we can respond to commands

*/

if (cl->startListener()) { //监听上层的反馈

SLOGE("Unable to start CommandListener (%s)", strerror(errno));

exit(1);

}

// Eventually we'll become the monitoring thread

while(1) {

sleep(1000);

}

SLOGI("Vold exiting");

exit(0);

}

 

etc/ vold.fstab的配置文件

例:(每一行的结束不能有空格等任何字符,vold对这个地方的处理有bug.)

dev_mount sdcard /data/disk auto /block/sda

 

格式是:

type label mount_point part sysfs_path sysfs_path

sysfs_path可以有多个,但最后不要有空格,否则会解析错误

part指定分区个数,如果是auto则只有一个分区

 

 

 

另一篇:

 

1. 总体架构

2. 流程概览

2.1 开启Vold

2.2 引导Uevent

2.3 处理事件


 

Vold - Volume Daemon存储类的守护进程,作为Android的一个本地服务,负责处理诸如SD、USB等存储类设备的插拔等事件。

1. 总体架构

Vold服务由volumeManager统一管控,它将具体任务分别分派给netlinkManager, commandListener, directVolume, Volume去完成。

Vold服务向下通过socket机制与底层驱动交互,向上通过JNI, intent, socket, doCommand等机制与Java Framework交互。

 

 2 流程概览

2.1 开启服务

初始化Android系统时开启Vold本地服务,

Vold在/dev/block下创建vold文件夹,开启VolumeManager, NetlinkManager, CommandListener。

2.2 引导Uevent

NetlinkManager负责监听底层Linux上报的uevent事件。

 

系统的SocketListner统一管理所有socket事件。

NetlinkListner负责解析socket事件。

最后由onEvent()将vold事件交还给NetlinkManager处理。

2.3 处理Block和Switch事件

NetlinkManager调用VolumeManager中处理vold事件的类。

handleBlockEvent()完成SD的挂载和卸载,具体交由DirectVolume完成。

handleSwitchEvent()完成由USB实现的U盘的连接。

两者最后都是通过setBroadcast()将ResponsibleCode经过nativeDaemonConnector的socket监听机制,最终上传到MountService作统一规划。

 

MountService里的onEvent()得到解析后的事件,完成两大任务,

-> 发送命令doCommand()通过commandListener传递给volumeManager

-> 将事件信息广播给相关服务,供上层应用使用。

 

 

又一篇

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

一、Kernel侧:

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

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

                     char *envp_ext[])

该函数的主要功能是根据参数组合一个字符串并发送。一个典型的字符串如下:change@/devices/platform/msm-battery/power_supply/usb纮ACTION=change纮DEVPATH=/devices/platform/msm-battery/power_supply/usb纮SUBSYSTEM=power_supply纮POWER_SUPPLY_NAME=usb纮POWER_SUPPLY_ONLINE=0纮SEQNUM=1486纮

首先,准备各个字符串:

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, "%s@%s", action_string, devpath);

此时scratch中就增加了change@/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 mObservers = new ArrayList();

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;

 

你可能感兴趣的:(linux/android uevent)