本文用的是#include <cutils/uevent.h>
主要讲述android中怎么在C++层接收uevent
uevent 是 kernel层向用户层发送的一个事件
首先创建一个线程用于循环去获取uevent
void testUevent {
//创建一个线程一直循环
pthread_t thread;
int ret = pthread_create(&thread, nullptr, getAndPraseUevent, nullptr);
if (ret != 0) {
ALOGD("Failed to create thread1");
return;
}
//与主线程分离
pthread_detach(thread);
}
然后在这个线程中就可循环获取uevent了,本文是通过NetLink的方式获取
static void parseUevent(const char *msg)
{
while (*msg) { // 循环遍历 uevent 消息字符串
if (!strncmp(msg, "key=mykey111", 12)) { // 如果字符串开头为 "key=mykey111"
ALOGD("get msg = %s", msg); // 输出日志信息,表示获取了 msg
}
// 继续遍历下一个字符
while(*msg++)
;
}
}
static void* getAndPraseUevent(void*) {
char msg[UEVENT_MSG_LEN+2]; // 定义一个字符数组用于存储 uevent 消息
int size;
int socket_fd = -1; // 定义 socket 文件描述符,并初始化为 -1
socket_fd = uevent_open_socket(64*1024, true); // 打开 uevent socket,设置缓冲区大小为 64KB
if (socket_fd < 0) {
ALOGD("socket_fd is error!"); // 输出日志,表示 socket_fd 错误
return nullptr; // 返回 nullptr
}
// 设置 socket_fd 为非阻塞模式
fcntl(socket_fd, F_SETFL, O_NONBLOCK);
while (true) {
size = uevent_kernel_multicast_recv(socket_fd, msg, UEVENT_MSG_LEN); // 接收 uevent 消息
if (size <= 0)
continue;
msg[size] = '\0'; // 在消息末尾添加字符串结束符
msg[size + 1] = '\0'; // 在消息末尾再添加一个字符串结束符,用于处理特殊情况
parseUevent(msg); // 解析 uevent 消息
}
return nullptr;
}
请注意本方法会对cpu有很大消耗
uevent_open_socket和uevent_kernel_recv是原生接口,具体见源码
uevent.cpp - OpenGrok cross reference for /system/core/libcutils/uevent.cpp
int uevent_open_socket(int buf_sz, bool passcred) {
struct sockaddr_nl addr;
int on = passcred;
int buf_sz_readback = 0;
socklen_t optlen = sizeof(buf_sz_readback);
int s;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
if (s < 0) return -1;
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
close(s);
return -1;
}
/* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we
* want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in
* case we don't have CAP_NET_ADMIN. This is the case, for example, for
* healthd. */
if (buf_sz_readback < 2 * buf_sz) {
if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {
close(s);
return -1;
}
}
setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(s);
return -1;
}
return s;
}
uevent.cpp - OpenGrok cross reference for /system/core/libcutils/uevent.cpp
ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {
struct iovec iov = {buffer, length};
struct sockaddr_nl addr;
char control[CMSG_SPACE(sizeof(struct ucred))];
struct msghdr hdr = {
&addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,
};
struct ucred* cred;
*uid = -1;
ssize_t n = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0));
if (n <= 0) {
return n;
}
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
/* ignoring netlink message with no sender credentials */
goto out;
}
cred = (struct ucred*)CMSG_DATA(cmsg);
*uid = cred->uid;
if (addr.nl_pid != 0) {
/* ignore non-kernel */
goto out;
}
if (require_group && addr.nl_groups == 0) {
/* ignore unicast messages when requested */
goto out;
}
return n;
out:
/* clear residual potentially malicious data */
bzero(buffer, length);
errno = EIO;
return -1;
}