zoukankan      html  css  js  c++  java
  • Android Uevent 分析,从kernel到framework

    http://blog.sina.com.cn/s/blog_6100a4f101015uwh.html

    http://www.cnblogs.com/armlinux/archive/2011/12/05/2396773.html


    --------------------------

    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, "%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<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;











  • 相关阅读:
    super.getClass().getName()方法调用的返回
    外观模式(Façade Pattern)
    Framework 4.0 将何去何从
    SQL Server 2005 第一篇 引言
    抽象工厂模式(Abstract Factory)
    浅谈分页技术
    垃圾邮件
    读书时的软件开发梦
    写技术博客的一个原因应该是寂寞吧
    当下10大最热门的网站开发技术
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744579.html
Copyright © 2011-2022 走看看