Merge changes I2ead6a77,I6bf2204a am: 4add3c0b71 am: e9070610f2

Change-Id: If41dada9442fbd754ea1a963f69fb7551b3d9351
diff --git a/contents/common/system_links.cc b/contents/common/system_links.cc
index 91b8bd2..207d9b5 100644
--- a/contents/common/system_links.cc
+++ b/contents/common/system_links.cc
@@ -36,16 +36,16 @@
   section->ForEachNamespaces([system_ns_name](Namespace& ns) {
     if (ns.GetName() != system_ns_name) {
       ns.GetLink(system_ns_name)
-          .AddSharedLib("@{STUB_LIBRARIES}", "@{SANITIZER_RUNTIME_LIBRARIES}");
+          .AddSharedLib(Var("STUB_LIBRARIES"),
+                        Var("SANITIZER_RUNTIME_LIBRARIES"));
     }
   });
 }
 
 std::vector<std::string> GetSystemStubLibraries() {
-  std::optional<std::string> stub_libraries_var =
-      android::linkerconfig::modules::Variables::GetValue("STUB_LIBRARIES");
-  return android::base::Split(stub_libraries_var.value_or(""), ":");
+  return android::base::Split(Var("STUB_LIBRARIES", ""), ":");
 }
+
 }  // namespace contents
 }  // namespace linkerconfig
 }  // namespace android
diff --git a/contents/configuration/baseconfig.cc b/contents/configuration/baseconfig.cc
index 94fe3bc..f35d265 100644
--- a/contents/configuration/baseconfig.cc
+++ b/contents/configuration/baseconfig.cc
@@ -50,12 +50,12 @@
   std::vector<DirToSection> dirToSection = {
       {"/system/bin/", "system"},
       {"/system/xbin/", "system"},
-      {"/@{SYSTEM_EXT:system_ext}/bin/", "system"},
+      {"/" + Var("SYSTEM_EXT", "system_ext") + "/bin/", "system"},
 
       // Processes from the product partition will have a separate section if
       // PRODUCT_PRODUCT_VNDK_VERSION is defined. Otherwise, they are run from
       // the "system" section.
-      {"/@{PRODUCT:product}/bin/", "product"},
+      {"/" + Var("PRODUCT", "product") + "/bin/", "product"},
 
       {"/odm/bin/", "vendor"},
       {"/vendor/bin/", "vendor"},
diff --git a/contents/configuration/legacy.cc b/contents/configuration/legacy.cc
index 6b82687..52e0186 100644
--- a/contents/configuration/legacy.cc
+++ b/contents/configuration/legacy.cc
@@ -21,23 +21,6 @@
 using android::linkerconfig::modules::DirToSection;
 using android::linkerconfig::modules::Section;
 
-namespace {
-const std::vector<DirToSection> kDirToSection = {
-    // All binaries gets the same configuration 'legacy'
-    {"/system", "legacy"},
-    {"/@{SYSTEM_EXT:system_ext}", "legacy"},
-    {"/@{PRODUCT:product}", "legacy"},
-    {"/vendor", "legacy"},
-    {"/odm", "legacy"},
-    {"/sbin", "legacy"},
-    // Except for /postinstall, where only /system and /product are searched
-    {"/postinstall", "postinstall"},
-    // Fallback entry to provide APEX namespace lookups for binaries anywhere
-    // else. This must be last.
-    {"/data", "legacy"},
-};
-}  // namespace
-
 namespace android {
 namespace linkerconfig {
 namespace contents {
@@ -49,6 +32,20 @@
   sections.emplace_back(BuildLegacySection(ctx));
   sections.emplace_back(BuildPostInstallSection(ctx));
 
+  const std::vector<DirToSection> kDirToSection = {
+      // All binaries gets the same configuration 'legacy'
+      {"/system", "legacy"},
+      {"/" + Var("SYSTEM_EXT", "system_ext"), "legacy"},
+      {"/" + Var("PRODUCT", "product"), "legacy"},
+      {"/vendor", "legacy"},
+      {"/odm", "legacy"},
+      {"/sbin", "legacy"},
+      // Except for /postinstall, where only /system and /product are searched
+      {"/postinstall", "postinstall"},
+      // Fallback entry to provide APEX namespace lookups for binaries anywhere
+      // else. This must be last.
+      {"/data", "legacy"},
+  };
   return android::linkerconfig::modules::Configuration(std::move(sections),
                                                        kDirToSection);
 }
diff --git a/contents/context/context.cc b/contents/context/context.cc
index 2aadc4e..ede1cd4 100644
--- a/contents/context/context.cc
+++ b/contents/context/context.cc
@@ -16,7 +16,9 @@
 
 #include "linkerconfig/context.h"
 
+#include "linkerconfig/log.h"
 #include "linkerconfig/namespacebuilder.h"
+#include "linkerconfig/variables.h"
 
 using android::linkerconfig::modules::ApexInfo;
 using android::linkerconfig::modules::Namespace;
@@ -87,6 +89,23 @@
   return BaseContext::BuildApexNamespace(apex_info, visible);
 }
 
+std::string Var(const std::string& name) {
+  auto val = modules::Variables::GetValue(name);
+  if (val.has_value()) {
+    return *val;
+  }
+  CHECK(!"undefined var") << name << " is not defined";
+  return "";
+}
+
+std::string Var(const std::string& name, const std::string& default_value) {
+  auto val = modules::Variables::GetValue(name);
+  if (val.has_value()) {
+    return *val;
+  }
+  return default_value;
+}
+
 }  // namespace contents
 }  // namespace linkerconfig
 }  // namespace android
diff --git a/contents/include/linkerconfig/context.h b/contents/include/linkerconfig/context.h
index 9ca8b62..abd953c 100644
--- a/contents/include/linkerconfig/context.h
+++ b/contents/include/linkerconfig/context.h
@@ -79,6 +79,10 @@
   LinkerConfigType current_linkerconfig_type_;
 };
 
+std::string Var(const std::string& name);
+
+std::string Var(const std::string& name, const std::string& default_value);
+
 }  // namespace contents
 }  // namespace linkerconfig
-}  // namespace android
+}  // namespace android
\ No newline at end of file
diff --git a/contents/namespace/postinstall.cc b/contents/namespace/postinstall.cc
index 3ac3bd4..89e75e4 100644
--- a/contents/namespace/postinstall.cc
+++ b/contents/namespace/postinstall.cc
@@ -26,8 +26,9 @@
   Namespace ns("default", /*is_isolated=*/false,
                /*is_visible=*/false);
   ns.AddSearchPath("/system/${LIB}", AsanPath::NONE);
-  ns.AddSearchPath("/@{SYSTEM_EXT:system_ext}/${LIB}", AsanPath::NONE);
-  ns.AddSearchPath("/@{PRODUCT:product}/${LIB}", AsanPath::NONE);
+  ns.AddSearchPath("/" + Var("SYSTEM_EXT", "system_ext") + "/${LIB}",
+                   AsanPath::NONE);
+  ns.AddSearchPath("/" + Var("PRODUCT", "product") + "/${LIB}", AsanPath::NONE);
 
   return ns;
 }
diff --git a/contents/namespace/productdefault.cc b/contents/namespace/productdefault.cc
index ce620df..98eca47 100644
--- a/contents/namespace/productdefault.cc
+++ b/contents/namespace/productdefault.cc
@@ -29,16 +29,17 @@
 Namespace BuildProductDefaultNamespace([[maybe_unused]] const Context& ctx) {
   Namespace ns("default", /*is_isolated=*/true, /*is_visible=*/true);
 
-  ns.AddSearchPath("/@{PRODUCT:product}/${LIB}", AsanPath::WITH_DATA_ASAN);
-  ns.AddPermittedPath("/@{PRODUCT:product}", AsanPath::WITH_DATA_ASAN);
+  ns.AddSearchPath("/" + Var("PRODUCT", "product") + "/${LIB}",
+                   AsanPath::WITH_DATA_ASAN);
+  ns.AddPermittedPath("/" + Var("PRODUCT", "product"), AsanPath::WITH_DATA_ASAN);
 
   ns.GetLink(ctx.GetSystemNamespaceName())
-      .AddSharedLib("@{LLNDK_LIBRARIES_PRODUCT}");
-  ns.GetLink("vndk").AddSharedLib({"@{VNDK_SAMEPROCESS_LIBRARIES_PRODUCT}",
-                                   "@{VNDK_CORE_LIBRARIES_PRODUCT}"});
+      .AddSharedLib(Var("LLNDK_LIBRARIES_PRODUCT"));
+  ns.GetLink("vndk").AddSharedLib({Var("VNDK_SAMEPROCESS_LIBRARIES_PRODUCT"),
+                                   Var("VNDK_CORE_LIBRARIES_PRODUCT")});
   if (android::linkerconfig::modules::IsVndkInSystemNamespace()) {
     ns.GetLink("vndk_in_system")
-        .AddSharedLib("@{VNDK_USING_CORE_VARIANT_LIBRARIES}");
+        .AddSharedLib(Var("VNDK_USING_CORE_VARIANT_LIBRARIES"));
   }
   ns.AddRequires(std::vector{
       "libneuralnetworks.so",
diff --git a/contents/namespace/rs.cc b/contents/namespace/rs.cc
index b73559a..67626d2 100644
--- a/contents/namespace/rs.cc
+++ b/contents/namespace/rs.cc
@@ -32,8 +32,9 @@
 
   ns.AddSearchPath("/odm/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
   ns.AddSearchPath("/vendor/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
-  ns.AddSearchPath("/apex/com.android.vndk.v@{VENDOR_VNDK_VERSION}/${LIB}",
-                   AsanPath::SAME_PATH);
+  ns.AddSearchPath(
+      "/apex/com.android.vndk.v" + Var("VENDOR_VNDK_VERSION") + "/${LIB}",
+      AsanPath::SAME_PATH);
   ns.AddSearchPath("/odm/${LIB}", AsanPath::WITH_DATA_ASAN);
   ns.AddSearchPath("/vendor/${LIB}", AsanPath::WITH_DATA_ASAN);
 
@@ -45,8 +46,8 @@
   // Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
   // namespace because RS framework libs are using them.
   ns.GetLink(ctx.GetSystemNamespaceName())
-      .AddSharedLib(
-          {"@{LLNDK_LIBRARIES_VENDOR}", "@{PRIVATE_LLNDK_LIBRARIES_VENDOR:}"});
+      .AddSharedLib({Var("LLNDK_LIBRARIES_VENDOR"),
+                     Var("PRIVATE_LLNDK_LIBRARIES_VENDOR", "")});
 
   ns.AddRequires(std::vector{"libneuralnetworks.so"});
 
diff --git a/contents/namespace/sphal.cc b/contents/namespace/sphal.cc
index c451bed..74858fb 100644
--- a/contents/namespace/sphal.cc
+++ b/contents/namespace/sphal.cc
@@ -45,9 +45,10 @@
   ns.AddPermittedPath("/system/vendor/${LIB}", AsanPath::NONE);
 
   if (ctx.IsApexBinaryConfig()) {
-    ns.GetLink("vndk").AddSharedLib("@{VNDK_SAMEPROCESS_LIBRARIES_VENDOR:}");
+    ns.GetLink("vndk").AddSharedLib(
+        Var("VNDK_SAMEPROCESS_LIBRARIES_VENDOR", ""));
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib("@{LLNDK_LIBRARIES_VENDOR:}",
+        .AddSharedLib(Var("LLNDK_LIBRARIES_VENDOR", ""),
                       // Add a link for libz.so which is llndk on
                       // devices where VNDK is not enforced.
                       "libz.so");
@@ -58,8 +59,9 @@
     // are capable of loading libRS_internal.so
     ns.GetLink("rs").AddSharedLib("libRS_internal.so");
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib("@{LLNDK_LIBRARIES_VENDOR:}");
-    ns.GetLink("vndk").AddSharedLib("@{VNDK_SAMEPROCESS_LIBRARIES_VENDOR:}");
+        .AddSharedLib(Var("LLNDK_LIBRARIES_VENDOR", ""));
+    ns.GetLink("vndk").AddSharedLib(
+        Var("VNDK_SAMEPROCESS_LIBRARIES_VENDOR", ""));
     ns.AddRequires(std::vector{"libneuralnetworks.so"});
   }
 
diff --git a/contents/namespace/system.cc b/contents/namespace/system.cc
index f5baa7c..1c8c1a1 100644
--- a/contents/namespace/system.cc
+++ b/contents/namespace/system.cc
@@ -31,9 +31,11 @@
 Namespace BuildSystemNamespace([[maybe_unused]] const Context& ctx) {
   Namespace ns("system", /*is_isolated=*/false, /*is_visible=*/false);
   ns.AddSearchPath("/system/${LIB}", AsanPath::WITH_DATA_ASAN);
-  ns.AddSearchPath("/@{SYSTEM_EXT:system_ext}/${LIB}", AsanPath::WITH_DATA_ASAN);
+  ns.AddSearchPath("/" + Var("SYSTEM_EXT", "system_ext") + "/${LIB}",
+                   AsanPath::WITH_DATA_ASAN);
   if (!IsProductVndkVersionDefined()) {
-    ns.AddSearchPath("/@{PRODUCT:product}/${LIB}", AsanPath::WITH_DATA_ASAN);
+    ns.AddSearchPath("/" + Var("PRODUCT", "product") + "/${LIB}",
+                     AsanPath::WITH_DATA_ASAN);
   }
 
   ns.AddRequires(std::vector{"libdexfile_external.so",
diff --git a/contents/namespace/systemdefault.cc b/contents/namespace/systemdefault.cc
index ef4587e..9e8d091 100644
--- a/contents/namespace/systemdefault.cc
+++ b/contents/namespace/systemdefault.cc
@@ -26,64 +26,14 @@
 using android::linkerconfig::modules::IsProductVndkVersionDefined;
 using android::linkerconfig::modules::Namespace;
 
-namespace {
-
-// We can't have entire /system/${LIB} as permitted paths because doing so makes
-// it possible to load libs in /system/${LIB}/vndk* directories by their
-// absolute paths, e.g. dlopen("/system/lib/vndk/libbase.so"). VNDK libs are
-// built with previous versions of Android and thus must not be loaded into this
-// namespace where libs built with the current version of Android are loaded.
-// Mixing the two types of libs in the same namespace can cause unexpected
-// problems.
-const std::vector<std::string> kPermittedPaths = {
-    "/system/${LIB}/drm",
-    "/system/${LIB}/extractors",
-    "/system/${LIB}/hw",
-    "/@{SYSTEM_EXT:system_ext}/${LIB}",
-
-    // These are where odex files are located. libart has to be able to
-    // dlopen the files
-    "/system/framework",
-
-    "/system/app",
-    "/system/priv-app",
-    "/@{SYSTEM_EXT:system_ext}/framework",
-    "/@{SYSTEM_EXT:system_ext}/app",
-    "/@{SYSTEM_EXT:system_ext}/priv-app",
-    "/vendor/framework",
-    "/vendor/app",
-    "/vendor/priv-app",
-    "/system/vendor/framework",
-    "/system/vendor/app",
-    "/system/vendor/priv-app",
-    "/odm/framework",
-    "/odm/app",
-    "/odm/priv-app",
-    "/oem/app",
-    "/@{PRODUCT:product}/framework",
-    "/@{PRODUCT:product}/app",
-    "/@{PRODUCT:product}/priv-app",
-    "/data",
-    "/mnt/expand",
-    "/apex/com.android.runtime/${LIB}/bionic",
-    "/system/${LIB}/bootstrap"};
-
-void BuildPermittedPath(Namespace& ns) {
-  for (const auto& path : kPermittedPaths) {
-    ns.AddPermittedPath(path, AsanPath::SAME_PATH);
-  }
-  if (!IsProductVndkVersionDefined()) {
-    // System processes can use product libs only if product VNDK is not enforced.
-    ns.AddPermittedPath("/@{PRODUCT:product}/${LIB}", AsanPath::SAME_PATH);
-  }
-}
-}  // namespace
-
 namespace android {
 namespace linkerconfig {
 namespace contents {
 Namespace BuildSystemDefaultNamespace([[maybe_unused]] const Context& ctx) {
   bool is_fully_treblelized = ctx.IsDefaultConfig();
+  std::string product = Var("PRODUCT", "product");
+  std::string system_ext = Var("SYSTEM_EXT", "system_ext");
+
   // Visible to allow links to be created at runtime, e.g. through
   // android_link_namespaces in libnativeloader.
   Namespace ns("default",
@@ -91,11 +41,11 @@
                /*is_visible=*/true);
 
   ns.AddSearchPath("/system/${LIB}", AsanPath::WITH_DATA_ASAN);
-  ns.AddSearchPath("/@{SYSTEM_EXT:system_ext}/${LIB}", AsanPath::WITH_DATA_ASAN);
+  ns.AddSearchPath("/" + system_ext + "/${LIB}", AsanPath::WITH_DATA_ASAN);
   if (!IsProductVndkVersionDefined() || !is_fully_treblelized) {
     // System processes can search product libs only if product VNDK is not
     // enforced.
-    ns.AddSearchPath("/@{PRODUCT:product}/${LIB}", AsanPath::WITH_DATA_ASAN);
+    ns.AddSearchPath("/" + product + "/${LIB}", AsanPath::WITH_DATA_ASAN);
   }
   if (!is_fully_treblelized) {
     ns.AddSearchPath("/vendor/${LIB}", AsanPath::WITH_DATA_ASAN);
@@ -103,7 +53,53 @@
   }
 
   if (is_fully_treblelized) {
-    BuildPermittedPath(ns);
+    // We can't have entire /system/${LIB} as permitted paths because doing so
+    // makes it possible to load libs in /system/${LIB}/vndk* directories by
+    // their absolute paths, e.g. dlopen("/system/lib/vndk/libbase.so"). VNDK
+    // libs are built with previous versions of Android and thus must not be
+    // loaded into this namespace where libs built with the current version of
+    // Android are loaded. Mixing the two types of libs in the same namespace
+    // can cause unexpected problems.
+    const std::vector<std::string> permitted_paths = {
+        "/system/${LIB}/drm",
+        "/system/${LIB}/extractors",
+        "/system/${LIB}/hw",
+        "/" + system_ext + "/${LIB}",
+
+        // These are where odex files are located. libart has to be able to
+        // dlopen the files
+        "/system/framework",
+
+        "/system/app",
+        "/system/priv-app",
+        "/" + system_ext + "/framework",
+        "/" + system_ext + "/app",
+        "/" + system_ext + "/priv-app",
+        "/vendor/framework",
+        "/vendor/app",
+        "/vendor/priv-app",
+        "/system/vendor/framework",
+        "/system/vendor/app",
+        "/system/vendor/priv-app",
+        "/odm/framework",
+        "/odm/app",
+        "/odm/priv-app",
+        "/oem/app",
+        "/" + product + "/framework",
+        "/" + product + "/app",
+        "/" + product + "/priv-app",
+        "/data",
+        "/mnt/expand",
+        "/apex/com.android.runtime/${LIB}/bionic",
+        "/system/${LIB}/bootstrap"};
+
+    for (const auto& path : permitted_paths) {
+      ns.AddPermittedPath(path, AsanPath::SAME_PATH);
+    }
+    if (!IsProductVndkVersionDefined()) {
+      // System processes can use product libs only if product VNDK is not enforced.
+      ns.AddPermittedPath("/" + product + "/${LIB}", AsanPath::SAME_PATH);
+    }
   }
 
   ns.AddRequires(std::vector{
diff --git a/contents/namespace/vendordefault.cc b/contents/namespace/vendordefault.cc
index d25d315..3539ab4 100644
--- a/contents/namespace/vendordefault.cc
+++ b/contents/namespace/vendordefault.cc
@@ -67,12 +67,14 @@
   // VNDK-Lite devices require broader access from vendor to system/product partition
   if (is_vndklite) {
     ns.AddSearchPath("/system/${LIB}", AsanPath::WITH_DATA_ASAN);
-    ns.AddSearchPath("/@{SYSTEM_EXT:system_ext}/${LIB}",
+    ns.AddSearchPath("/" + Var("SYSTEM_EXT", "system_ext") + "/${LIB}",
                      AsanPath::WITH_DATA_ASAN);
-    ns.AddSearchPath("/@{PRODUCT:product}/${LIB}", AsanPath::WITH_DATA_ASAN);
+    ns.AddSearchPath("/" + Var("PRODUCT", "product") + "/${LIB}",
+                     AsanPath::WITH_DATA_ASAN);
     // Put system vndk at the last search order in vndk_lite for GSI
-    ns.AddSearchPath("/apex/com.android.vndk.v@{VENDOR_VNDK_VERSION}/${LIB}",
-                     AsanPath::SAME_PATH);
+    ns.AddSearchPath(
+        "/apex/com.android.vndk.v" + Var("VENDOR_VNDK_VERSION") + "/${LIB}",
+        AsanPath::SAME_PATH);
   }
 
   if (ctx.IsDefaultConfig() && GetVendorVndkVersion() == "27") {
@@ -88,12 +90,12 @@
     ns.AddRequires(kVndkLiteArtLibs);
   } else {
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib("@{LLNDK_LIBRARIES_VENDOR}");
-    ns.GetLink("vndk").AddSharedLib({"@{VNDK_SAMEPROCESS_LIBRARIES_VENDOR}",
-                                     "@{VNDK_CORE_LIBRARIES_VENDOR}"});
+        .AddSharedLib(Var("LLNDK_LIBRARIES_VENDOR"));
+    ns.GetLink("vndk").AddSharedLib({Var("VNDK_SAMEPROCESS_LIBRARIES_VENDOR"),
+                                     Var("VNDK_CORE_LIBRARIES_VENDOR")});
     if (android::linkerconfig::modules::IsVndkInSystemNamespace()) {
       ns.GetLink("vndk_in_system")
-          .AddSharedLib("@{VNDK_USING_CORE_VARIANT_LIBRARIES}");
+          .AddSharedLib(Var("VNDK_USING_CORE_VARIANT_LIBRARIES"));
     }
   }
   ns.AddRequires(std::vector{"libneuralnetworks.so"});
diff --git a/contents/namespace/vndk.cc b/contents/namespace/vndk.cc
index a9191c2..9f36bcc 100644
--- a/contents/namespace/vndk.cc
+++ b/contents/namespace/vndk.cc
@@ -41,20 +41,23 @@
     // It is linked from vendor HAL. It must use vendor vndk libs.
     ns.AddSearchPath("/odm/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
     ns.AddSearchPath("/vendor/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
-    ns.AddSearchPath("/apex/com.android.vndk.v@{VENDOR_VNDK_VERSION}/${LIB}",
-                     AsanPath::SAME_PATH);
+    ns.AddSearchPath(
+        "/apex/com.android.vndk.v" + Var("VENDOR_VNDK_VERSION") + "/${LIB}",
+        AsanPath::SAME_PATH);
   } else if (is_product_section) {
     ns.AddSearchPath("/product/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
     ns.AddSearchPath("/product/${LIB}/vndk", AsanPath::WITH_DATA_ASAN);
-    ns.AddSearchPath("/apex/com.android.vndk.v@{PRODUCT_VNDK_VERSION}/${LIB}",
-                     AsanPath::SAME_PATH);
+    ns.AddSearchPath(
+        "/apex/com.android.vndk.v" + Var("PRODUCT_VNDK_VERSION") + "/${LIB}",
+        AsanPath::SAME_PATH);
   } else {
     ns.AddSearchPath("/odm/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
     ns.AddSearchPath("/odm/${LIB}/vndk", AsanPath::WITH_DATA_ASAN);
     ns.AddSearchPath("/vendor/${LIB}/vndk-sp", AsanPath::WITH_DATA_ASAN);
     ns.AddSearchPath("/vendor/${LIB}/vndk", AsanPath::WITH_DATA_ASAN);
-    ns.AddSearchPath("/apex/com.android.vndk.v@{VENDOR_VNDK_VERSION}/${LIB}",
-                     AsanPath::SAME_PATH);
+    ns.AddSearchPath(
+        "/apex/com.android.vndk.v" + Var("VENDOR_VNDK_VERSION") + "/${LIB}",
+        AsanPath::SAME_PATH);
   }
 
   if (is_system_section) {
@@ -69,7 +72,7 @@
 
     // This is exceptionally required since android.hidl.memory@1.0-impl.so is here
     ns.AddPermittedPath(
-        "/apex/com.android.vndk.v@{VENDOR_VNDK_VERSION}/${LIB}/hw",
+        "/apex/com.android.vndk.v" + Var("VENDOR_VNDK_VERSION") + "/${LIB}/hw",
         AsanPath::SAME_PATH);
   }
 
@@ -78,10 +81,10 @@
 
   if (is_product_section) {
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib({"@{LLNDK_LIBRARIES_PRODUCT}"});
+        .AddSharedLib({Var("LLNDK_LIBRARIES_PRODUCT")});
   } else {
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib({"@{LLNDK_LIBRARIES_VENDOR}"});
+        .AddSharedLib({Var("LLNDK_LIBRARIES_VENDOR")});
   }
 
   if (!is_vndklite) {
@@ -96,7 +99,7 @@
 
       if (android::linkerconfig::modules::IsVndkInSystemNamespace()) {
         ns.GetLink("vndk_in_system")
-            .AddSharedLib("@{VNDK_USING_CORE_VARIANT_LIBRARIES}");
+            .AddSharedLib(Var("VNDK_USING_CORE_VARIANT_LIBRARIES"));
       }
     }
   }
diff --git a/contents/namespace/vndkinsystem.cc b/contents/namespace/vndkinsystem.cc
index d47fc47..071e08c 100644
--- a/contents/namespace/vndkinsystem.cc
+++ b/contents/namespace/vndkinsystem.cc
@@ -42,13 +42,15 @@
 
   // The search paths here should be kept the same as that of the 'system' namespace.
   ns.AddSearchPath("/system/${LIB}", AsanPath::WITH_DATA_ASAN);
-  ns.AddSearchPath("/@{SYSTEM_EXT:system_ext}/${LIB}", AsanPath::WITH_DATA_ASAN);
+  ns.AddSearchPath("/" + Var("SYSTEM_EXT", "system_ext") + "/${LIB}",
+                   AsanPath::WITH_DATA_ASAN);
   if (!IsProductVndkVersionDefined()) {
-    ns.AddSearchPath("/@{PRODUCT:product}/${LIB}", AsanPath::WITH_DATA_ASAN);
+    ns.AddSearchPath("/" + Var("PRODUCT", "product") + "/${LIB}",
+                     AsanPath::WITH_DATA_ASAN);
   }
 
   if (android::linkerconfig::modules::IsVndkInSystemNamespace()) {
-    ns.AddWhitelisted("@{VNDK_USING_CORE_VARIANT_LIBRARIES}");
+    ns.AddWhitelisted(Var("VNDK_USING_CORE_VARIANT_LIBRARIES"));
   }
 
   // The links here should be identical to that of the 'vndk' namespace for the
@@ -58,10 +60,10 @@
   //      requires anything vendor would not be a vndk_in_system library.
   if (ctx.IsProductSection()) {
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib("@{LLNDK_LIBRARIES_PRODUCT}");
+        .AddSharedLib(Var("LLNDK_LIBRARIES_PRODUCT"));
   } else {
     ns.GetLink(ctx.GetSystemNamespaceName())
-        .AddSharedLib("@{LLNDK_LIBRARIES_VENDOR}");
+        .AddSharedLib(Var("LLNDK_LIBRARIES_VENDOR"));
   }
   ns.GetLink("vndk").AllowAllSharedLibs();
   ns.AddRequires(std::vector{"libneuralnetworks.so"});
diff --git a/contents/tests/backward_compatibility/testbase.h b/contents/tests/backward_compatibility/testbase.h
index 1c466ae..03ff680 100644
--- a/contents/tests/backward_compatibility/testbase.h
+++ b/contents/tests/backward_compatibility/testbase.h
@@ -18,20 +18,21 @@
 #include "linkerconfig/variables.h"
 
 inline void MockVariables(std::string vndk_ver = "Q") {
-  android::linkerconfig::modules::Variables::AddValue("VNDK_VER", vndk_ver);
+  android::linkerconfig::modules::Variables::AddValue("VENDOR_VNDK_VERSION",
+                                                      vndk_ver);
   android::linkerconfig::modules::Variables::AddValue("PRODUCT", "product");
   android::linkerconfig::modules::Variables::AddValue("SYSTEM_EXT",
                                                       "system_ext");
-  android::linkerconfig::modules::Variables::AddValue("LLNDK_LIBRARIES",
+  android::linkerconfig::modules::Variables::AddValue("LLNDK_LIBRARIES_VENDOR",
                                                       "llndk_libraries");
   android::linkerconfig::modules::Variables::AddValue(
       "SANITIZER_RUNTIME_LIBRARIES", "sanitizer_runtime_libraries");
   android::linkerconfig::modules::Variables::AddValue(
       "PRIVATE_LLNDK_LIBRARIES", "private_llndk_libraries");
   android::linkerconfig::modules::Variables::AddValue(
-      "VNDK_SAMEPROCESS_LIBRARIES", "vndk_sameprocess_libraries");
-  android::linkerconfig::modules::Variables::AddValue("VNDK_CORE_LIBRARIES",
-                                                      "vndk_core_libraries");
+      "VNDK_SAMEPROCESS_LIBRARIES_VENDOR", "vndk_sameprocess_libraries");
+  android::linkerconfig::modules::Variables::AddValue(
+      "VNDK_CORE_LIBRARIES_VENDOR", "vndk_core_libraries");
   android::linkerconfig::modules::Variables::AddValue(
       "VNDK_USING_CORE_VARIANT_LIBRARIES", "vndk_using_core_variant_libraries");
   android::linkerconfig::modules::Variables::AddValue("STUB_LIBRARIES",
diff --git a/contents/tests/configuration/vndk_test.cc b/contents/tests/configuration/vndk_test.cc
index c793e98..a7b9ee9 100644
--- a/contents/tests/configuration/vndk_test.cc
+++ b/contents/tests/configuration/vndk_test.cc
@@ -60,7 +60,7 @@
   auto vendor_vndk_lib_path = "/vendor/${LIB}/vndk";
   auto vendor_vndksp_lib_path = "/vendor/${LIB}/vndk-sp";
   auto apex_vndk_lib_path =
-      "/apex/com.android.vndk.v@{VENDOR_VNDK_VERSION}/${LIB}";
+      "/apex/com.android.vndk.v" + Var("VENDOR_VNDK_VERSION") + "/${LIB}";
 
   auto fs = fsmap{
       {system_lib_path, {libvndk, libvndksp}},
diff --git a/modules/configuration.cc b/modules/configuration.cc
index 555ca99..fdb0d16 100644
--- a/modules/configuration.cc
+++ b/modules/configuration.cc
@@ -27,19 +27,15 @@
 void Configuration::WriteConfig(ConfigWriter& writer) {
   std::unordered_map<std::string, std::string> resolved_dirs;
 
-  for (auto& dir_to_section : dir_to_section_list_) {
-    auto resolved_dir = Variables::ResolveVariables(dir_to_section.first);
-
-    auto it = resolved_dirs.find(resolved_dir);
-
+  for (const auto& [dir, section] : dir_to_section_list_) {
+    auto it = resolved_dirs.find(dir);
     if (it != resolved_dirs.end()) {
-      LOG(WARNING) << "Binary path " << resolved_dir << " already found from "
-                   << it->second << ". Path from " << dir_to_section.second
+      LOG(WARNING) << "Binary path " << dir << " already found from "
+                   << it->second << ". Path from " << section
                    << " will be ignored.";
     } else {
-      resolved_dirs[resolved_dir] = dir_to_section.second;
-      writer.WriteLine(
-          "dir.%s = %s", dir_to_section.second.c_str(), resolved_dir.c_str());
+      resolved_dirs[dir] = section;
+      writer.WriteLine("dir." + section + " = " + dir);
     }
   }
 
diff --git a/modules/configwriter.cc b/modules/configwriter.cc
index 0b2db69..153f870 100644
--- a/modules/configwriter.cc
+++ b/modules/configwriter.cc
@@ -16,9 +16,6 @@
 
 #include "linkerconfig/configwriter.h"
 
-#include <cstdarg>
-#include <cstdio>
-
 #include "linkerconfig/log.h"
 #include "linkerconfig/variables.h"
 
@@ -26,45 +23,17 @@
 namespace linkerconfig {
 namespace modules {
 
-void ConfigWriter::SetPrefix(const std::string& prefix) {
-  prefix_ = prefix;
-}
-
-void ConfigWriter::ResetPrefix() {
-  prefix_ = "";
-}
-
-void ConfigWriter::WriteLine(const char* format, ...) {
-  va_list args_for_length, args;
-
-  va_start(args, format);
-  va_copy(args_for_length, args);
-
-  int length = vsnprintf(nullptr, 0, format, args_for_length);
-  va_end(args_for_length);
-
-  if (length < 0) {
-    LOG(ERROR) << "Failed to get length of the string with format " << format;
-    va_end(args);
-    return;
+void ConfigWriter::WriteVars(const std::string& var,
+                             const std::vector<std::string>& values) {
+  bool is_first = true;
+  for (const auto& value : values) {
+    WriteLine(var + (is_first ? " = " : " += ") + value);
+    is_first = false;
   }
-
-  std::unique_ptr<char[]> formatted_string(new char[length + 1]);
-
-  int res = vsnprintf(formatted_string.get(), length + 1, format, args);
-  va_end(args);
-
-  if (res < 0) {
-    LOG(ERROR) << "Failed to write a string with format " << format;
-    return;
-  }
-
-  WriteLine(std::string(formatted_string.get()));
 }
 
 void ConfigWriter::WriteLine(const std::string& line) {
-  auto resolved_line = Variables::ResolveVariables(prefix_ + line);
-  content_ << resolved_line << std::endl;
+  content_ << line << '\n';
 }
 
 std::string ConfigWriter::ToString() {
diff --git a/modules/include/linkerconfig/configwriter.h b/modules/include/linkerconfig/configwriter.h
index 5f93e5f..a3016f7 100644
--- a/modules/include/linkerconfig/configwriter.h
+++ b/modules/include/linkerconfig/configwriter.h
@@ -17,6 +17,7 @@
 
 #include <sstream>
 #include <string>
+#include <vector>
 
 namespace android {
 namespace linkerconfig {
@@ -24,15 +25,12 @@
 
 class ConfigWriter {
  public:
-  void SetPrefix(const std::string& prefix);
-  void ResetPrefix();
+  void WriteVars(const std::string& var, const std::vector<std::string>& values);
   void WriteLine(const std::string& line);
-  void WriteLine(const char* format, ...);
   std::string ToString();
 
  private:
   std::stringstream content_;
-  std::string prefix_;
 };
 
 }  // namespace modules
diff --git a/modules/include/linkerconfig/link.h b/modules/include/linkerconfig/link.h
index 6ea9505..8fa67e1 100644
--- a/modules/include/linkerconfig/link.h
+++ b/modules/include/linkerconfig/link.h
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "linkerconfig/configwriter.h"
-#include "linkerconfig/log.h"
 
 namespace android {
 namespace linkerconfig {
diff --git a/modules/include/linkerconfig/namespace.h b/modules/include/linkerconfig/namespace.h
index 044fe62..eb8342b 100644
--- a/modules/include/linkerconfig/namespace.h
+++ b/modules/include/linkerconfig/namespace.h
@@ -22,7 +22,6 @@
 #include "linkerconfig/apex.h"
 #include "linkerconfig/configwriter.h"
 #include "linkerconfig/link.h"
-#include "linkerconfig/log.h"
 
 namespace android {
 namespace linkerconfig {
diff --git a/modules/include/linkerconfig/variables.h b/modules/include/linkerconfig/variables.h
index debd7e8..cf566e4 100644
--- a/modules/include/linkerconfig/variables.h
+++ b/modules/include/linkerconfig/variables.h
@@ -22,15 +22,16 @@
 namespace android {
 namespace linkerconfig {
 namespace modules {
+
 class Variables {
  public:
   static std::optional<std::string> GetValue(const std::string& key);
   static void AddValue(const std::string& key, const std::string& value);
-  static std::string ResolveVariables(const std::string& str);
 
  private:
   static std::map<std::string, std::string> variables_;
 };
+
 }  // namespace modules
 }  // namespace linkerconfig
 }  // namespace android
\ No newline at end of file
diff --git a/modules/link.cc b/modules/link.cc
index 4c0e2e9..9bf4c10 100644
--- a/modules/link.cc
+++ b/modules/link.cc
@@ -16,6 +16,8 @@
 
 #include "linkerconfig/link.h"
 
+#include "linkerconfig/log.h"
+
 namespace android {
 namespace linkerconfig {
 namespace modules {
@@ -34,23 +36,16 @@
 }
 
 void Link::WriteConfig(ConfigWriter& writer) const {
-  writer.SetPrefix("namespace." + origin_namespace_ + ".link." +
-                   target_namespace_);
+  const auto prefix =
+      "namespace." + origin_namespace_ + ".link." + target_namespace_ + ".";
   if (allow_all_shared_libs_) {
-    writer.WriteLine(".allow_all_shared_libs = true");
+    writer.WriteLine(prefix + "allow_all_shared_libs = true");
   } else if (!shared_libs_.empty()) {
-    bool is_first = true;
-
-    for (auto& lib_name : shared_libs_) {
-      writer.WriteLine(
-          ".shared_libs %s %s", is_first ? "=" : "+=", lib_name.c_str());
-      is_first = false;
-    }
+    writer.WriteVars(prefix + "shared_libs", shared_libs_);
   } else {
     LOG(WARNING) << "Ignored empty shared libs link from " << origin_namespace_
                  << " to " << target_namespace_;
   }
-  writer.ResetPrefix();
 }
 
 }  // namespace modules
diff --git a/modules/namespace.cc b/modules/namespace.cc
index 81f84b9..b553c46 100644
--- a/modules/namespace.cc
+++ b/modules/namespace.cc
@@ -50,17 +50,6 @@
   ns.AddRequires(apex_info.require_libs);
 }
 
-void Namespace::WritePathString(ConfigWriter& writer,
-                                const std::string& path_type,
-                                const std::vector<std::string>& path_list) {
-  std::string prefix = path_type + ".paths ";
-  bool is_first = true;
-  for (auto& path : path_list) {
-    writer.WriteLine(prefix + (is_first ? "= " : "+= ") + path);
-    is_first = false;
-  }
-}
-
 Link& Namespace::GetLink(const std::string& target_namespace) {
   for (auto& link : links_) {
     if (link.To() == target_namespace) {
@@ -71,25 +60,19 @@
 }
 
 void Namespace::WriteConfig(ConfigWriter& writer) {
-  writer.SetPrefix("namespace." + name_ + ".");
+  const auto prefix = "namespace." + name_ + ".";
 
-  writer.WriteLine("isolated = %s", is_isolated_ ? "true" : "false");
+  writer.WriteLine(prefix + "isolated = " + (is_isolated_ ? "true" : "false"));
 
   if (is_visible_) {
-    writer.WriteLine("visible = true");
+    writer.WriteLine(prefix + "visible = true");
   }
 
-  WritePathString(writer, "search", search_paths_);
-  WritePathString(writer, "permitted", permitted_paths_);
-  WritePathString(writer, "asan.search", asan_search_paths_);
-  WritePathString(writer, "asan.permitted", asan_permitted_paths_);
-
-  bool is_first = true;
-  for (const auto& whitelisted : whitelisted_) {
-    writer.WriteLine(
-        "whitelisted %s %s", is_first ? "=" : "+=", whitelisted.c_str());
-    is_first = false;
-  }
+  writer.WriteVars(prefix + "search.paths", search_paths_);
+  writer.WriteVars(prefix + "permitted.paths", permitted_paths_);
+  writer.WriteVars(prefix + "asan.search.paths", asan_search_paths_);
+  writer.WriteVars(prefix + "asan.permitted.paths", asan_permitted_paths_);
+  writer.WriteVars(prefix + "whitelisted", whitelisted_);
 
   if (!links_.empty()) {
     std::vector<std::string> link_list;
@@ -97,14 +80,12 @@
     for (const auto& link : links_) {
       link_list.push_back(link.To());
     }
-    writer.WriteLine("links = " + android::base::Join(link_list, ","));
+    writer.WriteLine(prefix + "links = " + android::base::Join(link_list, ","));
 
     for (const auto& link : links_) {
       link.WriteConfig(writer);
     }
   }
-
-  writer.ResetPrefix();
 }
 
 void Namespace::AddSearchPath(const std::string& path, AsanPath path_from_asan) {
diff --git a/modules/section.cc b/modules/section.cc
index c665d56..325a3d3 100644
--- a/modules/section.cc
+++ b/modules/section.cc
@@ -33,7 +33,7 @@
 namespace linkerconfig {
 namespace modules {
 void Section::WriteConfig(ConfigWriter& writer) {
-  writer.WriteLine("[%s]", name_.c_str());
+  writer.WriteLine("[" + name_ + "]");
 
   std::sort(namespaces_.begin(),
             namespaces_.end(),
diff --git a/modules/tests/configuration_test.cc b/modules/tests/configuration_test.cc
index abd2505..79f1b02 100644
--- a/modules/tests/configuration_test.cc
+++ b/modules/tests/configuration_test.cc
@@ -94,18 +94,17 @@
 )";
 
 TEST(linkerconfig_configuration, generate_configuration) {
-  Variables::AddValue("PRODUCT", "product");
   std::vector<Section> sections;
 
   std::vector<DirToSection> dir_to_sections = {
       {"/system/bin", "system"},
       {"/system/xbin", "system"},
-      {"/@{PRODUCT}/bin", "system"},
+      {"/product/bin", "system"},
       {"/odm/bin", "vendor"},
       {"/vendor/bin", "vendor"},
       {"/system/bin/vendor", "vendor"},
       {"/product/bin/vendor", "vendor"},
-      {"/product/bin", "vendor"},
+      {"/product/bin", "vendor"},  // note that this is ignored
   };
 
   std::vector<Namespace> system_namespaces;
diff --git a/modules/tests/configwriter_test.cc b/modules/tests/configwriter_test.cc
index 4c5c9fa..1abaaee 100644
--- a/modules/tests/configwriter_test.cc
+++ b/modules/tests/configwriter_test.cc
@@ -21,18 +21,6 @@
 
 constexpr const char* kSampleContent = "Lorem ipsum dolor sit amet";
 
-constexpr const char* kExpectedResultWithPrefix =
-    R"(Lorem ipsum dolor sit amet
-SAMPLE.TEST.Text : Lorem ipsum dolor sit amet
-end of context
-)";
-
-constexpr const char* kExpectedResultWithVariables =
-    R"(/Value/Test
-/Invalid_Value/Path
-//Path2
-)";
-
 TEST(linkerconfig_configwriter, write_line) {
   android::linkerconfig::modules::ConfigWriter writer;
 
@@ -41,33 +29,16 @@
   ASSERT_EQ(writer.ToString(), "Lorem ipsum dolor sit amet\n");
 }
 
-TEST(linkerconfig_configwriter, write_with_format) {
-  android::linkerconfig::modules::ConfigWriter writer;
-  writer.WriteLine("Sample text(%d) : %s", 10, kSampleContent);
-  ASSERT_EQ(writer.ToString(),
-            "Sample text(10) : Lorem ipsum dolor sit amet\n");
-}
-
-TEST(linkerconfig_configwriter, write_with_prefix) {
-  android::linkerconfig::modules::ConfigWriter writer;
-  writer.WriteLine(kSampleContent);
-  writer.SetPrefix("SAMPLE.TEST.");
-  writer.WriteLine("Text : %s", kSampleContent);
-  writer.ResetPrefix();
-  writer.WriteLine("end of context");
-
-  ASSERT_EQ(writer.ToString(), kExpectedResultWithPrefix);
-}
-
-TEST(linkerconfig_configwriter, replace_variable) {
+TEST(linkerconfig_configwriter, WriteVars) {
   android::linkerconfig::modules::ConfigWriter writer;
 
-  android::linkerconfig::modules::Variables::AddValue("Test_Prop_Q", "Value");
-  android::linkerconfig::modules::Variables::AddValue("VNDK_VER", "Q");
+  writer.WriteVars("var1", {"value1", "value2"});
+  writer.WriteVars("var2", {"value1"});
+  writer.WriteVars("var3", {});
 
-  writer.WriteLine("/@{Test_Prop_@{VNDK_VER}}/Test");
-  writer.WriteLine("/@{Invalid_Key:Invalid_Value}/Path");
-  writer.WriteLine("/@{Invalid_Key:}/Path2");
-
-  ASSERT_EQ(writer.ToString(), kExpectedResultWithVariables);
+  ASSERT_EQ(
+      "var1 = value1\n"
+      "var1 += value2\n"
+      "var2 = value1\n",
+      writer.ToString());
 }
\ No newline at end of file
diff --git a/modules/variables.cc b/modules/variables.cc
index 051aaba..99539f0 100644
--- a/modules/variables.cc
+++ b/modules/variables.cc
@@ -17,15 +17,6 @@
 #include "linkerconfig/variables.h"
 
 #include <android-base/properties.h>
-#include <regex>
-#include <sstream>
-
-#include "linkerconfig/log.h"
-
-namespace {
-constexpr const char* kVariableRegex =
-    "@\\{([^@\\{\\}:]+)(:([^@\\{\\}:]*))?\\}";
-}
 
 namespace android {
 namespace linkerconfig {
@@ -53,33 +44,6 @@
 void Variables::AddValue(const std::string& key, const std::string& value) {
   variables_[key] = value;
 }
-
-std::string Variables::ResolveVariables(const std::string& str) {
-  std::string result = str;
-  std::regex variable_regex(kVariableRegex);
-  std::smatch sm;
-
-  while (std::regex_search(result, sm, variable_regex)) {
-    std::stringstream ss;
-    ss << sm.prefix();
-    auto resolved_value = GetValue(sm[1]);
-    if (resolved_value.has_value()) {
-      ss << resolved_value.value();
-    } else {
-      LOG(WARNING) << "Unable to find value for " << sm[1];
-      bool contains_default = sm[2].length() > 0;
-      if (contains_default) {
-        ss << sm[3];
-      } else {
-        LOG(FATAL) << "There is no default value defined for " << sm[1];
-      }
-    }
-    ss << ResolveVariables(sm.suffix());
-    result = ss.str();
-  }
-
-  return result;
-}
 }  // namespace modules
 }  // namespace linkerconfig
 }  // namespace android
\ No newline at end of file