Merge changes from topic "native_isUidNetworkingBlocked" into main

* changes:
  [Test] for libcom.android.tethering.dns_helper.so
  Add a library for DNS resolver to read bpf maps
diff --git a/DnsResolver/Android.bp b/DnsResolver/Android.bp
new file mode 100644
index 0000000..d133034
--- /dev/null
+++ b/DnsResolver/Android.bp
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2023 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "libcom.android.tethering.dns_helper",
+    version_script: "libcom.android.tethering.dns_helper.map.txt",
+    stubs: {
+        versions: [
+            "1",
+        ],
+        symbol_file: "libcom.android.tethering.dns_helper.map.txt",
+    },
+    defaults: ["netd_defaults"],
+    header_libs: [
+        "bpf_connectivity_headers",
+        "libcutils_headers",
+    ],
+    srcs: [
+        "DnsBpfHelper.cpp",
+        "DnsHelper.cpp",
+    ],
+    static_libs: [
+        "libmodules-utils-build",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    export_include_dirs: ["include"],
+    header_abi_checker: {
+        enabled: true,
+        symbol_file: "libcom.android.tethering.dns_helper.map.txt",
+    },
+    sanitize: {
+        cfi: true,
+    },
+    apex_available: ["com.android.tethering"],
+    min_sdk_version: "30",
+}
+
+cc_test {
+    name: "dns_helper_unit_test",
+    defaults: ["netd_defaults"],
+    test_suites: ["general-tests", "mts-tethering"],
+    test_config_template: ":net_native_test_config_template",
+    header_libs: [
+        "bpf_connectivity_headers",
+    ],
+    srcs: [
+        "DnsBpfHelperTest.cpp",
+    ],
+    static_libs: [
+        "libcom.android.tethering.dns_helper",
+    ],
+    shared_libs: [
+       "libbase",
+       "libcutils",
+    ],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+}
diff --git a/DnsResolver/DnsBpfHelper.cpp b/DnsResolver/DnsBpfHelper.cpp
new file mode 100644
index 0000000..fbe4dc3
--- /dev/null
+++ b/DnsResolver/DnsBpfHelper.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "DnsBpfHelper"
+
+#include "DnsBpfHelper.h"
+
+#include <android-base/logging.h>
+#include <android-modules-utils/sdk_level.h>
+
+namespace android {
+namespace net {
+
+base::Result<void> DnsBpfHelper::init() {
+  if (!android::modules::sdklevel::IsAtLeastT()) {
+    LOG(ERROR) << __func__ << ": Unsupported before Android T.";
+    return base::Error(EOPNOTSUPP);
+  }
+
+  auto result = mConfigurationMap.init(CONFIGURATION_MAP_PATH);
+  if (!result.ok()) {
+    LOG(ERROR) << __func__ << ": Failed to init configuration_map: "
+               << strerror(result.error().code());
+    return result;
+  }
+
+  result = mUidOwnerMap.init(UID_OWNER_MAP_PATH);
+  if (!result.ok()) {
+    LOG(ERROR) << __func__ << ": Failed to init uid_owner_map: "
+               << strerror(result.error().code());
+  }
+  return result;
+}
+
+base::Result<bool> DnsBpfHelper::isUidNetworkingBlocked(uid_t uid, bool) {
+  if (is_system_uid(uid)) return false;
+  if (!mConfigurationMap.isValid() || !mUidOwnerMap.isValid()) {
+    LOG(ERROR) << __func__
+               << ": BPF maps are not ready. Forgot to call ADnsHelper_init?";
+    return base::Error(EUNATCH);
+  }
+
+  auto enabledRules = mConfigurationMap.readValue(UID_RULES_CONFIGURATION_KEY);
+  if (!enabledRules.ok()) {
+    LOG(ERROR) << __func__
+               << ": Failed to read enabled rules from configuration_map: "
+               << strerror(enabledRules.error().code());
+    return enabledRules.error();
+  }
+
+  auto value = mUidOwnerMap.readValue(uid);
+  uint32_t uidRules = value.ok() ? value.value().rule : 0;
+
+  if (isBlockedByUidRules(enabledRules.value(), uidRules)) return true;
+
+  // TODO: Read data saver settings from bpf maps. For metered network, check penalty box, happy box
+  // and data saver settings.
+
+  return false;
+}
+
+}  // namespace net
+}  // namespace android
diff --git a/DnsResolver/DnsBpfHelper.h b/DnsResolver/DnsBpfHelper.h
new file mode 100644
index 0000000..5520d29
--- /dev/null
+++ b/DnsResolver/DnsBpfHelper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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 <android-base/result.h>
+
+#include "bpf/BpfMap.h"
+#include "netd.h"
+
+namespace android {
+namespace net {
+
+class DnsBpfHelper {
+ public:
+  DnsBpfHelper() = default;
+  DnsBpfHelper(const DnsBpfHelper&) = delete;
+  DnsBpfHelper& operator=(const DnsBpfHelper&) = delete;
+
+  base::Result<void> init();
+  base::Result<bool> isUidNetworkingBlocked(uid_t uid, bool metered);
+
+ private:
+  android::bpf::BpfMapRO<uint32_t, uint32_t> mConfigurationMap;
+  android::bpf::BpfMapRO<uint32_t, UidOwnerValue> mUidOwnerMap;
+
+  // For testing
+  friend class DnsBpfHelperTest;
+};
+
+}  // namespace net
+}  // namespace android
diff --git a/DnsResolver/DnsBpfHelperTest.cpp b/DnsResolver/DnsBpfHelperTest.cpp
new file mode 100644
index 0000000..9bea729
--- /dev/null
+++ b/DnsResolver/DnsBpfHelperTest.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 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 <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
+#include "DnsBpfHelper.h"
+
+using namespace android::bpf;  // NOLINT(google-build-using-namespace): exempted
+
+namespace android {
+namespace net {
+
+constexpr int TEST_MAP_SIZE = 2;
+
+#define ASSERT_VALID(x) ASSERT_TRUE((x).isValid())
+
+class DnsBpfHelperTest : public ::testing::Test {
+ protected:
+  DnsBpfHelper mDnsBpfHelper;
+  BpfMap<uint32_t, uint32_t> mFakeConfigurationMap;
+  BpfMap<uint32_t, UidOwnerValue> mFakeUidOwnerMap;
+
+  void SetUp() {
+    mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_ARRAY, CONFIGURATION_MAP_SIZE);
+    ASSERT_VALID(mFakeConfigurationMap);
+
+    mFakeUidOwnerMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE);
+    ASSERT_VALID(mFakeUidOwnerMap);
+
+    mDnsBpfHelper.mConfigurationMap = mFakeConfigurationMap;
+    ASSERT_VALID(mDnsBpfHelper.mConfigurationMap);
+    mDnsBpfHelper.mUidOwnerMap = mFakeUidOwnerMap;
+    ASSERT_VALID(mDnsBpfHelper.mUidOwnerMap);
+  }
+
+  void ResetAllMaps() {
+    mDnsBpfHelper.mConfigurationMap.reset();
+    mDnsBpfHelper.mUidOwnerMap.reset();
+  }
+};
+
+TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked) {
+  struct TestConfig {
+    const uid_t uid;
+    const uint32_t enabledRules;
+    const uint32_t uidRules;
+    const int expectedResult;
+    std::string toString() const {
+      return fmt::format(
+          "uid: {}, enabledRules: {}, uidRules: {}, expectedResult: {}",
+          uid, enabledRules, uidRules, expectedResult);
+    }
+  } testConfigs[] = {
+    // clang-format off
+    //   No rule enabled:
+    // uid,         enabledRules,                  uidRules,                      expectedResult
+    {AID_APP_START, NO_MATCH,                      NO_MATCH,                      false},
+
+    //   An allowlist rule:
+    {AID_APP_START, NO_MATCH,                      DOZABLE_MATCH,                 false},
+    {AID_APP_START, DOZABLE_MATCH,                 NO_MATCH,                      true},
+    {AID_APP_START, DOZABLE_MATCH,                 DOZABLE_MATCH,                 false},
+    //   A denylist rule
+    {AID_APP_START, NO_MATCH,                      STANDBY_MATCH,                 false},
+    {AID_APP_START, STANDBY_MATCH,                 NO_MATCH,                      false},
+    {AID_APP_START, STANDBY_MATCH,                 STANDBY_MATCH,                 true},
+
+    //   Multiple rules enabled:
+    //     Match only part of the enabled allowlist rules.
+    {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH,                 true},
+    {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, POWERSAVE_MATCH,               true},
+    //     Match all of the enabled allowlist rules.
+    {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH|POWERSAVE_MATCH, false},
+    //     Match allowlist.
+    {AID_APP_START, DOZABLE_MATCH|STANDBY_MATCH,   DOZABLE_MATCH,                 false},
+    //     Match no rule.
+    {AID_APP_START, DOZABLE_MATCH|STANDBY_MATCH,   NO_MATCH,                      true},
+    {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, NO_MATCH,                      true},
+
+    // System UID: always unblocked.
+    {AID_SYSTEM,    NO_MATCH,                      NO_MATCH,                      false},
+    {AID_SYSTEM,    NO_MATCH,                      DOZABLE_MATCH,                 false},
+    {AID_SYSTEM,    DOZABLE_MATCH,                 NO_MATCH,                      false},
+    {AID_SYSTEM,    DOZABLE_MATCH,                 DOZABLE_MATCH,                 false},
+    {AID_SYSTEM,    NO_MATCH,                      STANDBY_MATCH,                 false},
+    {AID_SYSTEM,    STANDBY_MATCH,                 NO_MATCH,                      false},
+    {AID_SYSTEM,    STANDBY_MATCH,                 STANDBY_MATCH,                 false},
+    {AID_SYSTEM,    DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH,                 false},
+    {AID_SYSTEM,    DOZABLE_MATCH|POWERSAVE_MATCH, POWERSAVE_MATCH,               false},
+    {AID_SYSTEM,    DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH|POWERSAVE_MATCH, false},
+    {AID_SYSTEM,    DOZABLE_MATCH|STANDBY_MATCH,   DOZABLE_MATCH,                 false},
+    {AID_SYSTEM,    DOZABLE_MATCH|STANDBY_MATCH,   NO_MATCH,                      false},
+    {AID_SYSTEM,    DOZABLE_MATCH|POWERSAVE_MATCH, NO_MATCH,                      false},
+    // clang-format on
+  };
+
+  for (const auto& config : testConfigs) {
+    SCOPED_TRACE(config.toString());
+
+    // Setup maps.
+    EXPECT_RESULT_OK(mFakeConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY,
+                                                      config.enabledRules, BPF_EXIST));
+    EXPECT_RESULT_OK(mFakeUidOwnerMap.writeValue(config.uid, {.iif = 0, .rule = config.uidRules},
+                                                 BPF_ANY));
+
+    // Verify the function.
+    auto result = mDnsBpfHelper.isUidNetworkingBlocked(config.uid, /*metered=*/false);
+    EXPECT_TRUE(result.ok());
+    EXPECT_EQ(config.expectedResult, result.value());
+  }
+}
+
+TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked_uninitialized) {
+  ResetAllMaps();
+
+  auto result = mDnsBpfHelper.isUidNetworkingBlocked(AID_APP_START, /*metered=*/false);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(EUNATCH, result.error().code());
+
+  result = mDnsBpfHelper.isUidNetworkingBlocked(AID_SYSTEM, /*metered=*/false);
+  EXPECT_TRUE(result.ok());
+  EXPECT_FALSE(result.value());
+}
+
+}  // namespace net
+}  // namespace android
diff --git a/DnsResolver/DnsHelper.cpp b/DnsResolver/DnsHelper.cpp
new file mode 100644
index 0000000..3372908
--- /dev/null
+++ b/DnsResolver/DnsHelper.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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 <errno.h>
+
+#include "DnsBpfHelper.h"
+#include "DnsHelperPublic.h"
+
+static android::net::DnsBpfHelper sDnsBpfHelper;
+
+int ADnsHelper_init() {
+  auto result = sDnsBpfHelper.init();
+  if (!result.ok()) return -result.error().code();
+
+  return 0;
+}
+
+int ADnsHelper_isUidNetworkingBlocked(uid_t uid, bool metered) {
+  auto result = sDnsBpfHelper.isUidNetworkingBlocked(uid, metered);
+  if (!result.ok()) return -result.error().code();
+
+  // bool -> int conversion.
+  return result.value();
+}
diff --git a/DnsResolver/include/DnsHelperPublic.h b/DnsResolver/include/DnsHelperPublic.h
new file mode 100644
index 0000000..7c9fc9e
--- /dev/null
+++ b/DnsResolver/include/DnsHelperPublic.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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 <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * Perform any required initialization - including opening any required BPF maps. This function
+ * needs to be called before using other functions of this library.
+ *
+ * Returns 0 on success, a negative POSIX error code (see errno.h) on other failures.
+ */
+int ADnsHelper_init();
+
+/*
+ * The function reads bpf maps and returns whether the given uid has blocked networking or not. The
+ * function is supported starting from Android T.
+ *
+ * |uid| is a Linux/Android UID to be queried. It is a combination of UserID and AppID.
+ * |metered| indicates whether the uid is currently using a billing network.
+ *
+ * Returns 0(false)/1(true) on success, a negative POSIX error code (see errno.h) on other failures.
+ */
+int ADnsHelper_isUidNetworkingBlocked(uid_t uid, bool metered);
+
+__END_DECLS
diff --git a/DnsResolver/libcom.android.tethering.dns_helper.map.txt b/DnsResolver/libcom.android.tethering.dns_helper.map.txt
new file mode 100644
index 0000000..3c965a2
--- /dev/null
+++ b/DnsResolver/libcom.android.tethering.dns_helper.map.txt
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2023 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.
+#
+
+# This lists the entry points visible to applications that use the
+# libcom.android.tethering.dns_helper library. Other entry points present in
+# the library won't be usable.
+
+LIBCOM_ANDROID_TETHERING_DNS_HELPER {
+  global:
+    ADnsHelper_init; # apex
+    ADnsHelper_isUidNetworkingBlocked; # apex
+  local:
+    *;
+};
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index e69b872..dd60be7 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -226,6 +226,7 @@
         "com.android.tethering",
     ],
     native_shared_libs: [
+        "libcom.android.tethering.dns_helper",
         "libnetd_updatable",
     ],
 }
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index cd8eac8..ee44f3c 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -83,6 +83,7 @@
                 "libandroid_net_connectivity_com_android_net_module_util_jni",
             ],
             native_shared_libs: [
+                "libcom.android.tethering.dns_helper",
                 "libcom.android.tethering.connectivity_native",
                 "libnetd_updatable",
             ],
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index b3f8ed6..cdf47e7 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -45,6 +45,7 @@
         "com.android.tethering",
     ],
     visibility: [
+        "//packages/modules/Connectivity/DnsResolver",
         "//packages/modules/Connectivity/netd",
         "//packages/modules/Connectivity/service",
         "//packages/modules/Connectivity/service/native/libs/libclat",
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 5759df7..f223dd1 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -92,7 +92,7 @@
 DEFINE_BPF_MAP_RO_NETD(stats_map_A, HASH, StatsKey, StatsValue, STATS_MAP_SIZE)
 DEFINE_BPF_MAP_RO_NETD(stats_map_B, HASH, StatsKey, StatsValue, STATS_MAP_SIZE)
 DEFINE_BPF_MAP_NO_NETD(iface_stats_map, HASH, uint32_t, StatsValue, IFACE_STATS_MAP_SIZE)
-DEFINE_BPF_MAP_NO_NETD(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE)
+DEFINE_BPF_MAP_RO_NETD(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE)
 DEFINE_BPF_MAP_RO_NETD(uid_permission_map, HASH, uint32_t, uint8_t, UID_OWNER_MAP_SIZE)
 DEFINE_BPF_MAP_NO_NETD(ingress_discard_map, HASH, IngressDiscardKey, IngressDiscardValue,
                        INGRESS_DISCARD_MAP_SIZE)