Merge "Mark 'android.hardware.common.fmq' as available in any apex" into main
diff --git a/audio/OWNERS b/audio/OWNERS
index 3671685..80e2765 100644
--- a/audio/OWNERS
+++ b/audio/OWNERS
@@ -2,3 +2,4 @@
 
 elaurent@google.com
 mnaganov@google.com
+yaoshunkai@google.com
diff --git a/audio/aidl/default/EffectContext.cpp b/audio/aidl/default/EffectContext.cpp
index 7b8cfb1..5539177 100644
--- a/audio/aidl/default/EffectContext.cpp
+++ b/audio/aidl/default/EffectContext.cpp
@@ -242,4 +242,17 @@
     LOG(VERBOSE) << __func__ << " : signal client for reopen";
     return RetCode::SUCCESS;
 }
+
+RetCode EffectContext::enable() {
+    return RetCode::SUCCESS;
+}
+
+RetCode EffectContext::disable() {
+    return RetCode::SUCCESS;
+}
+
+RetCode EffectContext::reset() {
+    return RetCode::SUCCESS;
+}
+
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectImpl.cpp b/audio/aidl/default/EffectImpl.cpp
index 7192d97..3e61335 100644
--- a/audio/aidl/default/EffectImpl.cpp
+++ b/audio/aidl/default/EffectImpl.cpp
@@ -246,7 +246,6 @@
             startThread();
             break;
         case CommandId::STOP:
-        case CommandId::RESET:
             RETURN_OK_IF(mState == State::IDLE);
             mState = State::IDLE;
             RETURN_IF(notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS, EX_ILLEGAL_STATE,
@@ -254,6 +253,13 @@
             stopThread();
             RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed");
             break;
+        case CommandId::RESET:
+            mState = State::IDLE;
+            RETURN_IF(notifyEventFlag(mDataMqNotEmptyEf) != RetCode::SUCCESS, EX_ILLEGAL_STATE,
+                      "notifyEventFlagNotEmptyFailed");
+            stopThread();
+            RETURN_IF_ASTATUS_NOT_OK(commandImpl(command), "commandImplFailed");
+            break;
         default:
             LOG(ERROR) << getEffectNameWithVersion() << __func__ << " instance still processing";
             return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
@@ -266,8 +272,22 @@
 
 ndk::ScopedAStatus EffectImpl::commandImpl(CommandId command) {
     RETURN_IF(!mImplContext, EX_NULL_POINTER, "nullContext");
-    if (command == CommandId::RESET) {
-        mImplContext->resetBuffer();
+    switch (command) {
+        case CommandId::START:
+            mImplContext->enable();
+            break;
+        case CommandId::STOP:
+            mImplContext->disable();
+            break;
+        case CommandId::RESET:
+            mImplContext->disable();
+            mImplContext->reset();
+            mImplContext->resetBuffer();
+            break;
+        default:
+            LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "commandIdNotSupported");
     }
     return ndk::ScopedAStatus::ok();
 }
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
index 275378e..02a4caa 100644
--- a/audio/aidl/default/include/effect-impl/EffectContext.h
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -82,6 +82,10 @@
 
     virtual ::android::hardware::EventFlag* getStatusEventFlag();
 
+    virtual RetCode enable();
+    virtual RetCode disable();
+    virtual RetCode reset();
+
   protected:
     int mVersion = 0;
     size_t mInputFrameSize = 0;
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index cad1195..0fa170f 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -397,10 +397,10 @@
                                                               outputBuffer.size(), outputBuffer));
         }
 
+        // Disable the process
         ASSERT_NO_FATAL_FAILURE(command(effect, CommandId::STOP));
         EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(statusMQ, 0, outputMQ, 0, outputBuffer));
 
-        // Disable the process
         ASSERT_NO_FATAL_FAILURE(command(effect, CommandId::RESET));
     }
 
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
index 9b89055..61c29d3 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
@@ -818,7 +818,7 @@
       auto matched_setting = matchWithRequirement(
           matched_ase_configuration_settings, requirement, false);
       if (matched_setting.has_value()) {
-        result.push_back(matched_setting_with_context.value());
+        result.push_back(matched_setting.value());
       } else {
         // Cannot find a match for this requirement
         // Immediately return
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index 110a628..4481238 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -142,6 +142,7 @@
   VERSION_AIDL_V2,
   VERSION_AIDL_V3,
   VERSION_AIDL_V4,
+  VERSION_AIDL_V5,
 };
 
 // Some valid configs for HFP PCM configuration (software sessions)
@@ -683,6 +684,8 @@
         return BluetoothAudioHalVersion::VERSION_AIDL_V3;
       case 4:
         return BluetoothAudioHalVersion::VERSION_AIDL_V4;
+      case 5:
+        return BluetoothAudioHalVersion::VERSION_AIDL_V5;
       default:
         return BluetoothAudioHalVersion::VERSION_UNAVAILABLE;
     }
diff --git a/health/2.1/default/Android.bp b/health/2.1/default/Android.bp
index b7bcea5..00d89e2 100644
--- a/health/2.1/default/Android.bp
+++ b/health/2.1/default/Android.bp
@@ -81,7 +81,7 @@
     ],
 
     vintf_fragments: [
-        "android.hardware.health@2.1.xml"
+        "android.hardware.health@2.1.xml",
     ],
 
     overrides: [
diff --git a/health/utils/libhealthloop/Android.bp b/health/utils/libhealthloop/Android.bp
index 7aaf905..4ebc575 100644
--- a/health/utils/libhealthloop/Android.bp
+++ b/health/utils/libhealthloop/Android.bp
@@ -21,6 +21,17 @@
     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 {
     name: "libhealthloop",
     vendor_available: true,
@@ -30,10 +41,11 @@
         "utils.cpp",
     ],
     shared_libs: [
-        "libcutils",
         "libbase",
+        "libcutils",
     ],
     header_libs: [
+        "bpf_headers",
         "libbatteryservice_headers",
         "libhealthd_headers",
         "libutils_headers",
@@ -42,3 +54,30 @@
         "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",
+}
diff --git a/health/utils/libhealthloop/HealthLoop.cpp b/health/utils/libhealthloop/HealthLoop.cpp
index 4190769..70b7745 100644
--- a/health/utils/libhealthloop/HealthLoop.cpp
+++ b/health/utils/libhealthloop/HealthLoop.cpp
@@ -20,23 +20,22 @@
 #include <health/HealthLoop.h>
 
 #include <errno.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
+#include <sys/epoll.h>  // epoll_create1(), epoll_ctl(), epoll_wait()
 #include <sys/timerfd.h>
-#include <unistd.h>
+#include <unistd.h>  // read()
 
 #include <android-base/logging.h>
 #include <batteryservice/BatteryService.h>
-#include <cutils/klog.h>
+#include <cutils/klog.h>  // KLOG_*()
 #include <cutils/uevent.h>
 #include <healthd/healthd.h>
-#include <utils/Errors.h>
 
+#include <BpfSyscallWrappers.h>
 #include <health/utils.h>
 
+using android::base::ErrnoError;
+using android::base::Result;
+using android::base::unique_fd;
 using namespace android;
 using namespace std::chrono_literals;
 
@@ -57,19 +56,18 @@
 int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
     CHECK(!reject_event_register_);
 
-    auto* event_handler =
-            event_handlers_
-                    .emplace_back(std::make_unique<EventHandler>(EventHandler{this, fd, func}))
-                    .get();
+    auto* event_handler = event_handlers_
+                                  .emplace_back(std::make_unique<EventHandler>(
+                                          EventHandler{this, fd, std::move(func)}))
+                                  .get();
 
-    struct epoll_event ev;
-
-    ev.events = EPOLLIN;
+    struct epoll_event ev = {
+            .events = EPOLLIN | EPOLLERR,
+            .data.ptr = reinterpret_cast<void*>(event_handler),
+    };
 
     if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
 
-    ev.data.ptr = reinterpret_cast<void*>(event_handler);
-
     if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
         KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
         return -1;
@@ -122,11 +120,16 @@
     ScheduleBatteryUpdate();
 }
 
-// TODO(b/140330870): Use BPF instead.
 #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.
 
+    if (epevents & EPOLLERR) {
+        // The netlink receive buffer overflowed.
+        ScheduleBatteryUpdate();
+        return;
+    }
+
     char msg[UEVENT_MSG_LEN + 2];
     char* cp;
     int n;
@@ -152,8 +155,26 @@
     }
 }
 
+// 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) {
-    uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
+    uevent_fd_.reset(uevent_create_socket(64 * 1024, true));
 
     if (uevent_fd_ < 0) {
         KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
@@ -161,8 +182,25 @@
     }
 
     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))
         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*/) {
diff --git a/health/utils/libhealthloop/filterPowerSupplyEvents.c b/health/utils/libhealthloop/filterPowerSupplyEvents.c
new file mode 100644
index 0000000..5296993
--- /dev/null
+++ b/health/utils/libhealthloop/filterPowerSupplyEvents.c
@@ -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");
diff --git a/health/utils/libhealthloop/filterPowerSupplyEventsTest.cpp b/health/utils/libhealthloop/filterPowerSupplyEventsTest.cpp
new file mode 100644
index 0000000..e885f0b
--- /dev/null
+++ b/health/utils/libhealthloop/filterPowerSupplyEventsTest.cpp
@@ -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"}));
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
index fc3066e..1af7274 100644
--- a/health/utils/libhealthloop/include/health/HealthLoop.h
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -20,6 +20,7 @@
 #include <mutex>
 #include <vector>
 
+#include <android-base/result.h>
 #include <android-base/unique_fd.h>
 #include <healthd/healthd.h>
 
@@ -87,6 +88,7 @@
     };
 
     int InitInternal();
+    static android::base::Result<void> AttachFilter(int uevent_fd);
     void MainLoop();
     void WakeAlarmInit();
     void WakeAlarmEvent(uint32_t);
diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp
index 6f18d18..e7214e5 100644
--- a/radio/aidl/vts/radio_config_test.cpp
+++ b/radio/aidl/vts/radio_config_test.cpp
@@ -278,7 +278,14 @@
                     EXPECT_LT(logicalSlotId, slotPortMappingList.size());
                     if (logicalSlotId >= 0 && logicalSlotId < slotPortMappingList.size()) {
                         slotPortMappingList[logicalSlotId].physicalSlotId = i;
-                        slotPortMappingList[logicalSlotId].portId = j;
+                        if (radioRsp_config->simSlotStatus[i].supportedMepMode ==
+                                    MultipleEnabledProfilesMode::MEP_A1 ||
+                            radioRsp_config->simSlotStatus[i].supportedMepMode ==
+                                    MultipleEnabledProfilesMode::MEP_A2) {
+                            slotPortMappingList[logicalSlotId].portId = j + 1;
+                        } else {
+                            slotPortMappingList[logicalSlotId].portId = j;
+                        }
                     }
                 }
             }
diff --git a/radio/aidl/vts/radio_sim_test.cpp b/radio/aidl/vts/radio_sim_test.cpp
index 06654c2..e9b68cc 100644
--- a/radio/aidl/vts/radio_sim_test.cpp
+++ b/radio/aidl/vts/radio_sim_test.cpp
@@ -118,7 +118,14 @@
         EXPECT_EQ(CardStatus::STATE_PRESENT, slotStatus.cardState);
         if (CardStatus::STATE_PRESENT == slotStatus.cardState) {
             ASSERT_TRUE(slotStatus.portInfo[0].portActive);
-            EXPECT_EQ(0, cardStatus.slotMap.portId);
+            if (cardStatus.supportedMepMode == aidl::android::hardware::radio::config::
+                                                       MultipleEnabledProfilesMode::MEP_A1 ||
+                cardStatus.supportedMepMode == aidl::android::hardware::radio::config::
+                                                       MultipleEnabledProfilesMode::MEP_A2) {
+                EXPECT_EQ(1, cardStatus.slotMap.portId);
+            } else {
+                EXPECT_EQ(0, cardStatus.slotMap.portId);
+            }
         }
     }
 }
diff --git a/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl b/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
index 3c43238..40cf685 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
+++ b/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
@@ -62,9 +62,13 @@
 SignerName = tstr
 
 UdsCertChain = [
-    2* X509Certificate      ; Root -> ... -> Leaf. "Root" is the vendor self-signed
-                            ; cert, "Leaf" contains UDS_Public. There may also be
-                            ; intermediate certificates between Root and Leaf.
+    + X509Certificate       ; Root -> ... -> Leaf. "Root" is the vendor self-signed
+                            ; cert, "Leaf" contains UDS_Public. It's recommended to
+                            ; have at least 3 certificates in the chain.
+                            ; The Root certificate is recommended to be generated in an air-gapped,
+                            ; HSM-based secure environment. The intermediate signing keys may be
+                            ; online, and should be rotated regularly (e.g. annually). Additionally,
+                            ; the intermediate certificates may contain product family identifiers.
 ]
 
 ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or EdDSA)
diff --git a/threadnetwork/aidl/default/Android.bp b/threadnetwork/aidl/default/Android.bp
index 82a76e0..51e5c25 100644
--- a/threadnetwork/aidl/default/Android.bp
+++ b/threadnetwork/aidl/default/Android.bp
@@ -90,10 +90,20 @@
     installable: false,
 }
 
+filegroup {
+    name: "com.android.hardware.threadnetwork_manifest",
+    srcs: ["manifest.json"],
+}
+
+filegroup {
+    name: "com.android.hardware.threadnetwork_file_contexts",
+    srcs: ["file_contexts"],
+}
+
 apex {
     name: "com.android.hardware.threadnetwork",
-    manifest: "manifest.json",
-    file_contexts: "file_contexts",
+    manifest: ":com.android.hardware.threadnetwork_manifest",
+    file_contexts: ":com.android.hardware.threadnetwork_file_contexts",
     key: "com.android.hardware.key",
     certificate: ":com.android.hardware.certificate",
     updatable: false,
@@ -110,3 +120,21 @@
         "android.hardware.thread_network.prebuilt.xml", // permission
     ],
 }
+
+prebuilt_etc {
+    name: "threadnetwork-service-simulation-rcp.rc",
+    src: "threadnetwork-service-simulation-rcp.rc",
+    installable: false,
+}
+
+// Thread HAL service which uses a simulation RCP (i.e. ot-rcp),
+// typically used in emulator devices.
+override_apex {
+    name: "com.android.hardware.threadnetwork-simulation-rcp",
+    base: "com.android.hardware.threadnetwork",
+    prebuilts: [
+        "threadnetwork-service-simulation-rcp.rc",
+        "threadnetwork-default.xml",
+        "android.hardware.thread_network.prebuilt.xml",
+    ],
+}
diff --git a/threadnetwork/aidl/default/threadnetwork-service-simulation-rcp.rc b/threadnetwork/aidl/default/threadnetwork-service-simulation-rcp.rc
new file mode 100644
index 0000000..3b889eb
--- /dev/null
+++ b/threadnetwork/aidl/default/threadnetwork-service-simulation-rcp.rc
@@ -0,0 +1,3 @@
+service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service spinel+hdlc+forkpty:///apex/com.android.hardware.threadnetwork/bin/ot-rcp?forkpty-arg=1
+    class hal
+    user thread_network