use crate::CoreError;
use crate::reactor::Fd;
#[cfg(target_os = "android")]
mod imp {
use super::*;
use std::os::unix::io::AsRawFd;
fn errno() -> i32 {
std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
}
pub fn uevent_open() -> Result<Fd, CoreError> {
unsafe {
let fd = libc::socket(
libc::AF_NETLINK,
libc::SOCK_RAW | libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK,
libc::NETLINK_KOBJECT_UEVENT,
);
if fd < 0 {
return Err(CoreError::sys(errno(), "netlink socket"));
}
let mut addr: libc::sockaddr_nl = std::mem::zeroed();
addr.nl_family = libc::AF_NETLINK as u16;
addr.nl_pid = 0;
addr.nl_groups = 1; let r = libc::bind(
fd,
&addr as *const _ as *const libc::sockaddr,
std::mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t,
);
if r < 0 {
libc::close(fd);
return Err(CoreError::sys(errno(), "netlink bind"));
}
Fd::from_owned_raw_fd(fd, "netlink_uevent")
}
}
pub fn uevent_recv_raw(fd: &Fd, buf: &mut [u8]) -> Option<usize> {
let n = unsafe {
libc::recv(
fd.as_raw_fd(),
buf.as_mut_ptr() as *mut libc::c_void,
buf.len(),
libc::MSG_DONTWAIT,
)
};
if n > 0 { Some(n as usize) } else { None }
}
}
#[cfg(not(target_os = "android"))]
mod imp {
use super::*;
pub fn uevent_open() -> Result<Fd, CoreError> {
Err(CoreError::sys(libc::ENOSYS, "netlink uevent: android only"))
}
pub fn uevent_recv_raw(_fd: &Fd, _buf: &mut [u8]) -> Option<usize> {
None
}
}
pub fn uevent_open() -> Result<Fd, CoreError> {
imp::uevent_open()
}
pub fn uevent_drain_battery(fd: &Fd) -> Option<(u8, String)> {
let mut result: Option<(u8, String)> = None;
let mut buf = [0u8; 4096];
loop {
match imp::uevent_recv_raw(fd, &mut buf) {
None => break,
Some(n) => {
if let Some(batt) = parse_battery(&buf[..n]) {
result = Some(batt);
}
}
}
}
result
}
fn parse_battery(msg: &[u8]) -> Option<(u8, String)> {
let parts: Vec<&str> = msg
.split(|&b| b == 0)
.filter_map(|s| std::str::from_utf8(s).ok())
.filter(|s| !s.is_empty())
.collect();
if !parts.iter().any(|s| *s == "SUBSYSTEM=power_supply") {
return None;
}
let cap = parts.iter()
.find_map(|s| s.strip_prefix("POWER_SUPPLY_CAPACITY="))
.and_then(|v| v.parse::<u8>().ok())?;
let status = parts.iter()
.find_map(|s| s.strip_prefix("POWER_SUPPLY_STATUS="))
.unwrap_or("Unknown")
.to_string();
Some((cap, status))
}