Android uevent 电池电量上报机制

Android input epoll/inotify机制
power supply是如何上报电池信息的
Android :kernel uevent发送(热插拔)事件到用户空间

源码分析

Kernel space

kernel/msm-4.19/drivers/power/supply/power_supply_core.c
kernel/msm-4.19/drivers/power/supply/power_supply_sysfs.c

power_supply_class_init
    power_supply_class->dev_uevent = power_supply_uevent;

初始化workqueue,后续用于调度
INIT_WORK(&psy->changed_work, power_supply_changed_work);

在驱动中检测到硬件状态发生变化会调用power_supply_changed,进而调取workqueue
    schedule_work(&psy->changed_work);

添加环境变量,回调keset中注册的power_supply_uevent,将msg以socketbuffer的格式进行打包
power_supply_changed_work
    kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE);
        kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
            if(envp_ext != NULL)
	            add_uevnet_var(env, "%s", envp_ext[i]);//测试用例通过这种envp_ext的方式来实现
	
        if (uevent_ops && uevent_ops->uevent) 	      //uevent_ops = kset->uevent_ops; uevent_ops->uevent = dev_uevent
	        uevent_ops->uevent(kset, kobj, env);      //power_supply_class->dev_uevent = power_supply_uevent; class类kset
	            power_supply_uevent
	            
	    add_uevent_var(env, "ACTION=%s", action_string);
	    add_uevent_var(env, "DEVPATH=%s", devpath);
        add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
	
        kobject_uevent_net_broadcast(kobj, env, action_string, devpath);
            alloc_uevent_skb          // socket buffer的放置有关 header:action@devpath + socket_buffer 
 		    scratch = skb_put(skb, len);   /* add header*/
		    sprintf(scratch, "%s@%s", action_string, devpath) 
			skb_put_data(skb, env->buf, env->buflen);

User space

hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp
power_supply通过调用kobject_uevent, envp_ext为NULL, 会回调class的dev_uevent并且使用的是默认的add_uevent_var
ACTION=action_string DEVPATH=devpath SUBSYSTEM=subsystem,电池上层接受的时候会通过SUBSYSTEM进行过滤

	StartLoop
	    epollfd_.reset(epoll_create1(EPOLL_CLOEXEC)); //进程被替换时会关闭文件描述符
		uevent_fd_.reset(uevent_open_socket(64 * 1024, true)); // uevent_fd_
		ev.events = EPOLLIN; //1. 新的请求 2.接收到普通数据(缓冲未满)  3.正常关闭连接
	    ev.events |= EPOLLWAKEUP;	//1. 唤醒源,系统会保持唤醒
	    epoll_ctl(epollfd_, EPOLL_CTL_ADD, uevent_fd_, &ev);//ADD表示绑定事件
			
		MainLoop -> while(1)
		    epoll_wait(epollfd_, events, eventct, timeout);//epoll等待uevent事件
		        uevent_kernel_multicast_recv  //接受uevent事件
	            strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM) //判断subsystem,battery=power_supply
	            ScheduleBatteryUpdate(); //更新上报电池细节(电量,健康状态等)  以'\0'作为间隔,见log

测试用例

Kernel space

Makefile
obj-y                           += uevent_test.o

uevent_test.c
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/i2c.h>
#include <linux/input.h>
 
struct device *dev = NULL;

char * mesg[2];

static ssize_t power_supply_changed(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
    char s_buf[100]={0};

    mesg[0] =s_buf;
    mesg[1] = NULL;
    
    strcat(s_buf, buf);
    pr_err( "jerry echo buf=%s s_buf=%s count=%d\n",buf,s_buf,count);
    kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, mesg);

    return count;

}
static DEVICE_ATTR(capabitlity, S_IRUGO|S_IWUSR, NULL, power_supply_changed);
 
static const struct attribute *kobject_event_attr[] = {
        &dev_attr_capabitlity.attr,
        NULL,
};
 
static const struct attribute_group kobject_event_attr_group = {
        .attrs = (struct attribute **) kobject_event_attr,
};
 
static struct class kobject_event_class = {
        .name =         "uevent_class",
        .owner =        THIS_MODULE,
};
 
static int __init uevent_test_init(void)
{

    int ret = 0;
    ret = class_register(&kobject_event_class);
    if (ret < 0) {
      pr_err("jerry: class_register fail");
      return ret;
    }
 
    dev = device_create(&kobject_event_class, NULL, MKDEV(0, 0), NULL, "uevent_test");
    if (dev) {
       ret = sysfs_create_group(&dev->kobj, &kobject_event_attr_group);
       if(ret < 0) {
         pr_err("uevent_test:sysfs_create_group fail");
         return ret;
        }
    } else {
        pr_err("uevent_test:device_create fail");
        ret = -1;
        return ret;
   }

   return 0;
}

static __exit void uevent_test_exit(void)
{
    device_destroy(&kobject_event_class, MKDEV(0,0));
    class_destroy(&kobject_event_class);
    sysfs_remove_group(&dev->kobj, &kobject_event_attr_group);
}
 
module_init(uevent_test_init);
module_exit(uevent_test_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jerry");


User space

#include <fcntl.h>
#include <string.h>
#include <utils/Log.h>
#include <cutils/uevent.h>

#define UEVENT_MSG_LEN 2048


//hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp

int main()
{
    char msg[UEVENT_MSG_LEN + 2];
    int n, uevent_fd_;
    char* cp;

    uevent_fd_ = uevent_open_socket(64*1024, true);

    if (uevent_fd_ < 0) {
        ALOGE( "jerry uevent_init: uevent_open_socket failed\n");
        return 0;
    }

    while(1){
        n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);

        if(n < 0 || n >= UEVENT_MSG_LEN ){
            ALOGE("jerry failed to recevie uevent");
            return 0;
        }

        msg[n] = '\0';
        msg[n+1] = '\0';
        cp = msg;

        ALOGD("jerry get the uevent size n = %d, msg = %s", n, cp);
        while (*cp) {
        
            ALOGD("jerry receive the msg = %s", cp);
            /* advance to after the next \0 */
            while (*cp++)
                ;
        }
    }

    return 0;
}

Logcat log

cd /sys/devices/virtual/uevent_class/uevent_test or 
cd /sys/class/uevent_class/uevent_test
echo "uevent_test" > capabitlity

E            : jerry echo buf=uevent_test
D uevent_test: jerry get the uevent size n = 161, msg = change@/devices/virtual/uevent_class/uevent_test
D uevent_test: jerry receive the msg = change@/devices/virtual/uevent_class/uevent_test
D uevent_test: jerry receive the msg = ACTION=change
D uevent_test: jerry receive the msg = DEVPATH=/devices/virtual/uevent_class/uevent_test
D uevent_test: jerry receive the msg = SUBSYSTEM=uevent_class
D uevent_test: jerry receive the msg = uevent_test
D uevent_test: jerry receive the msg = SEQNUM=8695


power_supply battery
D uevent_test: jerry receive the msg = change@/devices/platform/soc/1c40000.qcom,spmi/spmi-0/spmi0-02/1c40000.qcom,spmi:qcom,pmi632@2:qcom,qpnp-smb5/power_supply/battery
D uevent_test: jerry receive the msg = ACTION=change
D uevent_test: jerry receive the msg = DEVPATH=/devices/platform/soc/1c40000.qcom,spmi/spmi-0/spmi0-02/1c40000.qcom,spmi:qcom,pmi632@2:qcom,qpnp-smb5/power_supply/battery
D uevent_test: jerry receive the msg = SUBSYSTEM=power_supply
D uevent_test: jerry receive the msg = POWER_SUPPLY_NAME=battery
D uevent_test: jerry receive the msg = POWER_SUPPLY_INPUT_SUSPEND=0
D uevent_test: jerry receive the msg = POWER_SUPPLY_STATUS=Full
...
 D uevent_test: jerry receive the msg = POWER_SUPPLY_FCC_STEPPER_ENABLE=0
 D uevent_test: jerry receive the msg = SEQNUM=8694

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统中,可以使用uevent来检测U盘的挂载地址。具体步骤如下: 1. 在Android系统中,U盘的插入和拔出都会发送uevent消息,可以通过注册一个uevent监听器来接收这些消息。 2. 在uevent消息中,会包含有U盘的一些信息,比如设备名称、挂载路径等。 3. 在接收到U盘插入消息时,可以解析uevent消息中的挂载路径信息,即可获得U盘的挂载地址。 以下是一个使用uevent检测U盘挂载地址的示例代码: ``` import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class UdiskMonitor { private static final String TAG = "UdiskMonitor"; private static final String UDISK_MOUNT_PATH = "/sys/class/android_usb/android0/f_mass_storage/lun/file"; private OnUdiskMountedListener mListener; public void startMonitor(final OnUdiskMountedListener listener) { mListener = listener; // 在主线程中监听uevent消息 new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { // 开始监听uevent消息 startUeventMonitor(); return false; } }); } }); } private void startUeventMonitor() { BufferedReader reader = null; try { // 打开uevent监听文件 reader = new BufferedReader(new FileReader(new File("/proc/net/netlink"))); String line; while ((line = reader.readLine()) != null) { // 解析uevent消息 String[] parts = line.split(" "); if (parts.length >= 6 && parts[0].equals("1")) { String action = parts[1]; String devPath = parts[4]; if (action.equals("add") && devPath.equals(UDISK_MOUNT_PATH)) { // U盘被插入,获取挂载路径并回调监听器 String mountPath = getUdiskMountPath(); if (mountPath != null) { if (mListener != null) { mListener.onUdiskMounted(mountPath); } } } } } } catch (IOException e) { Log.e(TAG, "startUeventMonitor: " + e.getMessage()); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { Log.e(TAG, "startUeventMonitor: " + e.getMessage()); } } } } private String getUdiskMountPath() { String mountPath = null; try { // 读取U盘挂载路径 BufferedReader reader = new BufferedReader(new FileReader(new File(UDISK_MOUNT_PATH))); mountPath = reader.readLine().trim(); reader.close(); } catch (IOException e) { Log.e(TAG, "getUdiskMountPath: " + e.getMessage()); } return mountPath; } public interface OnUdiskMountedListener { void onUdiskMounted(String mountPath); } } ``` 在上述代码中,我们通过监听uevent消息来检测U盘的插入和拔出事件。当接收到U盘插入事件时,我们通过读取U盘的挂载路径来获取U盘的挂载地址,并通过回调监听器来通知外部应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值