• android & Linux uevent机制


    Linux uevent机制
    
    
    
    
    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纮
    
    上面这块来自网上,这段内容是否有问题,待考究。
    
    下面看这个函数:
    
    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
    
    char *envp_ext[])
    
    {
    
    struct kobj_uevent_env *env;
    
    const char *action_string = kobject_actions[action];//获取object的动作
    
    //蓝色为为方便看代码加入的
    
    static const char *kobject_actions[] = {
    
    [KOBJ_ADD] = "add",
    
    [KOBJ_REMOVE] = "remove",
    
    [KOBJ_CHANGE] = "change",
    
    [KOBJ_MOVE] = "move",
    
    [KOBJ_ONLINE] = "online",
    
    [KOBJ_OFFLINE] = "offline",
    
    };
    
    //以上为kobject标准的动作,调用时需要传入相应的enum值
    
    const char *devpath = NULL;
    
    const char *subsystem;
    
    struct kobject *top_kobj;
    
    struct kset *kset;
    
    const struct kset_uevent_ops *uevent_ops;
    
    u64 seq;
    
    int i = 0;
    
    int retval = 0;
    
    #ifdef CONFIG_NET
    
    struct uevent_sock *ue_sk;
    
    #endif
    
    
    
    
    
    pr_debug("kobject: '%s' (%p): %s
    ",
    
    kobject_name(kobj), kobj, __func__);
    
    =========================================================
    
    这段代码用来查找该kobject所属于的kset,得到 uevent_ops
    
    top_kobj = kobj;
    
    while (!top_kobj->kset && top_kobj->parent)
    
    top_kobj = top_kobj->parent;
    
    
    
    
    
    if (!top_kobj->kset) {
    
    pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
    
    "without kset!
    ", kobject_name(kobj), kobj,
    
    __func__);
    
    return -EINVAL;
    
    }
    
    
    
    
    
    kset = top_kobj->kset;
    
    uevent_ops = kset->uevent_ops;
    
    =========================================================
    
    if (kobj->uevent_suppress) {
    
    pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
    
    "caused the event to drop!
    ",
    
    kobject_name(kobj), kobj, __func__);
    
    return 0;
    
    }
    
    if (uevent_ops && uevent_ops->filter)
    
    if (!uevent_ops->filter(kset, kobj)) {
    
    pr_debug("kobject: '%s' (%p): %s: filter function "
    
    "caused the event to drop!
    ",
    
    kobject_name(kobj), kobj, __func__);
    
    return 0;
    
    }
    
    
    
    
    
    ====================================================
    
    获取subsystem信息
    
    if (uevent_ops && uevent_ops->name)
    
    subsystem = uevent_ops->name(kset, kobj);
    
    else
    
    subsystem = kobject_name(&kset->kobj);
    
    if (!subsystem) {
    
    pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
    
    "event to drop!
    ", kobject_name(kobj), kobj,
    
    __func__);
    
    return 0;
    
    }
    
    =========================================================
    
    env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
    
    if (!env)
    
    return -ENOMEM;
    
    
    
    
    
    devpath = kobject_get_path(kobj, GFP_KERNEL);//获取kobject的设备路径
    
    if (!devpath) {
    
    retval = -ENOENT;
    
    goto exit;
    
    }
    
    //下面准备要传递的信息数据
    
    retval = add_uevent_var(env, "ACTION=%s", action_string);
    
    if (retval)
    
    goto exit;
    
    retval = add_uevent_var(env, "DEVPATH=%s", devpath);
    
    if (retval)
    
    goto exit;
    
    retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
    
    if (retval)
    
    goto exit;
    
    //envp_ext[i]是传进来的参数,为该event时携带的一些自定义的信息
    
    if (envp_ext) {
    
    for (i = 0; envp_ext[i]; i++) {
    
    retval = add_uevent_var(env, "%s", envp_ext[i]);
    
    if (retval)
    
    goto exit;
    
    }
    
    }
    
    
    
    
    
    if (uevent_ops && uevent_ops->uevent) {
    
    retval = uevent_ops->uevent(kset, kobj, env);
    
    if (retval) {
    
    pr_debug("kobject: '%s' (%p): %s: uevent() returned "
    
    "%d
    ", kobject_name(kobj), kobj,
    
    __func__, retval);
    
    goto exit;
    
    }
    
    }
    
    
    
    
    
    if (action == KOBJ_ADD)
    
    kobj->state_add_uevent_sent = 1;
    
    else if (action == KOBJ_REMOVE)
    
    kobj->state_remove_uevent_sent = 1;
    
    
    
    
    
    //加入该event的序号
    
    spin_lock(&sequence_lock);
    
    seq = ++uevent_seqnum;
    
    spin_unlock(&sequence_lock);
    
    retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
    
    if (retval)
    
    goto exit;
    
    
    
    
    
    #if defined(CONFIG_NET)
    
    //下面通过网络socket将数据发送出去
    
    mutex_lock(&uevent_sock_mutex);
    
    list_for_each_entry(ue_sk, &uevent_sock_list, list) {
    
    struct sock *uevent_sock = ue_sk->sk;
    
    struct sk_buff *skb;
    
    size_t len;
    
    
    
    
    
    len = strlen(action_string) + strlen(devpath) + 2;
    
    skb = alloc_skb(len + env->buflen, GFP_KERNEL);//申请网络skb数据
    
    if (skb) {
    
    char *scratch;
    
    
    
    
    
    scratch = skb_put(skb, len);
    
    sprintf(scratch, "%s@%s", action_string, devpath);
    
    此时scratch中就增加了change@/devices/platform/msm-battery/power_supply/usb的
    
    //组长网络skb数据结构
    
    for (i = 0; i < env->envp_idx; i++) {
    
    len = strlen(env->envp[i]) + 1;
    
    scratch = skb_put(skb, len);
    
    strcpy(scratch, env->envp[i]);
    
    }
    
    
    
    
    
    NETLINK_CB(skb).dst_group = 1;//下面开始发送数据
    
    retval = netlink_broadcast_filtered(uevent_sock, skb,
    
    0, 1, GFP_KERNEL,
    
    kobj_bcast_filter,
    
    kobj);
    
    if (retval == -ENOBUFS || retval == -ESRCH)
    
    retval = 0;
    
    } else
    
    retval = -ENOMEM;
    
    }
    
    mutex_unlock(&uevent_sock_mutex);
    
    #endif
    
    
    
    
    
    if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
    
    char *argv [3];
    
    
    
    
    
    argv [0] = uevent_helper;
    
    argv [1] = (char *)subsystem;
    
    argv [2] = NULL;
    
    retval = add_uevent_var(env, "HOME=/");
    
    if (retval)
    
    goto exit;
    
    retval = add_uevent_var(env,
    
    "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
    
    if (retval)
    
    goto exit;
    
    
    
    
    
    retval = call_usermodehelper(argv[0], argv,
    
    env->envp, UMH_WAIT_EXEC);
    
    }
    
    
    
    
    
    exit:
    
    kfree(devpath);
    
    kfree(env);
    
    return retval;
    
    }
    
    二 android 侧
    
    
    
    
     
    
    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");
    
    
    
    
    //Added for USB Develpment debug, more log for more debuging help
    
    if(DEBUG) Log.w(TAG, "mUEventObserver: onUEvent: state = " + state);
    
    //Added for USB Develpment debug, more log for more debuging help
    
    
    
    
    if (state != null) {
    
    mHandler.updateState(state);
    
    } else if ("START".equals(accessory)) {
    
    if (DEBUG) Slog.d(TAG, "got accessory start");
    
    setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false);
    
    }
    
    }
    
    };
    
    
    
    
    在类初始化时会调用下面的动作,启动监听动作。
    
    mUEventObserver.startObserving(USB_STATE_MATCH);
    
    
    
    
    最终会调用到UEventObserver的addObserver:
    
    private ArrayList<Object> mObservers = new ArrayList<Object>();
    
    public void addObserver(String match, UEventObserver observer) {
    
    synchronized(mObservers) {
    
    mObservers.add(match);
    
    mObservers.add(observer);
    
    }
    
    }
    
    
    
    
    
    private static final String USB_STATE_MATCH =
    
    "DEVPATH=/devices/virtual/android_usb/android0";
    
    该函数最终会将”DEVPATH=/devices/virtual/android_usb/android0”增加到匹配序列中,当kernel发送具有该字符串的数据时,就返回匹配成功,然后调用mUEventObserver 的onUEvent函数;
    
    
    
    
    
    UeventObserver.java
    
    private static class UEventThread extends Thread {
    
     
    
    private ArrayList<Object> mObservers = new ArrayList<Object>();
    
     
    
    UEventThread() {
    
    super("UEventObserver");
    
    }
    
     
    
    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));
    }
    }
    }
    }
    }
    }
  • 相关阅读:
    Java IO输入输出流 FileWriter 字符流
    Java IO输入输出流File 字节流
    Java List集合和Map集合的综合应用
    表单提交中的重复问题(表单令牌验证)
    php中const与define的区别
    阿里云中获取文件及目录列表的方法
    巧用php中的array_filter()函数去掉多维空值
    文件大小格式化函数
    UTC 通用格式时间 转换为 时间戳,并格式化为2017-01-01 12:00:00
    关于匿名函数的使用,购物车中计算销售税的应用
  • 原文地址:https://www.cnblogs.com/timssd/p/4328864.html
Copyright © 2020-2023  润新知