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;











  • 相关阅读:
    【转】【SEE】基于SSE指令集的程序设计简介
    【转】【Asp.Net】asp.net服务器控件创建
    ControlTemplate in WPF ——ScrollBar
    ControlTemplate in WPF —— Menu
    ControlTemplate in WPF —— Expander
    ControlTemplate in WPF —— TreeView
    ControlTemplate in WPF —— ListBox
    ControlTemplate in WPF —— ComboBox
    ControlTemplate in WPF —— TextBox
    ControlTemplate in WPF —— RadioButton
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744579.html
Copyright © 2011-2022 走看看