Android Uevent实例分析

system_server进程启动后不止启动了很多重要的服务,还开启了一些重要的观察着(Observer),如:DockObserver、WiredAccessoryObserver。接着以WiredAccessoryObserver为例详细分析。

WiredAccessoryObserver继承于UeventObserver。WiredAccessoryObserver的构造方法里会注册系统开机广播,收到开机广播后调用父类的startObserving方法把相关设备的DEVPATH加入到UeventObserver中,并开启UEventThread线程,run方法被执行,该方法调用native_setup、next_event获取kernel发来的uevent消息,并调用子类的onUEvent完成事件的处理。

native_setup、next_event是native方法@android_os_UEventObserver.cpp,

native_setup调用uevent_init@hardware中uevent.c创建套接字

addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0xffffffff;

s = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT);

bind(s, (struct sockaddr *) &addr, sizeof(addr));

创建socket描述符的时候指定协议族为AF_NETLINK或者PF_NETLINK,套接字type选择SOCK_RAW或者SOCK_DGRAM,Netlink协议并不区分这两种类型,第三个参数协议填充NETLINK_KOBJECT_UEVENT表示接收内核uevent信息。接着就绑定该文件描述符到sockadd_nl,注意该结构体nl_groups是接收掩码,取~0是将接收所有来自内核的消息,其实如果是接收热拔插只需要填NETLINK_KOBJECT_UEVENT。

next_event调用uevent_next_event通过recv接收kernel发送的uevent事件。


上面是用户空间如何接收uevent,下面分析kernel如何发送uevent:

设备驱动程序中一般在probe函数调用switch_set_state@switch_class.c调用kobject_uevent_env@kobject_uevent.c调用netlink_broadcast_filtered完成uevent的发送。

细心的读者可能要问,netlink如何与用户空间的socket对应起来呢?

postcore_initcall(kobject_uevent_init);会使kernel初始化时调用kobject_uevent_init,有兴趣的读者可以研究一下postcore_initcall的细节。kobject_uevent_init中调用register_pernet_subsys(&uevent_net_ops);register_pernet_subsys中会调用uevent_net_ops的init函数,也就是调用uevent_net_init,这里面可以看到netlink_kernel_create(net,NETLINK_KOBJECT_UEVENT,1, NULL, NULL, THIS_MODULE);netlink_kernel_create()函数创建一个socket套接字,该函数原型在netlink.h有定义,其类型是表示往用户空间发送消息的NETLINK_KOBJECT_UEVENT,groups=1,由于uevent只往用户空间发送消息而不接受,因此其输入回调函数input和cb_mutex都设置为NULL。

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、付费专栏及课程。

余额充值