mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
Merge "libhealthloop: Only wake up for power supply events" into main am: 2ab04e760b
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/3199931 Change-Id: I2d2f61ac824c7a23b97c1cf9e3d12e4a552b0f00 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -21,6 +21,17 @@ package {
|
|||||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bpf {
|
||||||
|
name: "filterPowerSupplyEvents.o",
|
||||||
|
srcs: ["filterPowerSupplyEvents.c"],
|
||||||
|
// "vendor: true" because all binaries that use this BPF filter are vendor
|
||||||
|
// binaries.
|
||||||
|
vendor: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since "required" sections are ignored in static library definitions,
|
||||||
|
// filterPowerSupplyEvents.o has been added in
|
||||||
|
// build/make/target/product/base_vendor.mk.
|
||||||
cc_library_static {
|
cc_library_static {
|
||||||
name: "libhealthloop",
|
name: "libhealthloop",
|
||||||
vendor_available: true,
|
vendor_available: true,
|
||||||
@@ -34,6 +45,7 @@ cc_library_static {
|
|||||||
"libcutils",
|
"libcutils",
|
||||||
],
|
],
|
||||||
header_libs: [
|
header_libs: [
|
||||||
|
"bpf_headers",
|
||||||
"libbatteryservice_headers",
|
"libbatteryservice_headers",
|
||||||
"libhealthd_headers",
|
"libhealthd_headers",
|
||||||
"libutils_headers",
|
"libutils_headers",
|
||||||
@@ -42,3 +54,30 @@ cc_library_static {
|
|||||||
"include",
|
"include",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
genrule {
|
||||||
|
name: "filterPowerSupplyEvents.h",
|
||||||
|
out: ["filterPowerSupplyEvents.h"],
|
||||||
|
srcs: [":filterPowerSupplyEvents.o"],
|
||||||
|
cmd: "cat $(in) | od -v -tx1 | cut -c9- | grep -v '^$$' | sed 's/^/0x/;s/ /, 0x/g;s/^, //;s/$$/,/' > $(out)",
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_test_host {
|
||||||
|
name: "filterPowerSupplyEventsTest",
|
||||||
|
team: "trendy_team_pixel_system_sw_storage",
|
||||||
|
srcs: [
|
||||||
|
"filterPowerSupplyEventsTest.cpp",
|
||||||
|
],
|
||||||
|
shared_libs: [
|
||||||
|
"libbase",
|
||||||
|
"libbpf",
|
||||||
|
],
|
||||||
|
static_libs: [
|
||||||
|
"libgmock",
|
||||||
|
],
|
||||||
|
generated_headers: [
|
||||||
|
"filterPowerSupplyEvents.h",
|
||||||
|
"libbpf_headers",
|
||||||
|
],
|
||||||
|
compile_multilib: "64",
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,8 +30,12 @@
|
|||||||
#include <cutils/uevent.h>
|
#include <cutils/uevent.h>
|
||||||
#include <healthd/healthd.h>
|
#include <healthd/healthd.h>
|
||||||
|
|
||||||
|
#include <BpfSyscallWrappers.h>
|
||||||
#include <health/utils.h>
|
#include <health/utils.h>
|
||||||
|
|
||||||
|
using android::base::ErrnoError;
|
||||||
|
using android::base::Result;
|
||||||
|
using android::base::unique_fd;
|
||||||
using namespace android;
|
using namespace android;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
@@ -116,7 +120,6 @@ void HealthLoop::PeriodicChores() {
|
|||||||
ScheduleBatteryUpdate();
|
ScheduleBatteryUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/140330870): Use BPF instead.
|
|
||||||
#define UEVENT_MSG_LEN 2048
|
#define UEVENT_MSG_LEN 2048
|
||||||
void HealthLoop::UeventEvent(uint32_t epevents) {
|
void HealthLoop::UeventEvent(uint32_t epevents) {
|
||||||
// No need to lock because uevent_fd_ is guaranteed to be initialized.
|
// No need to lock because uevent_fd_ is guaranteed to be initialized.
|
||||||
@@ -152,8 +155,26 @@ void HealthLoop::UeventEvent(uint32_t epevents) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach a BPF filter to the @uevent_fd file descriptor. This fails in recovery mode because BPF is
|
||||||
|
// not supported in recovery mode. This fails for kernel versions 5.4 and before because the BPF
|
||||||
|
// program is rejected by the BPF verifier of older kernels.
|
||||||
|
Result<void> HealthLoop::AttachFilter(int uevent_fd) {
|
||||||
|
static const char prg[] =
|
||||||
|
"/sys/fs/bpf/vendor/prog_filterPowerSupplyEvents_skfilter_power_supply";
|
||||||
|
int filter_fd(bpf::retrieveProgram(prg));
|
||||||
|
if (filter_fd < 0) {
|
||||||
|
return ErrnoError() << "failed to load BPF program " << prg;
|
||||||
|
}
|
||||||
|
if (setsockopt(uevent_fd, SOL_SOCKET, SO_ATTACH_BPF, &filter_fd, sizeof(filter_fd)) < 0) {
|
||||||
|
close(filter_fd);
|
||||||
|
return ErrnoError() << "failed to attach BPF program";
|
||||||
|
}
|
||||||
|
close(filter_fd);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void HealthLoop::UeventInit(void) {
|
void HealthLoop::UeventInit(void) {
|
||||||
uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
|
uevent_fd_.reset(uevent_create_socket(64 * 1024, true));
|
||||||
|
|
||||||
if (uevent_fd_ < 0) {
|
if (uevent_fd_ < 0) {
|
||||||
KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
|
KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
|
||||||
@@ -161,8 +182,25 @@ void HealthLoop::UeventInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
|
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
Result<void> attach_result = AttachFilter(uevent_fd_);
|
||||||
|
if (!attach_result.ok()) {
|
||||||
|
std::string error_msg = attach_result.error().message();
|
||||||
|
error_msg +=
|
||||||
|
". This is expected in recovery mode and also for kernel versions before 5.10.";
|
||||||
|
KLOG_WARNING(LOG_TAG, "%s", error_msg.c_str());
|
||||||
|
} else {
|
||||||
|
KLOG_INFO(LOG_TAG, "Successfully attached the BPF filter to the uevent socket");
|
||||||
|
}
|
||||||
|
|
||||||
if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
|
if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
|
||||||
KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
|
KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
|
||||||
|
|
||||||
|
if (uevent_bind(uevent_fd_.get()) < 0) {
|
||||||
|
uevent_fd_.reset();
|
||||||
|
KLOG_ERROR(LOG_TAG, "uevent_init: binding socket failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
|
void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
|
||||||
|
|||||||
87
health/utils/libhealthloop/filterPowerSupplyEvents.c
Normal file
87
health/utils/libhealthloop/filterPowerSupplyEvents.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <bpf_helpers.h> // load_word()
|
||||||
|
#include <linux/bpf.h> // struct __sk_buff
|
||||||
|
#include <linux/netlink.h> // struct nlmsghdr
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
// M4: match 4 bytes. Returns 0 if all bytes match.
|
||||||
|
static inline uint32_t M4(struct __sk_buff* skb, unsigned int offset, uint8_t c0, uint8_t c1,
|
||||||
|
uint8_t c2, uint8_t c3) {
|
||||||
|
return load_word(skb, offset) ^ ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// M2: match 2 bytes. Returns 0 if all bytes match.
|
||||||
|
static inline uint16_t M2(struct __sk_buff* skb, unsigned int offset, uint8_t c0, uint8_t c1) {
|
||||||
|
return load_half(skb, offset) ^ ((c0 << 8) | c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// M1: match 1 byte. Returns 0 in case of a match.
|
||||||
|
static inline uint8_t M1(struct __sk_buff* skb, unsigned int offset, uint8_t c0) {
|
||||||
|
return load_byte(skb, offset) ^ c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match "\0SUBSYSTEM=". Returns 0 in case of a match.
|
||||||
|
#define MATCH_SUBSYSTEM_LENGTH 11
|
||||||
|
static inline uint32_t match_subsystem(struct __sk_buff* skb, unsigned int offset) {
|
||||||
|
return M4(skb, offset + 0, '\0', 'S', 'U', 'B') | M4(skb, offset + 4, 'S', 'Y', 'S', 'T') |
|
||||||
|
M2(skb, offset + 8, 'E', 'M') | M1(skb, offset + 10, '=');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match "power_supply\0". Returns 0 in case of a match.
|
||||||
|
#define MATCH_POWER_SUPPLY_LENGTH 13
|
||||||
|
static inline uint32_t match_power_supply(struct __sk_buff* skb, unsigned int offset) {
|
||||||
|
return M4(skb, offset + 0, 'p', 'o', 'w', 'e') | M4(skb, offset + 4, 'r', '_', 's', 'u') |
|
||||||
|
M4(skb, offset + 8, 'p', 'p', 'l', 'y') | M1(skb, offset + 12, '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Linux kernel 5.4 BPF verifier rejects this program, probably because of its size. Hence the
|
||||||
|
// restriction that the kernel version must be at least 5.10.
|
||||||
|
DEFINE_BPF_PROG_KVER("skfilter/power_supply", AID_ROOT, AID_SYSTEM, filterPowerSupplyEvents,
|
||||||
|
KVER(5, 10, 0))
|
||||||
|
(struct __sk_buff* skb) {
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
// The first character matched by match_subsystem() is a '\0'. Starting
|
||||||
|
// right past the netlink message header is fine since the SUBSYSTEM= text
|
||||||
|
// never occurs at the start. See also the kobject_uevent_env() implementation:
|
||||||
|
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/lib/kobject_uevent.c?#n473
|
||||||
|
// The upper bound of this loop has been chosen not to exceed the maximum
|
||||||
|
// number of instructions in a BPF program (BPF loops are unrolled).
|
||||||
|
for (i = sizeof(struct nlmsghdr); i < 256; ++i) {
|
||||||
|
if (i + MATCH_SUBSYSTEM_LENGTH > skb->len) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (match_subsystem(skb, i) == 0) {
|
||||||
|
goto found_subsystem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The SUBSYSTEM= text has not been found in the bytes that have been
|
||||||
|
// examined: let the user space software perform filtering.
|
||||||
|
return skb->len;
|
||||||
|
|
||||||
|
found_subsystem:
|
||||||
|
i += MATCH_SUBSYSTEM_LENGTH;
|
||||||
|
if (i + MATCH_POWER_SUPPLY_LENGTH <= skb->len && match_power_supply(skb, i) == 0) {
|
||||||
|
return skb->len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LICENSE("Apache 2.0");
|
||||||
|
CRITICAL("healthd");
|
||||||
207
health/utils/libhealthloop/filterPowerSupplyEventsTest.cpp
Normal file
207
health/utils/libhealthloop/filterPowerSupplyEventsTest.cpp
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <android-base/unique_fd.h>
|
||||||
|
#include <bpf/libbpf.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <linux/bpf.h> // SO_ATTACH_BPF
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#define ASSERT_UNIX_OK(e) ASSERT_GE(e, 0) << strerror(errno)
|
||||||
|
|
||||||
|
// TODO(bvanassche): remove the code below. See also b/357099095.
|
||||||
|
#ifndef SO_ATTACH_BPF
|
||||||
|
#define SO_ATTACH_BPF 50 // From <asm-generic/socket.h>.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using ::android::base::unique_fd;
|
||||||
|
using ::testing::ScopedTrace;
|
||||||
|
|
||||||
|
struct test_data {
|
||||||
|
bool discarded;
|
||||||
|
std::string_view str;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t binary_bpf_prog[] = {
|
||||||
|
#include "filterPowerSupplyEvents.h"
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<std::unique_ptr<ScopedTrace>>* msg_vec;
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const test_data& td) {
|
||||||
|
os << "{.discarded=" << td.discarded << ", .str=";
|
||||||
|
for (auto c : td.str) {
|
||||||
|
if (isprint(c)) {
|
||||||
|
os << c;
|
||||||
|
} else {
|
||||||
|
os << ".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os << '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RECORD_ERR_MSG(fmt, ...) \
|
||||||
|
do { \
|
||||||
|
char* str; \
|
||||||
|
if (asprintf(&str, fmt, ##__VA_ARGS__) < 0) break; \
|
||||||
|
auto st = std::make_unique<ScopedTrace>(__FILE__, __LINE__, str); \
|
||||||
|
msg_vec->emplace_back(std::move(st)); \
|
||||||
|
free(str); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int libbpf_print_fn(enum libbpf_print_level, const char* fmt, va_list args) {
|
||||||
|
char* str;
|
||||||
|
if (vasprintf(&str, fmt, args) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
msg_vec->emplace_back(std::make_unique<ScopedTrace>(__FILE__, -1, str));
|
||||||
|
free(str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void record_libbpf_output() {
|
||||||
|
libbpf_set_print(libbpf_print_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
class filterPseTest : public testing::TestWithParam<test_data> {};
|
||||||
|
|
||||||
|
struct ConnectedSockets {
|
||||||
|
unique_fd write_fd;
|
||||||
|
unique_fd read_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
// socketpair() only supports AF_UNIX sockets. AF_UNIX sockets do not
|
||||||
|
// support BPF filters. Hence connect two TCP sockets with each other.
|
||||||
|
static ConnectedSockets ConnectSockets(int domain, int type, int protocol) {
|
||||||
|
int _server_fd = socket(domain, type, protocol);
|
||||||
|
if (_server_fd < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
unique_fd server_fd(_server_fd);
|
||||||
|
|
||||||
|
int _write_fd = socket(domain, type, protocol);
|
||||||
|
if (_write_fd < 0) {
|
||||||
|
RECORD_ERR_MSG("socket: %s", strerror(errno));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
unique_fd write_fd(_write_fd);
|
||||||
|
|
||||||
|
struct sockaddr_in sa = {.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY};
|
||||||
|
if (bind(_server_fd, (const struct sockaddr*)&sa, sizeof(sa)) < 0) {
|
||||||
|
RECORD_ERR_MSG("bind: %s", strerror(errno));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (listen(_server_fd, 1) < 0) {
|
||||||
|
RECORD_ERR_MSG("listen: %s", strerror(errno));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
socklen_t addr_len = sizeof(sa);
|
||||||
|
if (getsockname(_server_fd, (struct sockaddr*)&sa, &addr_len) < 0) {
|
||||||
|
RECORD_ERR_MSG("getsockname: %s", strerror(errno));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
if (connect(_write_fd, (const struct sockaddr*)&sa, sizeof(sa)) < 0 && errno != EINPROGRESS) {
|
||||||
|
RECORD_ERR_MSG("connect: %s", strerror(errno));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
int _read_fd = accept(_server_fd, NULL, NULL);
|
||||||
|
if (_read_fd < 0) {
|
||||||
|
RECORD_ERR_MSG("accept: %s", strerror(errno));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
unique_fd read_fd(_read_fd);
|
||||||
|
|
||||||
|
return {.write_fd = std::move(write_fd), .read_fd = std::move(read_fd)};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(filterPseTest, filterPse) {
|
||||||
|
if (getuid() != 0) {
|
||||||
|
GTEST_SKIP() << "Must be run as root.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!msg_vec) {
|
||||||
|
msg_vec = new typeof(*msg_vec);
|
||||||
|
}
|
||||||
|
std::unique_ptr<int, void (*)(int*)> clear_msg_vec_at_end_of_scope(new int, [](int* p) {
|
||||||
|
msg_vec->clear();
|
||||||
|
delete p;
|
||||||
|
});
|
||||||
|
record_libbpf_output();
|
||||||
|
|
||||||
|
auto connected_sockets = ConnectSockets(AF_INET, SOCK_STREAM, 0);
|
||||||
|
unique_fd write_fd = std::move(connected_sockets.write_fd);
|
||||||
|
unique_fd read_fd = std::move(connected_sockets.read_fd);
|
||||||
|
|
||||||
|
ASSERT_UNIX_OK(fcntl(read_fd, F_SETFL, O_NONBLOCK));
|
||||||
|
|
||||||
|
bpf_object* obj = bpf_object__open_mem(binary_bpf_prog, sizeof(binary_bpf_prog), NULL);
|
||||||
|
ASSERT_TRUE(obj) << "bpf_object__open() failed" << strerror(errno);
|
||||||
|
|
||||||
|
// Find the BPF program within the object.
|
||||||
|
bpf_program* prog = bpf_object__find_program_by_name(obj, "filterPowerSupplyEvents");
|
||||||
|
ASSERT_TRUE(prog);
|
||||||
|
|
||||||
|
ASSERT_UNIX_OK(bpf_program__set_type(prog, BPF_PROG_TYPE_SOCKET_FILTER));
|
||||||
|
|
||||||
|
ASSERT_UNIX_OK(bpf_object__load(obj));
|
||||||
|
|
||||||
|
int filter_fd = bpf_program__fd(prog);
|
||||||
|
ASSERT_UNIX_OK(filter_fd);
|
||||||
|
|
||||||
|
int setsockopt_result =
|
||||||
|
setsockopt(read_fd, SOL_SOCKET, SO_ATTACH_BPF, &filter_fd, sizeof(filter_fd));
|
||||||
|
ASSERT_UNIX_OK(setsockopt_result);
|
||||||
|
|
||||||
|
const test_data param = GetParam();
|
||||||
|
const std::string header(sizeof(struct nlmsghdr), '\0');
|
||||||
|
ASSERT_EQ(header.length(), sizeof(struct nlmsghdr));
|
||||||
|
const std::string data = header + std::string(param.str);
|
||||||
|
const size_t len = data.length();
|
||||||
|
std::cerr.write(data.data(), data.length());
|
||||||
|
std::cerr << ")\n";
|
||||||
|
ASSERT_EQ(write(write_fd, data.data(), len), len);
|
||||||
|
std::array<uint8_t, 512> read_buf;
|
||||||
|
int bytes_read = read(read_fd, read_buf.data(), read_buf.size());
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
ASSERT_EQ(errno, EAGAIN);
|
||||||
|
bytes_read = 0;
|
||||||
|
} else {
|
||||||
|
ASSERT_LT(bytes_read, read_buf.size());
|
||||||
|
}
|
||||||
|
EXPECT_EQ(bytes_read, param.discarded ? 0 : len);
|
||||||
|
|
||||||
|
bpf_object__close(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
filterPse, filterPseTest,
|
||||||
|
testing::Values(test_data{false, "a"},
|
||||||
|
test_data{true, std::string_view("abc\0SUBSYSTEM=block\0", 20)},
|
||||||
|
test_data{true, std::string_view("\0SUBSYSTEM=block", 16)},
|
||||||
|
test_data{true, std::string_view("\0SUBSYSTEM=power_supply", 23)},
|
||||||
|
test_data{false, std::string_view("\0SUBSYSTEM=power_supply\0", 24)},
|
||||||
|
test_data{
|
||||||
|
false,
|
||||||
|
"012345678901234567890123456789012345678901234567890123456789012345"
|
||||||
|
"678901234567890123456789012345678901234567890123456789012345678901"
|
||||||
|
"234567890123456789012345678901234567890123456789012345678901234567"
|
||||||
|
"890123456789012345678901234567890123456789\0SUBSYSTEM=block\0"}));
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <android-base/result.h>
|
||||||
#include <android-base/unique_fd.h>
|
#include <android-base/unique_fd.h>
|
||||||
#include <healthd/healthd.h>
|
#include <healthd/healthd.h>
|
||||||
|
|
||||||
@@ -87,6 +88,7 @@ class HealthLoop {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int InitInternal();
|
int InitInternal();
|
||||||
|
static android::base::Result<void> AttachFilter(int uevent_fd);
|
||||||
void MainLoop();
|
void MainLoop();
|
||||||
void WakeAlarmInit();
|
void WakeAlarmInit();
|
||||||
void WakeAlarmEvent(uint32_t);
|
void WakeAlarmEvent(uint32_t);
|
||||||
|
|||||||
Reference in New Issue
Block a user