[automerger skipped] Add test coverage for VPN local exclusion am: 7c9888cb0b -s ours

am skip reason: Merged-In Ibd47718b4e64048f815bd49737e362e6360bcd98 with SHA-1 d73826d98c is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/system/netd/+/19997517

Change-Id: I0e80d82c4cfcd9896cab1f25ee37ab350828c507
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index b654f76..8771237 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,14 +85,13 @@
         "-performance-noexcept-move-constructor",
         "-performance-unnecessary-value-param",
     ],
-    tidy_flags: [
-        "-warnings-as-errors="
-        + "android-*,"
-        + "bugprone-*,"
-        + "cert-*,"
-        + "clang-analyzer-security*,"
-        + "google-*,"
-        + "misc-*,"
-        + "performance-*"
+    tidy_checks_as_errors: [
+        "android-*",
+        "bugprone-*",
+        "cert-*",
+        "clang-analyzer-security*",
+        "google-*",
+        "misc-*",
+        "performance-*",
     ],
 }
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 4a9fa19..5e200e9 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -14,8 +14,10 @@
     "imports": [
         { "path": "packages/modules/DnsResolver" }
     ],
-    "hwasan-postsubmit": [
+    "hwasan-presubmit": [
         { "name": "netd_integration_test" },
-        { "name": "netd_unit_test" }
+        { "name": "netd_unit_test" },
+        { "name": "netdclient_test" },
+        { "name": "netdutils_test" }
     ]
 }
diff --git a/server/Android.bp b/server/Android.bp
index 06ea0ba..615e740 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -97,6 +97,7 @@
         "system/netd/include",
     ],
     init_rc: ["netd.rc"],
+    vintf_fragments: ["android.system.net.netd-service.xml"],
     required: [
         "bpfloader",
     ],
@@ -104,8 +105,10 @@
     shared_libs: [
         "android.system.net.netd@1.0",
         "android.system.net.netd@1.1",
+        "android.system.net.netd-V1-ndk",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcutils",
         "libdl",
         "libhidlbase",
@@ -138,6 +141,7 @@
         "MDnsSdListener.cpp",
         "MDnsService.cpp",
         "NetdCommand.cpp",
+        "NetdHwAidlService.cpp",
         "NetdHwService.cpp",
         "NetdNativeService.cpp",
         "NetlinkHandler.cpp",
diff --git a/server/Controllers.cpp b/server/Controllers.cpp
index 00ee186..43a2d1e 100644
--- a/server/Controllers.cpp
+++ b/server/Controllers.cpp
@@ -131,7 +131,7 @@
     std::string command = StringPrintf("*%s\n-S %s\nCOMMIT\n", table, parentChain);
     std::string output;
     if (Controllers::execIptablesRestoreWithOutput(target, command, &output) == -1) {
-        ALOGE("Error listing chain %s in table %s\n", parentChain, table);
+        ALOGE("Error listing chain %s in table %s", parentChain, table);
         return existing;
     }
 
diff --git a/server/FirewallController.h b/server/FirewallController.h
index 6d6f48f..1bff064 100644
--- a/server/FirewallController.h
+++ b/server/FirewallController.h
@@ -52,8 +52,6 @@
   /* Match traffic owned by given UID. This is specific to a particular chain. */
   int setUidRule(ChildChain, int, FirewallRule);
 
-  int enableChildChains(ChildChain, bool);
-
   static std::string makeCriticalCommands(IptablesTarget target, const char* chainName);
 
   static const char* TABLE;
diff --git a/server/InterfaceControllerTest.cpp b/server/InterfaceControllerTest.cpp
index 006018d..8075431 100644
--- a/server/InterfaceControllerTest.cpp
+++ b/server/InterfaceControllerTest.cpp
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 
 #include <netdutils/MockSyscalls.h>
+#include <netdutils/NetNativeTestBase.h>
 #include <netdutils/Utils.h>
 
 #include "InterfaceController.h"
@@ -66,7 +67,7 @@
 
 }  // namespace
 
-class StablePrivacyTest : public testing::Test {
+class StablePrivacyTest : public NetNativeTestBase {
   protected:
     void expectOpenFile(const std::string& path, const Fd fd, int err) {
         if (err == 0) {
@@ -179,7 +180,7 @@
     EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface));
 }
 
-class GetIfaceListTest : public testing::Test {};
+class GetIfaceListTest : public NetNativeTestBase {};
 
 TEST_F(GetIfaceListTest, IfaceNames) {
     StatusOr<std::vector<std::string>> ifaceNames = getIfaceNames();
diff --git a/server/IptablesBaseTest.cpp b/server/IptablesBaseTest.cpp
index ef4d743..dc70ae7 100644
--- a/server/IptablesBaseTest.cpp
+++ b/server/IptablesBaseTest.cpp
@@ -105,7 +105,7 @@
 }
 
 void IptablesBaseTest::expectIptablesRestoreCommands(const ExpectedIptablesCommands& expectedCmds) {
-    EXPECT_EQ(expectedCmds.size(), sRestoreCmds.size());
+    ASSERT_EQ(expectedCmds.size(), sRestoreCmds.size());
     for (size_t i = 0; i < expectedCmds.size(); i++) {
         EXPECT_EQ(expectedCmds[i], sRestoreCmds[i]) <<
             "iptables-restore command " << i << " differs";
diff --git a/server/IptablesBaseTest.h b/server/IptablesBaseTest.h
index abe3f84..bfcc71a 100644
--- a/server/IptablesBaseTest.h
+++ b/server/IptablesBaseTest.h
@@ -18,9 +18,11 @@
 
 #include <deque>
 
+#include <netdutils/NetNativeTestBase.h>
+
 #include "NetdConstants.h"
 
-class IptablesBaseTest : public ::testing::Test {
+class IptablesBaseTest : public NetNativeTestBase {
 public:
     IptablesBaseTest();
 
diff --git a/server/IptablesRestoreController.cpp b/server/IptablesRestoreController.cpp
index f7ba200..dc71830 100644
--- a/server/IptablesRestoreController.cpp
+++ b/server/IptablesRestoreController.cpp
@@ -241,12 +241,12 @@
         return;
     }
 
-    ALOGE("iptables error:\n");
-    ALOGE("------- COMMAND -------\n");
-    ALOGE("%s\n", command.c_str());
-    ALOGE("-------  ERROR -------\n");
+    ALOGE("iptables error:");
+    ALOGE("------- COMMAND -------");
+    ALOGE("%s", command.c_str());
+    ALOGE("-------  ERROR -------");
     ALOGE("%s", process->errBuf.c_str());
-    ALOGE("----------------------\n");
+    ALOGE("----------------------");
     process->errBuf.clear();
 }
 
diff --git a/server/IptablesRestoreControllerTest.cpp b/server/IptablesRestoreControllerTest.cpp
index a05c76d..cecdf4d 100644
--- a/server/IptablesRestoreControllerTest.cpp
+++ b/server/IptablesRestoreControllerTest.cpp
@@ -32,6 +32,7 @@
 #include <android-base/strings.h>
 #include <log/log.h>
 #include <netdutils/MockSyscalls.h>
+#include <netdutils/NetNativeTestBase.h>
 #include <netdutils/Stopwatch.h>
 
 #include "NetdConstants.h"
@@ -55,7 +56,7 @@
 using testing::Return;
 using testing::StrictMock;
 
-class IptablesRestoreControllerTest : public ::testing::Test {
+class IptablesRestoreControllerTest : public NetNativeTestBase {
 public:
   IptablesRestoreController con;
   int mDefaultMaxRetries = con.MAX_RETRIES;
diff --git a/server/NFLogListenerTest.cpp b/server/NFLogListenerTest.cpp
index 88ab2c6..878c988 100644
--- a/server/NFLogListenerTest.cpp
+++ b/server/NFLogListenerTest.cpp
@@ -25,6 +25,7 @@
 #include <linux/netfilter/nfnetlink_log.h>
 
 #include <netdutils/MockSyscalls.h>
+#include <netdutils/NetNativeTestBase.h>
 #include "NFLogListener.h"
 
 using ::testing::_;
@@ -58,7 +59,7 @@
     MOCK_METHOD1(registerSkErrorHandler, void(const SkErrorHandler& handler));
 };
 
-class NFLogListenerTest : public testing::Test {
+class NFLogListenerTest : public NetNativeTestBase {
   protected:
     NFLogListenerTest() {
         EXPECT_CALL(*mNLListener, subscribe(kNFLogPacketMsgType, _))
diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp
index 6de164f..fa21f44 100644
--- a/server/NetdConstants.cpp
+++ b/server/NetdConstants.cpp
@@ -155,8 +155,7 @@
 
     sigemptyset(&mask);
     sigaddset(&mask, SIGPIPE);
-    if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0)
-        ALOGW("WARNING: SIGPIPE not blocked\n");
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0) ALOGW("WARNING: SIGPIPE not blocked");
 }
 
 void setCloseOnExec(const char *sock) {
diff --git a/server/NetdHwAidlService.cpp b/server/NetdHwAidlService.cpp
new file mode 100644
index 0000000..4258153
--- /dev/null
+++ b/server/NetdHwAidlService.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 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 "NetdHwAidlService.h"
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include "Controllers.h"
+#include "Fwmark.h"
+#include "RouteController.h"
+#include "TetherController.h"
+
+// Tells TetherController::enableForwarding who is requesting forwarding, so that TetherController
+// can manage/refcount requests to enable forwarding by multiple parties such as the framework, this
+// binder interface, and the legacy "ndc ipfwd enable <requester>" commands.
+namespace {
+constexpr const char* FORWARDING_REQUESTER = "NetdHwAidlService";
+}
+
+namespace android {
+namespace net {
+namespace aidl {
+
+static int toHalStatus(int ret) {
+    switch (ret) {
+        case 0:
+            return 0;
+        case -EINVAL:
+            return NetdHwAidlService::STATUS_INVALID_ARGUMENTS;
+        case -EEXIST:
+            return NetdHwAidlService::STATUS_ALREADY_EXISTS;
+        case -ENONET:
+            return NetdHwAidlService::STATUS_NO_NETWORK;
+        case -EPERM:
+            return NetdHwAidlService::STATUS_PERMISSION_DENIED;
+        default:
+            ALOGE("HAL service error=%d", ret);
+            return NetdHwAidlService::STATUS_UNKNOWN_ERROR;
+    }
+}
+
+void NetdHwAidlService::run() {
+    std::shared_ptr<NetdHwAidlService> service = ndk::SharedRefBase::make<NetdHwAidlService>();
+
+    const std::string instance = std::string() + NetdHwAidlService::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(service->asBinder().get(), instance.c_str());
+    if (status != STATUS_OK) {
+        ALOGE("Failed to register AIDL INetd service. Status: %d.", status);
+        return;
+    }
+
+    ABinderProcess_joinThreadPool();
+}
+
+ScopedAStatus NetdHwAidlService::createOemNetwork(OemNetwork* network) {
+    unsigned netId;
+    Permission permission = PERMISSION_SYSTEM;
+
+    int ret = gCtls->netCtrl.createPhysicalOemNetwork(permission, &netId);
+
+    Fwmark fwmark;
+    fwmark.netId = netId;
+    fwmark.explicitlySelected = true;
+    fwmark.protectedFromVpn = true;
+    fwmark.permission = PERMISSION_SYSTEM;
+    network->networkHandle = netIdToNetHandle(netId);
+    network->packetMark = fwmark.intValue;
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+// Vendor code can only modify OEM networks. All other networks are managed by ConnectivityService.
+#define RETURN_IF_NOT_OEM_NETWORK(netId)                                                          \
+    if (((netId) < NetworkController::MIN_OEM_ID) || ((netId) > NetworkController::MAX_OEM_ID)) { \
+        return ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);                 \
+    }
+
+ScopedAStatus NetdHwAidlService::destroyOemNetwork(int64_t netHandle) {
+    unsigned netId = netHandleToNetId(netHandle);
+    RETURN_IF_NOT_OEM_NETWORK(netId);
+
+    auto ret = toHalStatus(gCtls->netCtrl.destroyNetwork(netId));
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+const char* maybeNullString(const std::string& nexthop) {
+    // std::strings can't be null, but RouteController wants null instead of an empty string.
+    const char* nh = nexthop.c_str();
+    if (nh && !*nh) {
+        nh = nullptr;
+    }
+    return nh;
+}
+
+ScopedAStatus NetdHwAidlService::addRouteToOemNetwork(int64_t networkHandle,
+                                                      const std::string& ifname,
+                                                      const std::string& destination,
+                                                      const std::string& nexthop) {
+    unsigned netId = netHandleToNetId(networkHandle);
+    RETURN_IF_NOT_OEM_NETWORK(netId);
+
+    auto ret = gCtls->netCtrl.addRoute(netId, ifname.c_str(), destination.c_str(),
+                                       maybeNullString(nexthop), false, INVALID_UID, 0 /* mtu */);
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+ScopedAStatus NetdHwAidlService::removeRouteFromOemNetwork(int64_t networkHandle,
+                                                           const std::string& ifname,
+                                                           const std::string& destination,
+                                                           const std::string& nexthop) {
+    unsigned netId = netHandleToNetId(networkHandle);
+    RETURN_IF_NOT_OEM_NETWORK(netId);
+
+    auto ret = gCtls->netCtrl.removeRoute(netId, ifname.c_str(), destination.c_str(),
+                                          maybeNullString(nexthop), false, INVALID_UID);
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+ScopedAStatus NetdHwAidlService::addInterfaceToOemNetwork(int64_t networkHandle,
+                                                          const std::string& ifname) {
+    unsigned netId = netHandleToNetId(networkHandle);
+    RETURN_IF_NOT_OEM_NETWORK(netId);
+
+    auto ret = gCtls->netCtrl.addInterfaceToNetwork(netId, ifname.c_str());
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+ScopedAStatus NetdHwAidlService::removeInterfaceFromOemNetwork(int64_t networkHandle,
+                                                               const std::string& ifname) {
+    unsigned netId = netHandleToNetId(networkHandle);
+    RETURN_IF_NOT_OEM_NETWORK(netId);
+
+    auto ret = gCtls->netCtrl.removeInterfaceFromNetwork(netId, ifname.c_str());
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+ScopedAStatus NetdHwAidlService::setIpForwardEnable(bool enable) {
+    std::lock_guard _lock(gCtls->tetherCtrl.lock);
+
+    bool success = enable ? gCtls->tetherCtrl.enableForwarding(FORWARDING_REQUESTER)
+                          : gCtls->tetherCtrl.disableForwarding(FORWARDING_REQUESTER);
+
+    if (!success) {
+        return ScopedAStatus::fromServiceSpecificError(STATUS_UNKNOWN_ERROR);
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+ScopedAStatus NetdHwAidlService::setForwardingBetweenInterfaces(const std::string& inputIfName,
+                                                                const std::string& outputIfName,
+                                                                bool enable) {
+    std::lock_guard _lock(gCtls->tetherCtrl.lock);
+
+    // TODO: check that one interface is an OEM interface and the other is another OEM interface, an
+    // IPsec interface or a dummy interface.
+    int ret = enable ? RouteController::enableTethering(inputIfName.c_str(), outputIfName.c_str())
+                     : RouteController::disableTethering(inputIfName.c_str(), outputIfName.c_str());
+    if (ret != 0) {
+        return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret));
+    } else {
+        return ScopedAStatus::ok();
+    }
+}
+
+}  // namespace aidl
+}  // namespace net
+}  // namespace android
diff --git a/server/NetdHwAidlService.h b/server/NetdHwAidlService.h
new file mode 100644
index 0000000..5cc5b6f
--- /dev/null
+++ b/server/NetdHwAidlService.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#pragma once
+
+#include <aidl/android/system/net/netd/BnNetd.h>
+
+namespace android {
+namespace net {
+namespace aidl {
+using NetdHw = ::aidl::android::system::net::netd::BnNetd;
+using OemNetwork = ::aidl::android::system::net::netd::INetd::OemNetwork;
+using ScopedAStatus = ::ndk::ScopedAStatus;
+
+class NetdHwAidlService : public NetdHw {
+  public:
+    // Start and run the AIDL service.
+    // This blocks when joining the threadpool so start this in a separate thread.
+    static void run();
+    ScopedAStatus createOemNetwork(OemNetwork* network) override;
+    ScopedAStatus destroyOemNetwork(int64_t netHandle) override;
+    ScopedAStatus addRouteToOemNetwork(int64_t networkHandle, const std::string& ifname,
+                                       const std::string& destination,
+                                       const std::string& nexthop) override;
+    ScopedAStatus removeRouteFromOemNetwork(int64_t networkHandle, const std::string& ifname,
+                                            const std::string& destination,
+                                            const std::string& nexthop) override;
+    ScopedAStatus addInterfaceToOemNetwork(int64_t networkHandle,
+                                           const std::string& ifname) override;
+    ScopedAStatus removeInterfaceFromOemNetwork(int64_t networkHandle,
+                                                const std::string& ifname) override;
+    ScopedAStatus setIpForwardEnable(bool enable) override;
+    ScopedAStatus setForwardingBetweenInterfaces(const std::string& inputIfName,
+                                                 const std::string& outputIfName,
+                                                 bool enable) override;
+};
+
+}  // namespace aidl
+}  // namespace net
+}  // namespace android
diff --git a/server/NetdHwService.cpp b/server/NetdHwService.cpp
index 15855da..d4a0e58 100644
--- a/server/NetdHwService.cpp
+++ b/server/NetdHwService.cpp
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
+#include "NetdHwService.h"
 #include <binder/IPCThreadState.h>
-#include <hidl/HidlTransportSupport.h>
 #include "Controllers.h"
 #include "Fwmark.h"
-#include "NetdHwService.h"
 #include "RouteController.h"
 #include "TetherController.h"
 
-using android::hardware::configureRpcThreadpool;
 using android::hardware::Void;
 
 // Tells TetherController::enableForwarding who is requesting forwarding, so that TetherController
@@ -55,9 +53,6 @@
 
 // Minimal service start.
 status_t NetdHwService::start() {
-    IPCThreadState::self()->disableBackgroundScheduling(true);
-    // Usage of this HAL is anticipated to be thin; one thread should suffice.
-    configureRpcThreadpool(1, false /* callerWillNotJoin */);
     // Register hardware service with ServiceManager.
     return INetd::registerAsService();
 }
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index a4e05b0..9e63a8c 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -106,7 +106,7 @@
 
     if (!strcmp(subsys, "net")) {
         NetlinkEvent::Action action = evt->getAction();
-        const char *iface = evt->findParam("INTERFACE");
+        const char *iface = evt->findParam("INTERFACE") ?: "";
         if (action == NetlinkEvent::Action::kAdd) {
             notifyInterfaceAdded(iface);
         } else if (action == NetlinkEvent::Action::kRemove) {
diff --git a/server/Network.cpp b/server/Network.cpp
index 85f942f..156cfb3 100644
--- a/server/Network.cpp
+++ b/server/Network.cpp
@@ -117,18 +117,12 @@
     }
 }
 
-bool Network::canAddUidRanges(const UidRanges& uidRanges, int32_t subPriority) const {
+bool Network::canAddUidRanges(const UidRanges& uidRanges) const {
     if (uidRanges.overlapsSelf()) {
         ALOGE("uid range %s overlaps self", uidRanges.toString().c_str());
         return false;
     }
 
-    auto iter = mUidRangeMap.find(subPriority);
-    if (iter != mUidRangeMap.end() && uidRanges.overlaps(iter->second)) {
-        ALOGE("uid range %s overlaps priority %d %s", uidRanges.toString().c_str(), subPriority,
-              iter->second.toString().c_str());
-        return false;
-    }
     return true;
 }
 
diff --git a/server/Network.h b/server/Network.h
index e18e1cd..dfead17 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -65,7 +65,7 @@
 
 protected:
     explicit Network(unsigned netId, bool secure = false);
-    bool canAddUidRanges(const UidRanges& uidRanges, int32_t subPriority) const;
+    bool canAddUidRanges(const UidRanges& uidRanges) const;
 
     const unsigned mNetId;
     std::set<std::string> mInterfaces;
diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp
index bb3f653..6813064 100644
--- a/server/PhysicalNetwork.cpp
+++ b/server/PhysicalNetwork.cpp
@@ -165,7 +165,7 @@
 }
 
 int PhysicalNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) {
-    if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) {
+    if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) {
         return -EINVAL;
     }
 
diff --git a/server/Process.cpp b/server/Process.cpp
index f43e82d..fa14587 100644
--- a/server/Process.cpp
+++ b/server/Process.cpp
@@ -85,7 +85,7 @@
     sigemptyset(&mask);
     sigaddset(&mask, SIGPIPE);
     if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0) {
-        ALOGW("WARNING: SIGPIPE not blocked\n");
+        ALOGW("WARNING: SIGPIPE not blocked");
     }
 }
 
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index d2af9a3..dca6bd9 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -64,7 +64,7 @@
 
 const char* const RouteController::LOCAL_MANGLE_INPUT = "routectrl_mangle_INPUT";
 
-const IPPrefix V4_LOCAL_ADDR[] = {
+const IPPrefix V4_LOCAL_PREFIXES[] = {
         IPPrefix::forString("169.254.0.0/16"),  // Link Local
         IPPrefix::forString("100.64.0.0/10"),   // CGNAT
         IPPrefix::forString("10.0.0.0/8"),      // RFC1918
@@ -1392,8 +1392,8 @@
     return modifyDefaultNetwork(RTM_DELRULE, interface, permission);
 }
 
-bool RouteController::isTargetV4LocalRange(const char* dst) {
-    for (IPPrefix addr : V4_LOCAL_ADDR) {
+bool RouteController::isWithinIpv4LocalPrefix(const char* dst) {
+    for (IPPrefix addr : V4_LOCAL_PREFIXES) {
         if (addr.contains(IPPrefix::forString(dst))) {
             return true;
         }
@@ -1401,14 +1401,14 @@
     return false;
 }
 
-bool RouteController::isLocalAddress(TableType tableType, const char* destination,
-                                     const char* nexthop) {
+bool RouteController::isLocalRoute(TableType tableType, const char* destination,
+                                   const char* nexthop) {
     IPPrefix prefix = IPPrefix::forString(destination);
     return nexthop == nullptr && tableType == RouteController::INTERFACE &&
            // Skip default route to prevent network being modeled as point-to-point interfaces.
            ((prefix.family() == AF_INET6 && prefix != IPPrefix::forString("::/0")) ||
             // Skip adding non-target local network range.
-            (prefix.family() == AF_INET && isTargetV4LocalRange(destination)));
+            (prefix.family() == AF_INET && isWithinIpv4LocalPrefix(destination)));
 }
 
 int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop,
@@ -1418,7 +1418,7 @@
         return ret;
     }
 
-    if (isLocalAddress(tableType, destination, nexthop)) {
+    if (isLocalRoute(tableType, destination, nexthop)) {
         return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination,
                            nexthop, tableType, mtu, priority, true /* isLocal */);
     }
@@ -1433,7 +1433,7 @@
         return ret;
     }
 
-    if (isLocalAddress(tableType, destination, nexthop)) {
+    if (isLocalRoute(tableType, destination, nexthop)) {
         return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
                            tableType, 0 /* mtu */, priority, true /* isLocal */);
     }
@@ -1447,7 +1447,7 @@
         return ret;
     }
 
-    if (isLocalAddress(tableType, destination, nexthop)) {
+    if (isLocalRoute(tableType, destination, nexthop)) {
         return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination,
                            nexthop, tableType, mtu, 0 /* priority */, true /* isLocal */);
     }
diff --git a/server/RouteController.h b/server/RouteController.h
index ff41678..f57bc24 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -230,8 +230,8 @@
 
     static int modifyUidLocalNetworkRule(const char* interface, uid_t uidStart, uid_t uidEnd,
                                          bool add);
-    static bool isLocalAddress(TableType tableType, const char* destination, const char* nexthop);
-    static bool isTargetV4LocalRange(const char* addrstr);
+    static bool isLocalRoute(TableType tableType, const char* destination, const char* nexthop);
+    static bool isWithinIpv4LocalPrefix(const char* addrstr);
 };
 
 // Public because they are called by by RouteControllerTest.cpp.
diff --git a/server/SockDiagTest.cpp b/server/SockDiagTest.cpp
index 49601aa..864d08d 100644
--- a/server/SockDiagTest.cpp
+++ b/server/SockDiagTest.cpp
@@ -24,6 +24,7 @@
 #include <linux/inet_diag.h>
 
 #include <gtest/gtest.h>
+#include <netdutils/NetNativeTestBase.h>
 
 #include "Fwmark.h"
 #include "NetdConstants.h"
@@ -33,7 +34,7 @@
 namespace android {
 namespace net {
 
-class SockDiagTest : public ::testing::Test {
+class SockDiagTest : public NetNativeTestBase {
 protected:
     static bool isLoopbackSocket(const inet_diag_msg *msg) {
         return SockDiag::isLoopbackSocket(msg);
diff --git a/server/UidRanges.cpp b/server/UidRanges.cpp
index c90f30b..765df32 100644
--- a/server/UidRanges.cpp
+++ b/server/UidRanges.cpp
@@ -145,25 +145,6 @@
     return false;
 }
 
-// std::binary_search cannot do partial match. For example, an uid range x-y not only overlaps with
-// x-y, but also w-x, y-z, w-z, ...etc. Therefore, we need a specialized binary search.
-bool UidRanges::overlaps(const UidRanges& other) const {
-    for (const auto& target : other.getRanges()) {
-        int first = 0;
-        int end = mRanges.size() - 1;
-
-        while (first <= end) {
-            int middle = (first + end) / 2;
-            if (isOverlapped(mRanges[middle], target)) return true;
-            if (compUidRangeParcel(mRanges[middle], target))
-                first = middle + 1;
-            else
-                end = middle - 1;
-        }
-    }
-    return false;
-}
-
 std::string UidRanges::toString() const {
     std::string s("uids{ ");
     for (const auto &range : mRanges) {
diff --git a/server/UidRanges.h b/server/UidRanges.h
index 9123eb1..f20dc44 100644
--- a/server/UidRanges.h
+++ b/server/UidRanges.h
@@ -51,12 +51,10 @@
 
     // check if 'mRanges' has uid overlap between elements.
     bool overlapsSelf() const;
-    // check if this object has uid overlap with the input object.
-    bool overlaps(const UidRanges& other) const;
+
     bool empty() const { return mRanges.empty(); }
 
   private:
-    // Keep it sorted. The overlaps() implements binary search, which requires a sorted data.
     std::vector<UidRangeParcel> mRanges;
 };
 
diff --git a/server/UnreachableNetwork.cpp b/server/UnreachableNetwork.cpp
index 6880225..dd6318c 100644
--- a/server/UnreachableNetwork.cpp
+++ b/server/UnreachableNetwork.cpp
@@ -27,7 +27,7 @@
 UnreachableNetwork::UnreachableNetwork(unsigned netId) : Network(netId) {}
 
 int UnreachableNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) {
-    if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) {
+    if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) {
         return -EINVAL;
     }
 
diff --git a/server/VirtualNetwork.cpp b/server/VirtualNetwork.cpp
index 495fd16..e0f6040 100644
--- a/server/VirtualNetwork.cpp
+++ b/server/VirtualNetwork.cpp
@@ -33,7 +33,7 @@
 VirtualNetwork::~VirtualNetwork() {}
 
 int VirtualNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) {
-    if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) {
+    if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) {
         return -EINVAL;
     }
 
diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp
index e7f5cfc..f1c3807 100644
--- a/server/XfrmControllerTest.cpp
+++ b/server/XfrmControllerTest.cpp
@@ -41,6 +41,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
+#include <netdutils/NetNativeTestBase.h>
 
 #include "Fwmark.h"
 #include "NetdConstants.h"
@@ -127,7 +128,7 @@
     EXPECT_EQ(expected, actualStr);
 }
 
-class XfrmControllerTest : public ::testing::Test {
+class XfrmControllerTest : public NetNativeTestBase {
   public:
     testing::StrictMock<netdutils::ScopedMockSyscalls> mockSyscalls;
 };
diff --git a/server/android.system.net.netd-service.xml b/server/android.system.net.netd-service.xml
new file mode 100644
index 0000000..7152da1
--- /dev/null
+++ b/server/android.system.net.netd-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.system.net.netd</name>
+        <version>1</version>
+        <fqname>INetd/default</fqname>
+    </hal>
+</manifest>
diff --git a/server/main.cpp b/server/main.cpp
index 0e81d4e..35c53de 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -32,8 +32,11 @@
 
 #include "log/log.h"
 
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <hidl/HidlTransportSupport.h>
 #include <netdutils/Stopwatch.h>
 #include <processgroup/processgroup.h>
 
@@ -43,6 +46,7 @@
 #include "MDnsService.h"
 #include "NFLogListener.h"
 #include "NetdConstants.h"
+#include "NetdHwAidlService.h"
 #include "NetdHwService.h"
 #include "NetdNativeService.h"
 #include "NetlinkManager.h"
@@ -64,6 +68,7 @@
 using android::net::NetdNativeService;
 using android::net::NetlinkManager;
 using android::net::NFLogListener;
+using android::net::aidl::NetdHwAidlService;
 using android::netdutils::Stopwatch;
 
 const char* const PID_FILE_PATH = "/data/misc/net/netd_pid";
@@ -162,8 +167,8 @@
         }
         logListener = std::move(result.value());
         auto status = gCtls->wakeupCtrl.init(logListener.get());
-        if (!isOk(result)) {
-            gLog.error("Unable to init WakeupController: %s", toString(result).c_str());
+        if (!isOk(status)) {
+            gLog.error("Unable to init WakeupController: %s", toString(status).c_str());
             // We can still continue without wakeup packet logging.
         }
     }
@@ -202,16 +207,26 @@
     android::net::process::ScopedPidFile pidFile(PID_FILE_PATH);
 
     // Now that netd is ready to process commands, advertise service availability for HAL clients.
+    // Usage of this HAL is anticipated to be thin; one thread per HAL service should suffice,
+    // AIDL and HIDL.
+    android::hardware::configureRpcThreadpool(2, true /* callerWillJoin */);
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+
+    std::thread aidlService = std::thread(NetdHwAidlService::run);
+
     sp<NetdHwService> mHwSvc(new NetdHwService());
+    bool startedHidlService = true;
     if ((ret = mHwSvc->start()) != android::OK) {
-        ALOGE("Unable to start NetdHwService: %d", ret);
-        exit(1);
+        ALOGE("Unable to start HIDL NetdHwService: %d", ret);
+        startedHidlService = false;
     }
+
     gLog.info("Registering NetdHwService: %" PRId64 "us", subTime.getTimeAndResetUs());
     gLog.info("Netd started in %" PRId64 "us", s.timeTakenUs());
-
-    IPCThreadState::self()->joinThreadPool();
-
+    if (startedHidlService) {
+        IPCThreadState::self()->joinThreadPool();
+    }
+    aidlService.join();
     gLog.info("netd exiting");
 
     exit(0);
diff --git a/tests/Android.bp b/tests/Android.bp
index ff918cc..3b27300 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -75,6 +75,7 @@
         "device-tests",
         "vts"
     ],
+    isolated: false,
     require_root: true,
     defaults: [
         "netd_aidl_interface_lateststable_cpp_static",
diff --git a/tests/benchmarks/bpf_benchmark.cpp b/tests/benchmarks/bpf_benchmark.cpp
index bf4bd54..4de8066 100644
--- a/tests/benchmarks/bpf_benchmark.cpp
+++ b/tests/benchmarks/bpf_benchmark.cpp
@@ -18,6 +18,7 @@
 #include <android-base/stringprintf.h>
 #include <benchmark/benchmark.h>
 
+#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
 #include "bpf/BpfMap.h"
 #include "bpf/BpfUtils.h"
 
diff --git a/tests/benchmarks/dns_benchmark.cpp b/tests/benchmarks/dns_benchmark.cpp
index b8f626e..060e40a 100644
--- a/tests/benchmarks/dns_benchmark.cpp
+++ b/tests/benchmarks/dns_benchmark.cpp
@@ -65,11 +65,12 @@
             std::vector<std::string> domains = { "example.com" };
             std::vector<std::string> servers;
             dns.SetupMappings(num_hosts, domains, &mappings);
-
             dns.SetupDNSServers(MAXNS, mappings, &mDns, &servers);
-
-            const std::vector<int> mDefaultParams_Binder = {300, 25, 8, 8, 1000};
-            dns.SetResolversForNetwork(servers, domains, mDefaultParams_Binder);
+            dns.SetResolversFromParcel(ResolverParams::Builder()
+                                               .setDnsServers(servers)
+                                               .setDotServers({})
+                                               .setDomains(domains)
+                                               .build());
         }
     }
 
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index dae1af3..52608f9 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -58,6 +58,7 @@
 #include <com/android/internal/net/IOemNetd.h>
 #include <cutils/multiuser.h>
 #include <gtest/gtest.h>
+#include <netdutils/NetNativeTestBase.h>
 #include <netutils/ifc.h>
 #include <utils/Errors.h>
 #include "Fwmark.h"
@@ -176,7 +177,7 @@
         {// 2001:db8:cafe::8888
          .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}};
 
-class NetdBinderTest : public ::testing::Test {
+class NetdBinderTest : public NetNativeTestBase {
   public:
     NetdBinderTest() {
         sp<IServiceManager> sm = android::defaultServiceManager();
@@ -645,7 +646,7 @@
                                               int32_t subPriority) {
     NativeUidRangeConfig res;
     res.netId = netId;
-    res.uidRanges = move(uidRanges);
+    res.uidRanges = std::move(uidRanges);
     res.subPriority = subPriority;
 
     return res;
@@ -1241,7 +1242,7 @@
     for (const auto& path : { IPTABLES_PATH, IP6TABLES_PATH }) {
         delTetherCounterValues(path, intIface1, extIface1);
         delTetherCounterValues(path, intIface2, extIface2);
-        if (path == IP6TABLES_PATH) {
+        if (strcmp(path, IP6TABLES_PATH) == 0) {
             delTetherCounterValues(path, intIface3, extIface2);
         }
     }
@@ -3910,7 +3911,7 @@
 
 void verifyAppUidRules(std::vector<bool>&& expectedResults, NativeUidRangeConfig& uidRangeConfig,
                        const std::string& iface) {
-    verifyAppUidRules(move(expectedResults), uidRangeConfig.uidRanges, iface,
+    verifyAppUidRules(std::move(expectedResults), uidRangeConfig.uidRanges, iface,
                       uidRangeConfig.subPriority);
 }
 
@@ -3999,7 +4000,7 @@
 
 }  // namespace
 
-// Verify whether API reject overlapped UID ranges
+// Verify how the API handle overlapped UID ranges
 TEST_F(NetdBinderTest, PerAppDefaultNetwork_OverlappedUidRanges) {
     const auto& config = makeNativeNetworkConfig(APP_DEFAULT_NETID, NativeNetworkType::PHYSICAL,
                                                  INetd::PERMISSION_NONE, false, false);
@@ -4013,28 +4014,23 @@
     binder::Status status;
     status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID,
                                         {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)});
-    EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_TRUE(status.isOk());
 
     status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID,
                                         {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 10)});
-    EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_TRUE(status.isOk());
 
     status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID,
                                         {makeUidRangeParcel(BASE_UID + 11, BASE_UID + 11)});
-    EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_TRUE(status.isOk());
 
     status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID,
                                         {makeUidRangeParcel(BASE_UID + 12, BASE_UID + 13)});
-    EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_TRUE(status.isOk());
 
     status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID,
                                         {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 13)});
-    EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_TRUE(status.isOk());
 
     std::vector<UidRangeParcel> selfOverlappedUidRanges = {
             makeUidRangeParcel(BASE_UID + 20, BASE_UID + 20),
@@ -4730,11 +4726,6 @@
     uidRangeConfig.subPriority = SUB_PRIORITY_2;
     EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig).isOk());
 
-    // For a single network, identical UID ranges with the same priority is invalid.
-    status = mNetd->networkAddUidRangesParcel(uidRangeConfig);
-    EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
-
     // Overlapping ranges is invalid.
     uidRangeConfig.uidRanges = {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1),
                                 makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)};
@@ -5112,8 +5103,39 @@
     }
 }
 
-class MDnsBinderTest : public ::testing::Test {
+class MDnsBinderTest : public NetNativeTestBase {
   public:
+    class TestMDnsListener : public android::net::mdns::aidl::BnMDnsEventListener {
+      public:
+        Status onServiceRegistrationStatus(const RegistrationInfo& /*status*/) override {
+            // no-op
+            return Status::ok();
+        }
+        Status onServiceDiscoveryStatus(const DiscoveryInfo& /*status*/) override {
+            // no-op
+            return Status::ok();
+        }
+        Status onServiceResolutionStatus(const ResolutionInfo& /*status*/) override {
+            // no-op
+            return Status::ok();
+        }
+        Status onGettingServiceAddressStatus(const GetAddressInfo& status) override {
+            if (status.id == mOperationId) {
+                std::lock_guard lock(mCvMutex);
+                mCv.notify_one();
+            }
+            return Status::ok();
+        }
+        std::condition_variable& getCv() { return mCv; }
+        std::mutex& getCvMutex() { return mCvMutex; }
+        void setOperationId(int operationId) { mOperationId = operationId; }
+
+      private:
+        std::mutex mCvMutex;
+        std::condition_variable mCv;
+        int mOperationId;
+    };
+
     MDnsBinderTest() {
         sp<IServiceManager> sm = android::defaultServiceManager();
         sp<IBinder> binder = sm->getService(String16("mdns"));
@@ -5122,31 +5144,45 @@
         }
     }
 
-    void SetUp() override { ASSERT_NE(nullptr, mMDns.get()); }
+    void SetUp() override {
+        ASSERT_NE(nullptr, mMDns.get());
+        // Start the daemon for mdns operations.
+        mDaemonStarted = mMDns->startDaemon().isOk();
+    }
 
-    void TearDown() override {}
+    void TearDown() override {
+        if (mDaemonStarted) mMDns->stopDaemon();
+    }
+
+    std::cv_status getServiceAddress(int operationId, const sp<TestMDnsListener>& listener);
 
   protected:
     sp<IMDns> mMDns;
+
+  private:
+    bool mDaemonStarted = false;
 };
 
-class TestMDnsListener : public android::net::mdns::aidl::BnMDnsEventListener {
-  public:
-    Status onServiceRegistrationStatus(const RegistrationInfo& /* status */) override {
-        return Status::ok();
-    }
-    Status onServiceDiscoveryStatus(const DiscoveryInfo& /* status */) override {
-        return Status::ok();
-    }
-    Status onServiceResolutionStatus(const ResolutionInfo& /* status */) override {
-        return Status::ok();
-    }
-    Status onGettingServiceAddressStatus(const GetAddressInfo& /* status */) override {
-        return Status::ok();
-    }
-};
+std::cv_status MDnsBinderTest::getServiceAddress(int operationId,
+                                                 const sp<TestMDnsListener>& listener) {
+    GetAddressInfo info;
+    info.id = operationId;
+    info.hostname = "Android.local";
+    info.interfaceIdx = 0;
+    binder::Status status = mMDns->getServiceAddress(info);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    auto& cv = listener->getCv();
+    auto& cvMutex = listener->getCvMutex();
+    std::unique_lock lock(cvMutex);
+    // Wait for a long time to prevent test flaky.
+    return cv.wait_for(lock, std::chrono::milliseconds(2500));
+}
 
 TEST_F(MDnsBinderTest, EventListenerTest) {
+    // Start the Binder thread pool.
+    android::ProcessState::self()->startThreadPool();
+
     // Register a null listener.
     binder::Status status = mMDns->registerEventListener(nullptr);
     EXPECT_FALSE(status.isOk());
@@ -5155,8 +5191,8 @@
     status = mMDns->unregisterEventListener(nullptr);
     EXPECT_FALSE(status.isOk());
 
-    // Register the test listener.
-    android::sp<TestMDnsListener> testListener = new TestMDnsListener();
+    // Register a test listener
+    auto testListener = android::sp<TestMDnsListener>::make();
     status = mMDns->registerEventListener(testListener);
     EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
 
@@ -5164,7 +5200,28 @@
     status = mMDns->registerEventListener(testListener);
     EXPECT_FALSE(status.isOk());
 
+    // Verify the listener can receive callback.
+    int id = arc4random_uniform(10000);  // use random number
+    testListener->setOperationId(id);
+    EXPECT_EQ(std::cv_status::no_timeout, getServiceAddress(id, testListener));
+    // Stop getting address operation to release the service reference on MDnsSd
+    status = mMDns->stopOperation(id);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
     // Unregister the test listener
     status = mMDns->unregisterEventListener(testListener);
     EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Verify the listener can not receive callback.
+    testListener->setOperationId(id + 1);
+    EXPECT_EQ(std::cv_status::timeout, getServiceAddress(id + 1, testListener));
+    // Stop getting address operation to release the service reference on MDnsSd
+    status = mMDns->stopOperation(id + 1);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Registering and unregistering the listener again should work.
+    status = mMDns->registerEventListener(testListener);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    status = mMDns->unregisterEventListener(testListener);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
 }
diff --git a/tests/bpf_base_test.cpp b/tests/bpf_base_test.cpp
index e402e08..8a21506 100644
--- a/tests/bpf_base_test.cpp
+++ b/tests/bpf_base_test.cpp
@@ -33,6 +33,7 @@
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <netdutils/NetNativeTestBase.h>
 
 #include "bpf/BpfMap.h"
 #include "bpf/BpfUtils.h"
@@ -48,7 +49,7 @@
 constexpr uid_t TEST_UID = UID_MAX - 1;
 constexpr uint32_t TEST_TAG = 42;
 
-class BpfBasicTest : public testing::Test {
+class BpfBasicTest : public NetNativeTestBase {
   protected:
     BpfBasicTest() {}
 };
@@ -84,7 +85,7 @@
 
 TEST_F(BpfBasicTest, TestTagSocket) {
     BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
-    ASSERT_LE(0, cookieTagMap.getMap());
+    ASSERT_TRUE(cookieTagMap.isValid());
     int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
     ASSERT_LE(0, sock);
     uint64_t cookie = getSocketCookie(sock);
@@ -102,7 +103,7 @@
 
 TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) {
     BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
-    ASSERT_LE(0, cookieTagMap.getMap());
+    ASSERT_TRUE(cookieTagMap.isValid());
     int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
     ASSERT_LE(0, sock);
     uint64_t cookie = getSocketCookie(sock);
diff --git a/tests/netd_test.cpp b/tests/netd_test.cpp
index 8d5d8bc..169fa2b 100644
--- a/tests/netd_test.cpp
+++ b/tests/netd_test.cpp
@@ -29,6 +29,7 @@
 
 #include <gtest/gtest.h>
 
+#include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 
 #define LOG_TAG "NetdTest"
@@ -70,6 +71,44 @@
                                        "'^u:object_r:sysfs_net:s0 /sys/class/net/'"));
 }
 
+static void assertBpfContext(const char* const target, const char* const label) {
+    // Use 'ls' cli utility to print the selinux context of the target directory or file.
+    // egrep -q will return 0 if it matches, ie. if the selinux context is as expected
+    std::string cmd = android::base::StringPrintf("ls -dZ %s | egrep -q '^u:object_r:%s:s0 %s$'",
+                                                  target, label, target);
+
+    // NOLINTNEXTLINE(cert-env33-c)
+    ASSERT_EQ(W_EXITCODE(0, 0), system(cmd.c_str())) << cmd << " - did not return success(0)";
+}
+
+// This test will fail if kernel is missing:
+//   https://android-review.googlesource.com/c/kernel/common/+/1831252
+//   UPSTREAM: security: selinux: allow per-file labeling for bpffs
+TEST(NetdSELinuxTest, CheckProperBpfLabels) {
+    assertBpfContext("/sys/fs/bpf", "fs_bpf");
+    assertBpfContext("/sys/fs/bpf/net_private", "fs_bpf_net_private");
+    assertBpfContext("/sys/fs/bpf/net_shared", "fs_bpf_net_shared");
+    assertBpfContext("/sys/fs/bpf/netd_readonly", "fs_bpf_netd_readonly");
+    assertBpfContext("/sys/fs/bpf/netd_shared", "fs_bpf_netd_shared");
+    assertBpfContext("/sys/fs/bpf/vendor", "fs_bpf_vendor");
+}
+
+bool isTetheringInProcess() {
+    int v = access("/apex/com.android.tethering/etc/flag/in-process", F_OK);
+    if (!v) return true;
+    EXPECT_EQ(v, -1) << "expected return of found(0) or notfound(-1/ENOENT)";
+    EXPECT_EQ(errno, ENOENT) << "expected return of found(0) or notfound(-1/ENOENT)";
+    return false;
+}
+
+TEST(NetdSELinuxTest, CheckProperBpfTetheringLabels) {
+    if (isTetheringInProcess()) {
+        assertBpfContext("/sys/fs/bpf/net_shared/tethering", "fs_bpf_net_shared");
+    } else {
+        assertBpfContext("/sys/fs/bpf/tethering", "fs_bpf_tethering");
+    }
+}
+
 // Trivial thread function that simply immediately terminates successfully.
 static int thread(void*) {
     return 0;
diff --git a/tests/sock_diag_test.cpp b/tests/sock_diag_test.cpp
index 8ee9908..5d142dc 100644
--- a/tests/sock_diag_test.cpp
+++ b/tests/sock_diag_test.cpp
@@ -21,6 +21,7 @@
 #include <linux/inet_diag.h>
 
 #include <gtest/gtest.h>
+#include <netdutils/NetNativeTestBase.h>
 
 #include "NetdConstants.h"
 #include "SockDiag.h"
@@ -29,7 +30,7 @@
 #define NUM_SOCKETS 500
 
 
-class SockDiagTest : public ::testing::Test {
+class SockDiagTest : public NetNativeTestBase {
 };
 
 uint16_t bindAndListen(int s) {