Merge "snapuserd: Move GetNumSectors call to snapuserd_server." into main
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index ca59ef3..0c8760c 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -72,9 +72,6 @@
     ],
     init_rc: ["bootstat.rc"],
     product_variables: {
-        pdk: {
-            enabled: false,
-        },
         debuggable: {
             init_rc: ["bootstat-debug.rc"],
         },
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 4c45828..4b3a5d3 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -128,12 +128,11 @@
 }
 
 static android::sp<android::os::IVold> GetVold() {
+    auto sm = android::defaultServiceManager();
     while (true) {
-        if (auto sm = android::defaultServiceManager()) {
-            if (auto binder = sm->getService(android::String16("vold"))) {
-                if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
-                    return vold;
-                }
+        if (auto binder = sm->checkService(android::String16("vold"))) {
+            if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
+                return vold;
             }
         }
         std::this_thread::sleep_for(2s);
diff --git a/fs_mgr/libfstab/boot_config.cpp b/fs_mgr/libfstab/boot_config.cpp
index 8fb28c6..b21495e 100644
--- a/fs_mgr/libfstab/boot_config.cpp
+++ b/fs_mgr/libfstab/boot_config.cpp
@@ -17,20 +17,101 @@
 #include <algorithm>
 #include <iterator>
 #include <string>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #include "fstab_priv.h"
 #include "logging_macros.h"
 
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline) {
+namespace android {
+namespace fs_mgr {
+
+const std::string& GetAndroidDtDir() {
+    // Set once and saves time for subsequent calls to this function
+    static const std::string kAndroidDtDir = [] {
+        std::string android_dt_dir;
+        if ((GetBootconfig("androidboot.android_dt_dir", &android_dt_dir) ||
+             GetKernelCmdline("androidboot.android_dt_dir", &android_dt_dir)) &&
+            !android_dt_dir.empty()) {
+            // Ensure the returned path ends with a /
+            if (android_dt_dir.back() != '/') {
+                android_dt_dir.push_back('/');
+            }
+        } else {
+            // Fall back to the standard procfs-based path
+            android_dt_dir = "/proc/device-tree/firmware/android/";
+        }
+        LINFO << "Using Android DT directory " << android_dt_dir;
+        return android_dt_dir;
+    }();
+    return kAndroidDtDir;
+}
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+                                const std::function<void(std::string, std::string)>& fn) {
+    for (std::string_view line : android::base::Split(bootconfig, "\n")) {
+        const auto equal_pos = line.find('=');
+        std::string key = android::base::Trim(line.substr(0, equal_pos));
+        if (key.empty()) {
+            continue;
+        }
+        std::string value;
+        if (equal_pos != line.npos) {
+            value = android::base::Trim(line.substr(equal_pos + 1));
+            // If the value is a comma-delimited list, the kernel would insert a space between the
+            // list elements when read from /proc/bootconfig.
+            // BoardConfig.mk:
+            //      BOARD_BOOTCONFIG := key=value1,value2,value3
+            // /proc/bootconfig:
+            //      key = "value1", "value2", "value3"
+            if (key == "androidboot.boot_device" || key == "androidboot.boot_devices") {
+                // boot_device[s] is a special case where a list element can contain comma and the
+                // caller expects a space-delimited list, so don't remove space here.
+                value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+            } else {
+                // In order to not break the expectations of existing code, we modify the value to
+                // keep the format consistent with the kernel cmdline by removing quote and space.
+                std::string_view sv(value);
+                android::base::ConsumePrefix(&sv, "\"");
+                android::base::ConsumeSuffix(&sv, "\"");
+                value = android::base::StringReplace(sv, R"(", ")", ",", true);
+            }
+        }
+        // "key" and "key =" means empty value.
+        fn(std::move(key), std::move(value));
+    }
+}
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+                             std::string* out) {
+    bool found = false;
+    ImportBootconfigFromString(bootconfig, [&](std::string config_key, std::string value) {
+        if (!found && config_key == key) {
+            *out = std::move(value);
+            found = true;
+        }
+    });
+    return found;
+}
+
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn) {
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+    ImportBootconfigFromString(bootconfig, fn);
+}
+
+bool GetBootconfig(const std::string& key, std::string* out) {
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+    return GetBootconfigFromString(bootconfig, key, out);
+}
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+                                   const std::function<void(std::string, std::string)>& fn) {
     static constexpr char quote = '"';
 
-    std::vector<std::pair<std::string, std::string>> result;
     size_t base = 0;
     while (true) {
         // skip quoted spans
@@ -41,111 +122,60 @@
             if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
             ++found;
         }
-        std::string piece;
-        auto source = cmdline.substr(base, found - base);
-        std::remove_copy(source.begin(), source.end(),
-                         std::back_insert_iterator<std::string>(piece), quote);
+        std::string piece = cmdline.substr(base, found - base);
+        piece.erase(std::remove(piece.begin(), piece.end(), quote), piece.end());
         auto equal_sign = piece.find('=');
         if (equal_sign == piece.npos) {
             if (!piece.empty()) {
                 // no difference between <key> and <key>=
-                result.emplace_back(std::move(piece), "");
+                fn(std::move(piece), "");
             }
         } else {
-            result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
+            std::string value = piece.substr(equal_sign + 1);
+            piece.resize(equal_sign);
+            fn(std::move(piece), std::move(value));
         }
         if (found == cmdline.npos) break;
         base = found + 1;
     }
-
-    return result;
 }
 
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
-        const std::string& cmdline) {
-    static constexpr char quote = '"';
-
-    std::vector<std::pair<std::string, std::string>> result;
-    for (auto& line : android::base::Split(cmdline, "\n")) {
-        line.erase(std::remove(line.begin(), line.end(), quote), line.end());
-        auto equal_sign = line.find('=');
-        if (equal_sign == line.npos) {
-            if (!line.empty()) {
-                // no difference between <key> and <key>=
-                result.emplace_back(std::move(line), "");
-            }
-        } else {
-            result.emplace_back(android::base::Trim(line.substr(0, equal_sign)),
-                                android::base::Trim(line.substr(equal_sign + 1)));
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+                                std::string* out) {
+    bool found = false;
+    ImportKernelCmdlineFromString(cmdline, [&](std::string config_key, std::string value) {
+        if (!found && config_key == key) {
+            *out = std::move(value);
+            found = true;
         }
-    }
-
-    return result;
+    });
+    return found;
 }
 
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
-                                            const std::string& android_key, std::string* out_val) {
-    FSTAB_CHECK(out_val != nullptr);
-
-    const std::string bootconfig_key("androidboot." + android_key);
-    for (const auto& [key, value] : fs_mgr_parse_proc_bootconfig(bootconfig)) {
-        if (key == bootconfig_key) {
-            *out_val = value;
-            return true;
-        }
-    }
-
-    *out_val = "";
-    return false;
-}
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
-                                        std::string* out_val) {
-    FSTAB_CHECK(out_val != nullptr);
-
-    const std::string cmdline_key("androidboot." + android_key);
-    for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
-        if (key == cmdline_key) {
-            *out_val = value;
-            return true;
-        }
-    }
-
-    *out_val = "";
-    return false;
-}
-
-// Tries to get the given boot config value from bootconfig.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val) {
-    std::string bootconfig;
-    if (!android::base::ReadFileToString("/proc/bootconfig", &bootconfig)) return false;
-    if (!bootconfig.empty() && bootconfig.back() == '\n') {
-        bootconfig.pop_back();
-    }
-    return fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, out_val);
-}
-
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn) {
     std::string cmdline;
-    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
-    if (!cmdline.empty() && cmdline.back() == '\n') {
-        cmdline.pop_back();
-    }
-    return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    ImportKernelCmdlineFromString(android::base::Trim(cmdline), fn);
 }
 
-// Tries to get the boot config value in device tree, properties and
-// kernel cmdline (in that order).  Returns 'true' if successfully
-// found, 'false' otherwise.
+bool GetKernelCmdline(const std::string& key, std::string* out) {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    return GetKernelCmdlineFromString(android::base::Trim(cmdline), key, out);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
+
+// Tries to get the boot config value in device tree, properties, kernel bootconfig and kernel
+// cmdline (in that order).
+// Returns 'true' if successfully found, 'false' otherwise.
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
     FSTAB_CHECK(out_val != nullptr);
 
     // firstly, check the device tree
     if (is_dt_compatible()) {
-        std::string file_name = get_android_dt_dir() + "/" + key;
+        std::string file_name = android::fs_mgr::GetAndroidDtDir() + key;
         if (android::base::ReadFileToString(file_name, out_val)) {
             if (!out_val->empty()) {
                 out_val->pop_back();  // Trims the trailing '\0' out.
@@ -161,12 +191,13 @@
     }
 
     // next, check if we have the property in bootconfig
-    if (fs_mgr_get_boot_config_from_bootconfig_source(key, out_val)) {
+    const std::string config_key = "androidboot." + key;
+    if (android::fs_mgr::GetBootconfig(config_key, out_val)) {
         return true;
     }
 
     // finally, fallback to kernel cmdline, properties may not be ready yet
-    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+    if (android::fs_mgr::GetKernelCmdline(config_key, out_val)) {
         return true;
     }
 
diff --git a/fs_mgr/libfstab/fstab.cpp b/fs_mgr/libfstab/fstab.cpp
index 5b5c3d2..32460b1 100644
--- a/fs_mgr/libfstab/fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -51,7 +51,6 @@
 namespace fs_mgr {
 namespace {
 
-constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
 constexpr char kProcMountsPath[] = "/proc/mounts";
 
 struct FlagList {
@@ -337,25 +336,14 @@
     return true;
 }
 
-std::string InitAndroidDtDir() {
-    std::string android_dt_dir;
-    // The platform may specify a custom Android DT path in kernel cmdline
-    if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
-        !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
-        // Fall back to the standard procfs-based path
-        android_dt_dir = kDefaultAndroidDtDir;
-    }
-    return android_dt_dir;
-}
-
 bool IsDtFstabCompatible() {
     std::string dt_value;
-    std::string file_name = get_android_dt_dir() + "/fstab/compatible";
+    std::string file_name = GetAndroidDtDir() + "fstab/compatible";
 
     if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
         // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
         std::string status_value;
-        std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+        std::string status_file_name = GetAndroidDtDir() + "fstab/status";
         return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
                status_value == "okay";
     }
@@ -368,7 +356,7 @@
         return {};
     }
 
-    std::string fstabdir_name = get_android_dt_dir() + "/fstab";
+    std::string fstabdir_name = GetAndroidDtDir() + "fstab";
     std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
     if (!fstabdir) return {};
 
@@ -843,9 +831,8 @@
     Fstab default_fstab;
     const std::string default_fstab_path = GetFstabPath();
     if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
-        for (auto&& entry : default_fstab) {
-            fstab->emplace_back(std::move(entry));
-        }
+        fstab->insert(fstab->end(), std::make_move_iterator(default_fstab.begin()),
+                      std::make_move_iterator(default_fstab.end()));
     } else {
         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
     }
@@ -875,40 +862,33 @@
 }
 
 std::set<std::string> GetBootDevices() {
+    std::set<std::string> boot_devices;
     // First check bootconfig, then kernel commandline, then the device tree
-    std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
     std::string value;
-    if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
-        fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
-        std::set<std::string> boot_devices;
-        // remove quotes and split by spaces
-        auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
-        for (std::string_view device : boot_device_strings) {
-            // trim the trailing comma, keep the rest.
+    if (GetBootconfig("androidboot.boot_devices", &value) ||
+        GetBootconfig("androidboot.boot_device", &value)) {
+        // split by spaces and trim the trailing comma.
+        for (std::string_view device : android::base::Split(value, " ")) {
             base::ConsumeSuffix(&device, ",");
             boot_devices.emplace(device);
         }
         return boot_devices;
     }
 
-    if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
-        ReadDtFile(dt_file_name, &value)) {
-        auto boot_devices = Split(value, ",");
-        return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+    const std::string dt_file_name = GetAndroidDtDir() + "boot_devices";
+    if (GetKernelCmdline("androidboot.boot_devices", &value) || ReadDtFile(dt_file_name, &value)) {
+        auto boot_devices_list = Split(value, ",");
+        return {std::make_move_iterator(boot_devices_list.begin()),
+                std::make_move_iterator(boot_devices_list.end())};
     }
 
-    std::string cmdline;
-    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        std::set<std::string> boot_devices;
-        const std::string cmdline_key = "androidboot.boot_device";
-        for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
-            if (key == cmdline_key) {
-                boot_devices.emplace(value);
-            }
+    ImportKernelCmdline([&](std::string key, std::string value) {
+        if (key == "androidboot.boot_device") {
+            boot_devices.emplace(std::move(value));
         }
-        if (!boot_devices.empty()) {
-            return boot_devices;
-        }
+    });
+    if (!boot_devices.empty()) {
+        return boot_devices;
     }
 
     // Fallback to extract boot devices from fstab.
@@ -948,15 +928,8 @@
 }  // namespace fs_mgr
 }  // namespace android
 
-// FIXME: The same logic is duplicated in system/core/init/
-const std::string& get_android_dt_dir() {
-    // Set once and saves time for subsequent calls to this function
-    static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
-    return kAndroidDtDir;
-}
-
 bool is_dt_compatible() {
-    std::string file_name = get_android_dt_dir() + "/compatible";
+    std::string file_name = android::fs_mgr::GetAndroidDtDir() + "compatible";
     std::string dt_value;
     if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
         if (dt_value == "android,firmware") {
diff --git a/fs_mgr/libfstab/fstab_priv.h b/fs_mgr/libfstab/fstab_priv.h
index fb12b9f..5105da0 100644
--- a/fs_mgr/libfstab/fstab_priv.h
+++ b/fs_mgr/libfstab/fstab_priv.h
@@ -16,27 +16,16 @@
 
 #pragma once
 
+#include <functional>
 #include <string>
-#include <utility>
-#include <vector>
 
 #include <fstab/fstab.h>
 
 // Do not include logging_macros.h here as this header is used by fs_mgr, too.
 
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline);
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
-                                        std::string* out_val);
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
-        const std::string& bootconfig);
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig, const std::string& key,
-                                            std::string* out_val);
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val);
 
 bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
-const std::string& get_android_dt_dir();
 bool is_dt_compatible();
 
 namespace android {
@@ -47,5 +36,17 @@
 bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
 std::string GetFstabPath();
 
+void ImportBootconfigFromString(const std::string& bootconfig,
+                                const std::function<void(std::string, std::string)>& fn);
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+                             std::string* out);
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+                                   const std::function<void(std::string, std::string)>& fn);
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+                                std::string* out);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
index e0683ac..150a47d 100644
--- a/fs_mgr/libfstab/include/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <functional>
 #include <set>
 #include <string>
 #include <vector>
@@ -124,5 +125,25 @@
 // expected name.
 std::string GetVerityDeviceName(const FstabEntry& entry);
 
+// Returns the Android Device Tree directory as specified in the kernel bootconfig or cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& GetAndroidDtDir();
+
+// Import the kernel bootconfig by calling the callback |fn| with each key-value pair.
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel bootconfig value for |key|.
+// Returns true if |key| is found in bootconfig.
+// Otherwise returns false and |*out| is not modified.
+bool GetBootconfig(const std::string& key, std::string* out);
+
+// Import the kernel cmdline by calling the callback |fn| with each key-value pair.
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel cmdline value for |key|.
+// Returns true if |key| is found in the kernel cmdline.
+// Otherwise returns false and |*out| is not modified.
+bool GetKernelCmdline(const std::string& key, std::string* out);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
index 37f28e1..5349e41 100644
--- a/fs_mgr/liblp/super_layout_builder.cpp
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -46,21 +46,21 @@
 bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
     for (const auto& partition : metadata.partitions) {
         if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
-            // Retrofit devices are not supported.
+            LOG(ERROR) << "Retrofit devices are not supported";
             return false;
         }
         if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
-            // Writable partitions are not supported.
+            LOG(ERROR) << "Writable partitions are not supported";
             return false;
         }
     }
     if (!metadata.extents.empty()) {
-        // Partitions that already have extents are not supported (should
-        // never be true of super_empty.img).
+        LOG(ERROR) << "Partitions that already have extents are not supported";
+        // should never be true of super_empty.img.
         return false;
     }
     if (metadata.block_devices.size() != 1) {
-        // Only one "super" is supported.
+        LOG(ERROR) << "Only one 'super' is supported";
         return false;
     }
 
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index b7f792f..2aeba0a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -38,6 +38,8 @@
     ],
     static_libs: [
         "libfs_mgr",
+        "libgmock",
+        "libgtest",
     ],
     srcs: [
         "file_wait_test.cpp",
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index c51df2a..322bf1b 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -29,11 +29,13 @@
 #include <android-base/strings.h>
 #include <fs_mgr.h>
 #include <fstab/fstab.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "../fs_mgr_priv.h"
 
 using namespace android::fs_mgr;
+using namespace testing;
 
 namespace {
 
@@ -119,37 +121,42 @@
         {"terminator", "truncated"},
 };
 
-const std::string bootconfig =
-        "androidboot.bootdevice = \"1d84000.ufshc\"\n"
-        "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
-        "androidboot.baseband = \"sdy\"\n"
-        "androidboot.keymaster = \"1\"\n"
-        "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
-        "androidboot.slot_suffix = \"_a\"\n"
-        "androidboot.hardware.platform = \"sdw813\"\n"
-        "androidboot.hardware = \"foo\"\n"
-        "androidboot.revision = \"EVT1.0\"\n"
-        "androidboot.bootloader = \"burp-0.1-7521\"\n"
-        "androidboot.hardware.sku = \"mary\"\n"
-        "androidboot.hardware.radio.subtype = \"0\"\n"
-        "androidboot.dtbo_idx = \"2\"\n"
-        "androidboot.mode = \"normal\"\n"
-        "androidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\n"
-        "androidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\n"
-        "androidboot.hardware.ufs = \"2GB,combushi\"\n"
-        "androidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\n"
-        "androidboot.ramdump = \"disabled\"\n"
-        "androidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\n"
-        "androidboot.vbmeta.avb_version = \"1.1\"\n"
-        "androidboot.vbmeta.device_state = \"unlocked\"\n"
-        "androidboot.vbmeta.hash_alg = \"sha256\"\n"
-        "androidboot.vbmeta.size = \"5248\"\n"
-        "androidboot.vbmeta.digest = \""
-        "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\n"
-        "androidboot.vbmeta.invalidate_on_error = \"yes\"\n"
-        "androidboot.veritymode = \"enforcing\"\n"
-        "androidboot.verifiedbootstate = \"orange\"\n"
-        "androidboot.space = \"sha256 5248 androidboot.nospace = nope\"\n";
+const std::string bootconfig = R"(
+androidboot.bootdevice = "1d84000.ufshc"
+androidboot.boot_devices = "dev1", "dev2,withcomma", "dev3"
+androidboot.baseband = "sdy"
+androidboot.keymaster = "1"
+androidboot.serialno = "BLAHBLAHBLAH"
+androidboot.slot_suffix = "_a"
+androidboot.hardware.platform = "sdw813"
+androidboot.hardware = "foo"
+androidboot.revision = "EVT1.0"
+androidboot.bootloader = "burp-0.1-7521"
+androidboot.hardware.sku = "mary"
+androidboot.hardware.radio.subtype = "0"
+androidboot.dtbo_idx = "2"
+androidboot.mode = "normal"
+androidboot.hardware.ddr = "1GB,combuchi,LPDDR4X"
+androidboot.ddr_info = "combuchiandroidboot.ddr_size=2GB"
+androidboot.hardware.ufs = "2GB,combushi"
+androidboot.boottime = "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"
+androidboot.ramdump = "disabled"
+androidboot.vbmeta.device = "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"
+androidboot.vbmeta.avb_version = "1.1"
+androidboot.vbmeta.device_state = "unlocked"
+androidboot.vbmeta.hash_alg = "sha256"
+androidboot.vbmeta.size = "5248"
+androidboot.vbmeta.digest = "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"
+androidboot.vbmeta.invalidate_on_error = "yes"
+androidboot.veritymode = "enforcing"
+androidboot.verifiedbootstate = "orange"
+androidboot.space = "sha256 5248 androidboot.nospace = nope"
+just.key
+key.empty.value =
+dessert.value = "ice, cream"
+dessert.list = "ice", "cream"
+ambiguous.list = ", ", ", "
+)";
 
 const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
         {"androidboot.bootdevice", "1d84000.ufshc"},
@@ -182,6 +189,11 @@
         {"androidboot.veritymode", "enforcing"},
         {"androidboot.verifiedbootstate", "orange"},
         {"androidboot.space", "sha256 5248 androidboot.nospace = nope"},
+        {"just.key", ""},
+        {"key.empty.value", ""},
+        {"dessert.value", "ice, cream"},
+        {"dessert.list", "ice,cream"},
+        {"ambiguous.list", ", ,, "},
 };
 
 bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
@@ -212,44 +224,61 @@
 
 }  // namespace
 
-TEST(fs_mgr, fs_mgr_parse_cmdline) {
-    EXPECT_EQ(result_space, fs_mgr_parse_cmdline(cmdline));
+TEST(fs_mgr, ImportKernelCmdline) {
+    std::vector<std::pair<std::string, std::string>> result;
+    ImportKernelCmdlineFromString(
+            cmdline, [&](std::string key, std::string value) { result.emplace_back(key, value); });
+    EXPECT_THAT(result, ContainerEq(result_space));
 }
 
-TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+TEST(fs_mgr, GetKernelCmdline) {
     std::string content;
-    for (const auto& entry : result_space) {
-        static constexpr char androidboot[] = "androidboot.";
-        if (!android::base::StartsWith(entry.first, androidboot)) continue;
-        auto key = entry.first.substr(strlen(androidboot));
-        EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
-        EXPECT_EQ(entry.second, content);
-    }
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
-    EXPECT_TRUE(content.empty()) << content;
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
-    EXPECT_TRUE(content.empty()) << content;
-}
-
-TEST(fs_mgr, fs_mgr_parse_bootconfig) {
-    EXPECT_EQ(bootconfig_result_space, fs_mgr_parse_proc_bootconfig(bootconfig));
-}
-
-TEST(fs_mgr, fs_mgr_get_boot_config_from_bootconfig) {
-    std::string content;
-    for (const auto& entry : bootconfig_result_space) {
-        static constexpr char androidboot[] = "androidboot.";
-        if (!android::base::StartsWith(entry.first, androidboot)) continue;
-        auto key = entry.first.substr(strlen(androidboot));
-        EXPECT_TRUE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, &content))
-                << " for " << key;
-        EXPECT_EQ(entry.second, content);
+    for (const auto& [key, value] : result_space) {
+        EXPECT_TRUE(GetKernelCmdlineFromString(cmdline, key, &content)) << " for " << key;
+        EXPECT_EQ(content, value);
     }
 
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "vbmeta.avb_versio", &content));
-    EXPECT_TRUE(content.empty()) << content;
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "nospace", &content));
-    EXPECT_TRUE(content.empty()) << content;
+    const std::string kUnmodifiedToken = "<UNMODIFIED>";
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "androidboot.vbmeta.avb_versio", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetKernelCmdlineFromString(bootconfig, "androidboot.nospace", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+}
+
+TEST(fs_mgr, ImportBootconfig) {
+    std::vector<std::pair<std::string, std::string>> result;
+    ImportBootconfigFromString(bootconfig, [&](std::string key, std::string value) {
+        result.emplace_back(key, value);
+    });
+    EXPECT_THAT(result, ContainerEq(bootconfig_result_space));
+}
+
+TEST(fs_mgr, GetBootconfig) {
+    std::string content;
+    for (const auto& [key, value] : bootconfig_result_space) {
+        EXPECT_TRUE(GetBootconfigFromString(bootconfig, key, &content)) << " for " << key;
+        EXPECT_EQ(content, value);
+    }
+
+    const std::string kUnmodifiedToken = "<UNMODIFIED>";
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetBootconfigFromString(bootconfig, "", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.vbmeta.avb_versio", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.nospace", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
 }
 
 TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
diff --git a/init/Android.bp b/init/Android.bp
index ee34215..d4852d6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -539,6 +539,7 @@
         "libprotobuf-cpp-lite",
     ],
     static_libs: [
+        "libfs_mgr",
         "libhidl-gen-utils",
     ],
 }
diff --git a/init/devices.cpp b/init/devices.cpp
index 7c23492..e76786a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -32,6 +32,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <fs_mgr.h>
 #include <libdm/dm.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -203,8 +204,8 @@
                 partition_map.emplace_back(map_pieces[0], map_pieces[1]);
             }
         };
-        ImportKernelCmdline(parser);
-        ImportBootconfig(parser);
+        android::fs_mgr::ImportKernelCmdline(parser);
+        android::fs_mgr::ImportBootconfig(parser);
         return partition_map;
     }();
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 8da6982..f5de17d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -57,6 +57,7 @@
 #include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <fs_mgr.h>
 #include <property_info_parser/property_info_parser.h>
 #include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
@@ -1317,7 +1318,8 @@
         return;
     }
 
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(android::fs_mgr::GetAndroidDtDir().c_str()),
+                                            closedir);
     if (!dir) return;
 
     std::string dt_file;
@@ -1328,7 +1330,7 @@
             continue;
         }
 
-        std::string file_name = get_android_dt_dir() + dp->d_name;
+        std::string file_name = android::fs_mgr::GetAndroidDtDir() + dp->d_name;
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -1340,7 +1342,7 @@
 constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
 
 static void ProcessKernelCmdline() {
-    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+    android::fs_mgr::ImportKernelCmdline([&](const std::string& key, const std::string& value) {
         if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
             InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
         }
@@ -1349,7 +1351,7 @@
 
 
 static void ProcessBootconfig() {
-    ImportBootconfig([&](const std::string& key, const std::string& value) {
+    android::fs_mgr::ImportBootconfig([&](const std::string& key, const std::string& value) {
         if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
             InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
         }
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index e6b868e..547b186 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -27,6 +27,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
+#include <fs_mgr.h>
 #include <unwindstack/AndroidUnwinder.h>
 
 #include "capabilities.h"
@@ -48,13 +49,9 @@
 
     const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";
     if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {
-        init_fatal_panic = false;
-        ImportBootconfig(
-                [kInitFatalPanicParamString](const std::string& key, const std::string& value) {
-                    if (key == kInitFatalPanicParamString && value == "true") {
-                        init_fatal_panic = true;
-                    }
-                });
+        std::string value;
+        init_fatal_panic = (android::fs_mgr::GetBootconfig(kInitFatalPanicParamString, &value) &&
+                            value == "true");
     } else {
         const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";
         init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
@@ -68,11 +65,7 @@
     const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";
     auto start_pos = cmdline.find(kRebootTargetString);
     if (start_pos == std::string::npos) {
-        ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {
-            if (key == kRebootTargetString) {
-                init_fatal_reboot_target = value;
-            }
-        });
+        android::fs_mgr::GetBootconfig(kRebootTargetString, &init_fatal_reboot_target);
         // We already default to bootloader if no setting is provided.
     } else {
         const std::string kRebootTargetStringPattern = kRebootTargetString + "=";
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 51093d8..f34474f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -101,23 +101,14 @@
 enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
 EnforcingStatus StatusFromProperty() {
-    EnforcingStatus status = SELINUX_ENFORCING;
-
-    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
-        if (key == "androidboot.selinux" && value == "permissive") {
-            status = SELINUX_PERMISSIVE;
-        }
-    });
-
-    if (status == SELINUX_ENFORCING) {
-        ImportBootconfig([&](const std::string& key, const std::string& value) {
-            if (key == "androidboot.selinux" && value == "permissive") {
-                status = SELINUX_PERMISSIVE;
-            }
-        });
+    std::string value;
+    if (android::fs_mgr::GetKernelCmdline("androidboot.selinux", &value) && value == "permissive") {
+        return SELINUX_PERMISSIVE;
     }
-
-    return status;
+    if (android::fs_mgr::GetBootconfig("androidboot.selinux", &value) && value == "permissive") {
+        return SELINUX_PERMISSIVE;
+    }
+    return SELINUX_ENFORCING;
 }
 
 bool IsEnforcing() {
diff --git a/init/util.cpp b/init/util.cpp
index d0478e8..e760a59 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,10 @@
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
+#if defined(__ANDROID__)
+#include <fs_mgr.h>
+#endif
+
 #ifdef INIT_FULL_SOURCES
 #include <android/api-level.h>
 #include <sys/system_properties.h>
@@ -60,8 +64,6 @@
 namespace android {
 namespace init {
 
-const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
-
 const std::string kDataDirPrefix("/data/");
 
 void (*trigger_shutdown)(const std::string& command) = nullptr;
@@ -240,33 +242,6 @@
     return -1;
 }
 
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) {
-    std::string cmdline;
-    android::base::ReadFileToString("/proc/cmdline", &cmdline);
-
-    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        if (pieces.size() == 2) {
-            fn(pieces[0], pieces[1]);
-        }
-    }
-}
-
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
-    std::string bootconfig;
-    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
-
-    for (const auto& entry : android::base::Split(bootconfig, "\n")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        if (pieces.size() == 2) {
-            // get rid of the extra space between a list of values and remove the quotes.
-            std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
-            value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
-            fn(android::base::Trim(pieces[0]), android::base::Trim(value));
-        }
-    }
-}
-
 bool make_dir(const std::string& path, mode_t mode) {
     std::string secontext;
     if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
@@ -375,45 +350,18 @@
     return dst;
 }
 
-static std::string init_android_dt_dir() {
-    // Use the standard procfs-based path by default
-    std::string android_dt_dir = kDefaultAndroidDtDir;
-    // The platform may specify a custom Android DT path in kernel cmdline
-    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
-        if (key == "androidboot.android_dt_dir") {
-            android_dt_dir = value;
-        }
-    });
-    // ..Or bootconfig
-    if (android_dt_dir == kDefaultAndroidDtDir) {
-        ImportBootconfig([&](const std::string& key, const std::string& value) {
-            if (key == "androidboot.android_dt_dir") {
-                android_dt_dir = value;
-            }
-        });
-    }
-
-    LOG(INFO) << "Using Android DT directory " << android_dt_dir;
-    return android_dt_dir;
-}
-
-// FIXME: The same logic is duplicated in system/core/fs_mgr/
-const std::string& get_android_dt_dir() {
-    // Set once and saves time for subsequent calls to this function
-    static const std::string kAndroidDtDir = init_android_dt_dir();
-    return kAndroidDtDir;
-}
-
 // Reads the content of device tree file under the platform's Android DT directory.
 // Returns true if the read is success, false otherwise.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
-    const std::string file_name = get_android_dt_dir() + sub_path;
+#if defined(__ANDROID__)
+    const std::string file_name = android::fs_mgr::GetAndroidDtDir() + sub_path;
     if (android::base::ReadFileToString(file_name, dt_content)) {
         if (!dt_content->empty()) {
             dt_content->pop_back();  // Trims the trailing '\0' out.
             return true;
         }
     }
+#endif
     return false;
 }
 
diff --git a/init/util.h b/init/util.h
index 3f0a4e0..2d02182 100644
--- a/init/util.h
+++ b/init/util.h
@@ -53,16 +53,11 @@
 Result<uid_t> DecodeUid(const std::string& name);
 
 bool mkdir_recursive(const std::string& pathname, mode_t mode);
-int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
+int wait_for_file(const char* filename, std::chrono::nanoseconds timeout);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
 Result<std::string> ExpandProps(const std::string& src);
 
-// Returns the platform's Android DT directory as specified in the kernel cmdline.
-// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
-const std::string& get_android_dt_dir();
 // Reads or compares the content of device tree file under the platform's Android DT directory.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
diff --git a/trusty/line-coverage/Android.bp b/trusty/line-coverage/Android.bp
new file mode 100644
index 0000000..36a73aa
--- /dev/null
+++ b/trusty/line-coverage/Android.bp
@@ -0,0 +1,36 @@
+// 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: "libtrusty_line_coverage",
+    vendor_available: true,
+    srcs: [
+        "coverage.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+        "libext2_uuid",
+        "liblog",
+        "libdmabufheap",
+        "libtrusty",
+    ],
+}
+
diff --git a/trusty/line-coverage/coverage.cpp b/trusty/line-coverage/coverage.cpp
new file mode 100644
index 0000000..57b7025
--- /dev/null
+++ b/trusty/line-coverage/coverage.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Sourete 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 "line-coverage"
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <assert.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/line-coverage/tipc.h>
+#include <trusty/tipc.h>
+#include <iostream>
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+struct control {
+    /* Written by controller, read by instrumented TA */
+    uint64_t        cntrl_flags;
+
+    /* Written by instrumented TA, read by controller */
+    uint64_t        oper_flags;
+    uint64_t        write_buffer_start_count;
+    uint64_t        write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using ::android::base::ErrnoError;
+using ::android::base::Error;
+using ::std::string;
+
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+}
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
+    : tipc_dev_(std::move(tipc_dev)),
+      coverage_srv_fd_(-1),
+      uuid_(*uuid),
+      record_len_(0),
+      shm_(NULL),
+      shm_len_(0) {}
+
+CoverageRecord::~CoverageRecord() {
+    if (shm_) {
+        munmap((void*)shm_, shm_len_);
+    }
+}
+
+volatile void *CoverageRecord::getShm() {
+    if(!IsOpen()) {
+        fprintf(stderr, "Warning! SHM is NULL!\n");
+    }
+    return shm_;
+}
+
+Result<void> CoverageRecord::Rpc(struct line_coverage_client_req* req, \
+                                  int req_fd, \
+                                  struct line_coverage_client_resp* resp) {
+    int rc;
+
+    if (req_fd < 0) {
+        rc = write(coverage_srv_fd_, req, sizeof(*req));
+    } else {
+        iovec iov = {
+                .iov_base = req,
+                .iov_len = sizeof(*req),
+        };
+
+        trusty_shm shm = {
+                .fd = req_fd,
+                .transfer = TRUSTY_SHARE,
+        };
+
+        rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);
+    }
+
+    if (rc != (int)sizeof(*req)) {
+        return ErrnoError() << "failed to send request to coverage server: ";
+    }
+
+    rc = read(coverage_srv_fd_, resp, sizeof(*resp));
+    if (rc != (int)sizeof(*resp)) {
+        return ErrnoError() << "failed to read reply from coverage server: ";
+    }
+
+    if (resp->hdr.cmd != (req->hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+        return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd;
+    }
+
+    return {};
+}
+
+Result<void> CoverageRecord::Open(int fd) {
+    struct line_coverage_client_req req;
+    struct line_coverage_client_resp resp;
+
+    if (shm_) {
+        return {}; /* already initialized */
+    }
+
+    coverage_srv_fd_= fd;
+
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+    req.open_args.uuid = uuid_;
+    auto ret = Rpc(&req, -1, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to open coverage client: " << ret.error();
+    }
+    record_len_ = resp.open_args.record_len;
+    shm_len_ = RoundPageUp(record_len_);
+
+    BufferAllocator allocator;
+
+    fd = allocator.Alloc("system", shm_len_);
+    if (fd < 0) {
+        return ErrnoError() << "failed to create dmabuf of size " << shm_len_
+                            << " err code: " << fd;
+    }
+    unique_fd dma_buf(fd);
+
+    void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (shm == MAP_FAILED) {
+        return ErrnoError() << "failed to map memfd: ";
+    }
+
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD;
+    req.share_record_args.shm_len = shm_len_;
+    ret = Rpc(&req, dma_buf, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to send shared memory: " << ret.error();
+    }
+
+    shm_ = shm;
+
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+    req.open_args.uuid = uuid_;
+    ret = Rpc(&req, -1, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to open coverage client: " << ret.error();
+    }
+
+    return {};
+}
+
+bool CoverageRecord::IsOpen() {
+    return shm_;
+}
+
+Result<void> CoverageRecord::SaveFile(const std::string& filename) {
+    if(!IsOpen()) {
+        return ErrnoError() << "Warning! SHM is NULL!";
+    }
+    android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+    if (!output_fd.ok()) {
+        return ErrnoError() << "Could not open output file";
+    }
+
+    uintptr_t* begin = (uintptr_t*)((char *)shm_ + sizeof(struct control));
+    bool ret = WriteFully(output_fd, begin, record_len_);
+    if(!ret) {
+        fprintf(stderr, "Coverage write to file failed\n");
+    }
+
+    return {};
+}
+
+}  // namespace line_coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/coverage.h b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
new file mode 100644
index 0000000..53901be
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <optional>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <stdint.h>
+#include <trusty/line-coverage/tipc.h>
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using android::base::Result;
+using android::base::unique_fd;
+
+class CoverageRecord {
+  public:
+    CoverageRecord(std::string tipc_dev, struct uuid* uuid);
+
+    ~CoverageRecord();
+    Result<void> Open(int fd);
+    bool IsOpen();
+    Result<void> SaveFile(const std::string& filename);
+    volatile void* getShm();
+
+  private:
+    Result<void> Rpc(struct line_coverage_client_req* req, \
+                      int req_fd, \
+                      struct line_coverage_client_resp* resp);
+
+    Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
+    std::string tipc_dev_;
+    int coverage_srv_fd_;
+    struct uuid uuid_;
+    size_t record_len_;
+    volatile void* shm_;
+    size_t shm_len_;
+};
+
+}  // namespace line_coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/tipc.h b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
new file mode 100644
index 0000000..20e855c
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
@@ -0,0 +1,140 @@
+/*
+ * 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 file needs to be kept in-sync with its counterpart on Trusty side */
+
+#pragma once
+
+#include <stdint.h>
+#include <trusty/line-coverage/uuid.h>
+
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+/**
+ * enum line_coverage_client_cmd - command identifiers for coverage client interface
+ * @LINE_COVERAGE_CLIENT_CMD_RESP_BIT:     response bit set as part of response
+ * @LINE_COVERAGE_CLIENT_CMD_SHIFT:        number of bits used by response bit
+ * @LINE_COVERAGE_CLIENT_CMD_OPEN:         command to open coverage record
+ * @LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD: command to register a shared memory region
+ *                                    where coverage record will be written to
+ */
+enum line_coverage_client_cmd {
+    LINE_COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
+    LINE_COVERAGE_CLIENT_CMD_SHIFT = 1U,
+    LINE_COVERAGE_CLIENT_CMD_OPEN = (1U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+    LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+    LINE_COVERAGE_CLIENT_CMD_SEND_LIST = (3U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+};
+
+/**
+ * struct line_coverage_client_hdr - header for coverage client messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct line_coverage_client_hdr {
+    uint32_t cmd;
+};
+
+/**
+ * struct line_coverage_client_open_req - arguments for request to open coverage
+ *                                   record
+ * @uuid: UUID of target TA
+ *
+ * There is one coverage record per TA. @uuid is used to identify both the TA
+ * and corresponding coverage record.
+ */
+struct line_coverage_client_open_req {
+    struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_open_resp - arguments for response to open coverage
+ *                                    record
+ * @record_len: length of coverage record that will be emitted by target TA
+ *
+ * Shared memory allocated for this coverage record must larger than
+ * @record_len.
+ */
+struct line_coverage_client_open_resp {
+    uint32_t record_len;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ *                                   record
+ * @uuid: UUID of TA that connected to aggregator
+ */
+struct line_coverage_client_send_list_resp {
+    struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ *                                   record
+ * @index: index of the list being requested
+ */
+struct line_coverage_client_send_list_req {
+    uint32_t index;
+};
+
+/**
+ * struct line_coverage_client_share_record_req - arguments for request to share
+ *                                           memory for coverage record
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is used to store coverage record.
+ *
+ * Upon success, this memory region can be assumed to be shared between the
+ * client and target TA.
+ */
+struct line_coverage_client_share_record_req {
+    uint32_t shm_len;
+};
+
+/**
+ * struct line_coverage_client_req - structure for a coverage client request
+ * @hdr:               message header
+ * @open_args:         arguments for %COVERAGE_CLIENT_CMD_OPEN request
+ * @share_record_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ * @index: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ */
+struct line_coverage_client_req {
+    struct line_coverage_client_hdr hdr;
+    union {
+        struct line_coverage_client_open_req open_args;
+        struct line_coverage_client_share_record_req share_record_args;
+        struct line_coverage_client_send_list_req send_list_args;
+    };
+};
+
+/**
+ * struct line_coverage_client_resp - structure for a coverage client response
+ * @hdr:       message header
+ * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN response
+ * @send_list_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD response
+ */
+struct line_coverage_client_resp {
+    struct line_coverage_client_hdr hdr;
+    union {
+        struct line_coverage_client_open_resp open_args;
+        struct line_coverage_client_send_list_resp send_list_args;
+    };
+};
diff --git a/trusty/line-coverage/include/trusty/line-coverage/uuid.h b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
new file mode 100644
index 0000000..1873980
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
@@ -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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+#define UUCMP(u1, u2) if (u1 != u2) return u1 < u2
+
+struct uuid {
+    uint32_t time_low;
+    uint16_t time_mid;
+    uint16_t time_hi_and_version;
+    uint8_t clock_seq_and_node[8];
+
+    bool operator<(const struct uuid& rhs) const
+    {
+        UUCMP(time_low, rhs.time_low);
+        UUCMP(time_mid, rhs.time_mid);
+        UUCMP(time_hi_and_version, rhs.time_hi_and_version);
+        return memcmp(clock_seq_and_node, rhs.clock_seq_and_node, 8);
+    }
+};
diff --git a/trusty/utils/coverage-controller/Android.bp b/trusty/utils/coverage-controller/Android.bp
new file mode 100644
index 0000000..1aa88cc
--- /dev/null
+++ b/trusty/utils/coverage-controller/Android.bp
@@ -0,0 +1,34 @@
+// 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_binary {
+    name: "coverage-controller",
+    vendor: true,
+
+    srcs: ["controller.cpp"],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libbase",
+        "libdmabufheap",
+    ],
+    static_libs: [
+        "libtrusty",
+        "libtrusty_line_coverage",
+    ],
+}
diff --git a/trusty/utils/coverage-controller/controller.cpp b/trusty/utils/coverage-controller/controller.cpp
new file mode 100644
index 0000000..730c010
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 <getopt.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/tipc.h>
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "controller.h"
+
+#define READ_ONCE(x) (*((volatile __typeof__(x) *) &(x)))
+#define WRITE_ONCE(x, val) (*((volatile __typeof__(val) *) &(x)) = (val))
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+using ::android::trusty::line_coverage::CoverageRecord;
+
+void Controller::run(std::string output_dir) {
+    connectCoverageServer();
+    struct control *control;
+    uint64_t complete_cnt = 0, start_cnt = 0, flags;
+
+    while(1) {
+        setUpShm();
+
+        for (int index = 0; index < record_list_.size(); index++) {
+            control = (struct control *)record_list_[index]->getShm();
+            start_cnt = READ_ONCE((control->write_buffer_start_count));
+            complete_cnt = READ_ONCE(control->write_buffer_complete_count);
+            flags = READ_ONCE(control->cntrl_flags);
+
+            if (complete_cnt != counters[index] && start_cnt == complete_cnt) {
+                WRITE_ONCE(control->cntrl_flags, FLAG_NONE);
+                std::string fmt = "/%d.%lu.profraw";
+                int sz = std::snprintf(nullptr, 0, fmt.c_str(), index, counters[index]);
+                std::string filename(sz+1, '.');
+                std::sprintf(filename.data(), fmt.c_str(), index, counters[index]);
+                filename.insert(0, output_dir);
+                android::base::Result<void> res = record_list_[index]->SaveFile(filename);
+                counters[index]++;
+            }
+            if(complete_cnt == counters[index] &&
+                !(flags & FLAG_RUN)) {
+                flags |= FLAG_RUN;
+                WRITE_ONCE(control->cntrl_flags, flags);
+            }
+        }
+    }
+}
+
+void Controller::connectCoverageServer() {
+    coverage_srv_fd = tipc_connect(TIPC_DEV, LINE_COVERAGE_CLIENT_PORT);
+    if (coverage_srv_fd < 0) {
+        fprintf(stderr, \
+                "Error: Failed to connect to Trusty coverage server: %d\n", coverage_srv_fd);
+        return;
+    }
+}
+
+void Controller::setUpShm() {
+    struct line_coverage_client_req req;
+    struct line_coverage_client_resp resp;
+    uint32_t cur_index = record_list_.size();
+    struct uuid zero_uuid = {0, 0, 0, { 0 }};
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+    int rc = write(coverage_srv_fd, &req, sizeof(req));
+        if (rc != (int)sizeof(req)) {
+            fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+            return;
+    }
+
+    while(1) {
+        rc = read(coverage_srv_fd, &resp, sizeof(resp));
+        if (rc != (int)sizeof(resp)) {
+            fprintf(stderr, "failed to read reply from coverage server:: %d\n", rc);
+        }
+
+        if (resp.hdr.cmd == (req.hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+            if (!memcmp(&resp.send_list_args.uuid, &zero_uuid, sizeof(struct uuid))) {
+                break;
+            }
+            if(uuid_set_.find(resp.send_list_args.uuid) == uuid_set_.end()) {
+                uuid_set_.insert(resp.send_list_args.uuid);
+                record_list_.push_back(std::make_unique<CoverageRecord>(TIPC_DEV,
+                                                                    &resp.send_list_args.uuid));
+                counters.push_back(0);
+            }
+        }
+        else {
+            fprintf(stderr, "Unknown response header\n");
+        }
+        cur_index++;
+        req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+        req.send_list_args.index = cur_index;
+        int rc = write(coverage_srv_fd, &req, sizeof(req));
+        if (rc != (int)sizeof(req)) {
+            fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+        }
+    }
+
+    for(int ind = 0 ; ind < record_list_.size() ; ind++) {
+        record_list_[ind]->Open(coverage_srv_fd);
+    }
+}
+
+
+}  // namespace controller
+}  // namespace trusty
+}  // namespace android
+
+int main(int argc, char* argv[]) {
+
+    std::string optarg = "";
+    do {
+        int c;
+        c = getopt(argc, argv, "o");
+
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'o':
+            break;
+        default:
+            fprintf(stderr, "usage: %s -o [output_directory]\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    } while (1);
+
+    if (argc > optind + 1) {
+        fprintf(stderr, "%s: too many arguments\n", argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (argc > optind) {
+        optarg = argv[optind];
+    }
+    if (optarg.size()==0) {
+        optarg = "data/local/tmp";
+    }
+
+    android::trusty::controller::Controller cur;
+    cur.run(optarg);
+
+    return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/trusty/utils/coverage-controller/controller.h b/trusty/utils/coverage-controller/controller.h
new file mode 100644
index 0000000..b771c16
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.h
@@ -0,0 +1,64 @@
+/*
+ * 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 <trusty/line-coverage/coverage.h>
+#include <array>
+#include <memory>
+#include <vector>
+#include <set>
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
+
+#define FLAG_NONE               0x0
+#define FLAG_RUN                0x1
+#define FLAG_TOGGLE_CLEAR       0x2
+
+struct control {
+    /* Written by controller, read by instrumented TA */
+    uint64_t        cntrl_flags;
+
+    /* Written by instrumented TA, read by controller */
+    uint64_t        oper_flags;
+    uint64_t        write_buffer_start_count;
+    uint64_t        write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+class Controller {
+  public:
+    public:
+        void run(std::string output_dir);
+
+    private:
+        std::vector<std::unique_ptr<line_coverage::CoverageRecord>>record_list_;
+        std::set<struct uuid>uuid_set_;
+        std::vector<uint64_t> counters;
+        int coverage_srv_fd;
+
+        void connectCoverageServer();
+        void setUpShm();
+};
+
+}  // namespace controller
+}  // namespace trusty
+}  // namespace android