Merge "Parallel Module Loading: Refactor threads pool and architecture" into main
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 858956a..34f2c45 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -335,7 +335,7 @@
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0+dead)");
 
-  if (mte_supported()) {
+  if (mte_supported() && mte_enabled()) {
     // Test that the default TAGGED_ADDR_CTRL value is set.
     ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)"
                          R"( \(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe\))");
@@ -443,7 +443,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_uaf) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -490,7 +490,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_oob_uaf) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -522,7 +522,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_overflow) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -565,7 +565,7 @@
 
 TEST_P(SizeParamCrasherTest, mte_underflow) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -614,7 +614,7 @@
   //     unsubtle chaos is sure to result.
   // https://man7.org/linux/man-pages/man3/longjmp.3.html
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -648,7 +648,7 @@
 
 TEST_F(CrasherTest, mte_async) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -678,7 +678,7 @@
 
 TEST_F(CrasherTest, mte_multiple_causes) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -764,7 +764,7 @@
 
 TEST_F(CrasherTest, mte_register_tag_dump) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -797,7 +797,7 @@
 
 TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -828,7 +828,7 @@
 
 TEST_F(CrasherTest, mte_fault_tag_dump) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
@@ -862,7 +862,7 @@
 
 TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {
 #if defined(__aarch64__)
-  if (!mte_supported()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "Requires MTE";
   }
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
index aad209a..988ca0c 100644
--- a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
@@ -175,3 +175,8 @@
   ProtoToString();
   EXPECT_MATCH(text_, "\\(BuildId: 0123456789abcdef\\)\\nSYMBOLIZE 0123456789abcdef 12345\\n");
 }
+
+TEST_F(TombstoneProtoToTextTest, uid) {
+  ProtoToString();
+  EXPECT_MATCH(text_, "\\nLOG uid: 0\\n");
+}
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 444c973..9deeeec 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -15,6 +15,15 @@
 // NOTE TO OEMS:
 // If you add custom fields to this proto, do not use numbers in the reserved range.
 
+// NOTE TO CONSUMERS:
+// With proto3 -- unlike proto2 -- HasValue is unreliable for any field
+// where the default value for that type is also a valid value for the field.
+// This means, for example, that a boolean that is false or an integer that
+// is zero will appear to be missing --- but because they're not actually
+// marked as `optional` in this schema, consumers should just use values
+// without first checking whether or not they're "present".
+// https://protobuf.dev/programming-guides/proto3/#default
+
 message CrashDetail {
   bytes name = 1;
   bytes data = 2;
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 6e47a08..d0938ee 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -170,7 +170,7 @@
         "android.hardware.fastboot@1.1",
         "android.hardware.fastboot-V1-ndk",
         "android.hardware.health@2.0",
-        "android.hardware.health-V3-ndk",
+        "android.hardware.health-V4-ndk",
         "libasyncio",
         "libbase",
         "libbinder_ndk",
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a963322..94c320a 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -551,6 +551,17 @@
     return GetTable(name, DM_STATUS_TABLE_FLAG, table);
 }
 
+void RedactTableInfo(const struct dm_target_spec& spec, std::string* data) {
+    if (DeviceMapper::GetTargetType(spec) == "crypt") {
+        auto parts = android::base::Split(*data, " ");
+        if (parts.size() < 2) {
+            return;
+        }
+        parts[1] = "redacted";
+        *data = android::base::Join(parts, " ");
+    }
+}
+
 // private methods of DeviceMapper
 bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
                             std::vector<TargetInfo>* table) {
@@ -589,6 +600,9 @@
             // Note: we use c_str() to eliminate any extra trailing 0s.
             data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();
         }
+        if (flags & DM_STATUS_TABLE_FLAG) {
+            RedactTableInfo(*spec, &data);
+        }
         table->emplace_back(*spec, data);
         cursor = next_cursor;
     }
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index b890f47..5eddb51 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -814,3 +814,31 @@
     TempDevice thin("thin", thinTable);
     ASSERT_TRUE(thin.valid());
 }
+
+TEST_F(DmTest, RedactDmCrypt) {
+    static constexpr uint64_t kImageSize = 65536;
+    unique_fd temp_file(CreateTempFile("file_1", kImageSize));
+    ASSERT_GE(temp_file, 0);
+
+    LoopDevice loop(temp_file, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    static constexpr const char* kAlgorithm = "aes-cbc-essiv:sha256";
+    static constexpr const char* kKey = "0e64ef514e6a1315b1f6390cb57c9e6a";
+
+    auto target = std::make_unique<DmTargetCrypt>(0, kImageSize / 512, kAlgorithm, kKey, 0,
+                                                  loop.device(), 0);
+    target->AllowDiscards();
+
+    DmTable table;
+    table.AddTarget(std::move(target));
+
+    auto& dm = DeviceMapper::Instance();
+    std::string crypt_path;
+    ASSERT_TRUE(dm.CreateDevice(test_name_, table, &crypt_path, 10s));
+
+    std::vector<DeviceMapper::TargetInfo> targets;
+    ASSERT_TRUE(dm.GetTableInfo(test_name_, &targets));
+    ASSERT_EQ(targets.size(), 1);
+    EXPECT_EQ(targets[0].data.find(kKey), std::string::npos);
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 8a70400..79443b2 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -64,6 +64,7 @@
   public:
     // Not thread safe.
     static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+    SnapshotMergeStats(const std::string& path);
 
     // ISnapshotMergeStats overrides
     bool Start() override;
@@ -88,7 +89,6 @@
   private:
     bool ReadState();
     bool DeleteState();
-    SnapshotMergeStats(const std::string& path);
 
     std::string path_;
     SnapshotMergeReport report_;
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 8e9d9c5..e684d87 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -24,9 +24,12 @@
 namespace snapshot {
 
 SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
-    static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
-    CHECK_EQ(g_instance.path_, parent.GetMergeStateFilePath());
-    return &g_instance;
+    static std::unique_ptr<SnapshotMergeStats> g_instance;
+
+    if (!g_instance || g_instance->path_ != parent.GetMergeStateFilePath()) {
+        g_instance = std::make_unique<SnapshotMergeStats>(parent.GetMergeStateFilePath());
+    }
+    return g_instance.get();
 }
 
 SnapshotMergeStats::SnapshotMergeStats(const std::string& path) : path_(path), running_(false) {}
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 639116e..9972bc7 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -88,6 +88,7 @@
         "libprocessgroup",
         "libprocessgroup_util",
         "libjsoncpp",
+        "liburing_cpp",
     ],
     export_include_dirs: ["include"],
     header_libs: [
@@ -136,6 +137,7 @@
         "libext4_utils",
         "liburing",
         "libzstd",
+        "liburing_cpp",
     ],
 
     header_libs: [
@@ -222,6 +224,7 @@
         "libjsoncpp",
         "liburing",
         "libz",
+        "liburing_cpp",
     ],
     include_dirs: [
         ".",
@@ -319,6 +322,7 @@
         "libjsoncpp",
         "liburing",
         "libz",
+        "liburing_cpp",
     ],
     include_dirs: [
         ".",
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
index 957c6a8..97f8df4 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -22,6 +22,7 @@
 
 #include "android-base/properties.h"
 #include "snapuserd_core.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -104,43 +105,108 @@
         return false;
     }
 
-    loff_t file_offset = offset;
-    auto verify_block_size = android::base::GetUintProperty<uint>("ro.virtual_ab.verify_block_size",
-                                                                  kBlockSizeVerify);
-    const uint64_t read_sz = verify_block_size;
+    int queue_depth = std::max(queue_depth_, 1);
+    int verify_block_size = verify_block_size_;
 
-    void* addr;
-    ssize_t page_size = getpagesize();
-    if (posix_memalign(&addr, page_size, read_sz) < 0) {
-        SNAP_PLOG(ERROR) << "posix_memalign failed "
-                         << " page_size: " << page_size << " read_sz: " << read_sz;
+    // Smaller partitions don't need a bigger queue-depth.
+    // This is required for low-memory devices.
+    if (dev_sz < threshold_size_) {
+        queue_depth = std::max(queue_depth / 2, 1);
+        verify_block_size >>= 2;
+    }
+
+    if (!IsBlockAligned(verify_block_size)) {
+        verify_block_size = EXT4_ALIGN(verify_block_size, BLOCK_SZ);
+    }
+
+    std::unique_ptr<io_uring_cpp::IoUringInterface> ring =
+            io_uring_cpp::IoUringInterface::CreateLinuxIoUring(queue_depth, 0);
+    if (ring.get() == nullptr) {
+        PLOG(ERROR) << "Verify: io_uring_queue_init failed for queue_depth: " << queue_depth;
         return false;
     }
 
-    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
-
-    uint64_t bytes_read = 0;
-
-    while (true) {
-        size_t to_read = std::min((dev_sz - file_offset), read_sz);
-
-        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
-            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
-                             << " partition-name: " << partition_name
-                             << " at offset: " << file_offset << " read-size: " << to_read
-                             << " block-size: " << dev_sz;
+    std::unique_ptr<struct iovec[]> vecs = std::make_unique<struct iovec[]>(queue_depth);
+    std::vector<std::unique_ptr<void, decltype(&::free)>> buffers;
+    for (int i = 0; i < queue_depth; i++) {
+        void* addr;
+        ssize_t page_size = getpagesize();
+        if (posix_memalign(&addr, page_size, verify_block_size) < 0) {
+            LOG(ERROR) << "posix_memalign failed";
             return false;
         }
 
-        bytes_read += to_read;
-        file_offset += (skip_blocks * verify_block_size);
-        if (file_offset >= dev_sz) {
+        buffers.emplace_back(addr, ::free);
+        vecs[i].iov_base = addr;
+        vecs[i].iov_len = verify_block_size;
+    }
+
+    auto ret = ring->RegisterBuffers(vecs.get(), queue_depth);
+    if (!ret.IsOk()) {
+        SNAP_LOG(ERROR) << "io_uring_register_buffers failed: " << ret.ErrCode();
+        return false;
+    }
+
+    loff_t file_offset = offset;
+    const uint64_t read_sz = verify_block_size;
+    uint64_t total_read = 0;
+    int num_submitted = 0;
+
+    SNAP_LOG(DEBUG) << "VerifyBlocks: queue_depth: " << queue_depth
+                    << " verify_block_size: " << verify_block_size << " dev_sz: " << dev_sz
+                    << " file_offset: " << file_offset << " skip_blocks: " << skip_blocks;
+
+    while (file_offset < dev_sz) {
+        for (size_t i = 0; i < queue_depth; i++) {
+            uint64_t to_read = std::min((dev_sz - file_offset), read_sz);
+            if (to_read <= 0) break;
+
+            const auto sqe =
+                    ring->PrepReadFixed(fd.get(), vecs[i].iov_base, to_read, file_offset, i);
+            if (!sqe.IsOk()) {
+                SNAP_PLOG(ERROR) << "PrepReadFixed failed";
+                return false;
+            }
+            file_offset += (skip_blocks * to_read);
+            total_read += to_read;
+            num_submitted += 1;
+            if (file_offset >= dev_sz) {
+                break;
+            }
+        }
+
+        if (num_submitted == 0) {
             break;
         }
+
+        const auto io_submit = ring->SubmitAndWait(num_submitted);
+        if (!io_submit.IsOk()) {
+            SNAP_LOG(ERROR) << "SubmitAndWait failed: " << io_submit.ErrMsg()
+                            << " for: " << num_submitted << " entries.";
+            return false;
+        }
+
+        SNAP_LOG(DEBUG) << "io_uring_submit: " << total_read << "num_submitted: " << num_submitted
+                        << "ret: " << ret;
+
+        const auto cqes = ring->PopCQE(num_submitted);
+        if (cqes.IsErr()) {
+            SNAP_LOG(ERROR) << "PopCqe failed for: " << num_submitted
+                            << " error: " << cqes.GetError().ErrMsg();
+            return false;
+        }
+        for (const auto& cqe : cqes.GetResult()) {
+            if (cqe.res < 0) {
+                SNAP_LOG(ERROR) << "I/O failed: cqe->res: " << cqe.res;
+                return false;
+            }
+            num_submitted -= 1;
+        }
     }
 
-    SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
-                    << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+    SNAP_LOG(DEBUG) << "Verification success with io_uring: "
+                    << " dev_sz: " << dev_sz << " partition_name: " << partition_name
+                    << " total_read: " << total_read;
 
     return true;
 }
@@ -175,21 +241,14 @@
         return false;
     }
 
-    /*
-     * Not all partitions are of same size. Some partitions are as small as
-     * 100Mb. We can just finish them in a single thread. For bigger partitions
-     * such as product, 4 threads are sufficient enough.
-     *
-     * TODO: With io_uring SQ_POLL support, we can completely cut this
-     * down to just single thread for all partitions and potentially verify all
-     * the partitions with zero syscalls. Additionally, since block layer
-     * supports polling, IO_POLL could be used which will further cut down
-     * latency.
-     */
+    if (!KernelSupportsIoUring()) {
+        SNAP_LOG(INFO) << "Kernel does not support io_uring. Skipping verification.\n";
+        // This will fallback to update_verifier to do the verification.
+        return false;
+    }
+
     int num_threads = kMinThreadsToVerify;
-    auto verify_threshold_size = android::base::GetUintProperty<uint>(
-            "ro.virtual_ab.verify_threshold_size", kThresholdSize);
-    if (dev_sz > verify_threshold_size) {
+    if (dev_sz > threshold_size_) {
         num_threads = kMaxThreadsToVerify;
     }
 
@@ -197,13 +256,11 @@
     off_t start_offset = 0;
     const int skip_blocks = num_threads;
 
-    auto verify_block_size =
-            android::base::GetUintProperty("ro.virtual_ab.verify_block_size", kBlockSizeVerify);
     while (num_threads) {
         threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,
                                         partition_name, dm_block_device, start_offset, skip_blocks,
                                         dev_sz));
-        start_offset += verify_block_size;
+        start_offset += verify_block_size_;
         num_threads -= 1;
         if (start_offset >= dev_sz) {
             break;
@@ -218,9 +275,9 @@
     if (ret) {
         succeeded = true;
         UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
-        SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
-                       << " Size: " << dev_sz
-                       << " verification success. Duration : " << timer.duration().count() << " ms";
+        SNAP_LOG(INFO) << "Partition verification success: " << partition_name
+                       << " Block-device: " << dm_block_device << " Size: " << dev_sz
+                       << " Duration : " << timer.duration().count() << " ms";
         return true;
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
index b300a70..69a334b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -15,6 +15,7 @@
 
 #pragma once
 
+#include <liburing.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -22,6 +23,7 @@
 #include <mutex>
 #include <string>
 
+#include <liburing_cpp/IoUring.h>
 #include <snapuserd/snapuserd_kernel.h>
 #include <storage_literals/storage_literals.h>
 
@@ -48,27 +50,23 @@
     std::mutex m_lock_;
     std::condition_variable m_cv_;
 
+    int kMinThreadsToVerify = 1;
+    int kMaxThreadsToVerify = 3;
+
     /*
-     * Scanning of partitions is an expensive operation both in terms of memory
-     * and CPU usage. The goal here is to scan the partitions fast enough without
-     * significant increase in the boot time.
-     *
-     * Partitions such as system, product which may be huge and may need multiple
-     * threads to speed up the verification process. Using multiple threads for
-     * all partitions may increase CPU usage significantly. Hence, limit that to
-     * 1 thread per partition.
+     * To optimize partition scanning speed without significantly impacting boot time,
+     * we employ O_DIRECT, bypassing the page-cache. However, O_DIRECT's memory
+     * allocation from CMA can be problematic on devices with restricted CMA space.
+     * To address this, io_uring_register_buffers() pre-registers I/O buffers,
+     * preventing CMA usage. See b/401952955 for more details.
      *
      * These numbers were derived by monitoring the memory and CPU pressure
      * (/proc/pressure/{cpu,memory}; and monitoring the Inactive(file) and
      * Active(file) pages from /proc/meminfo.
-     *
-     * Additionally, for low memory devices, it is advisable to use O_DIRECT
-     * functionality for source block device.
      */
-    int kMinThreadsToVerify = 1;
-    int kMaxThreadsToVerify = 3;
-    uint64_t kThresholdSize = 750_MiB;
-    uint64_t kBlockSizeVerify = 2_MiB;
+    uint64_t verify_block_size_ = 1_MiB;
+    uint64_t threshold_size_ = 2_GiB;
+    int queue_depth_ = 4;
 
     bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
     void UpdatePartitionVerificationState(UpdateVerifyState state);
diff --git a/healthd/Android.bp b/healthd/Android.bp
index e158e07..7eb6edd 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -4,7 +4,10 @@
 
 cc_defaults {
     name: "libbatterymonitor_defaults",
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     vendor_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
@@ -76,7 +79,7 @@
     defaults: ["libbatterymonitor_defaults"],
     srcs: ["BatteryMonitor.cpp"],
     static_libs: [
-        "android.hardware.health-V3-ndk",
+        "android.hardware.health-V4-ndk",
     ],
     whole_static_libs: [
         // Need to translate HIDL to AIDL to support legacy APIs in
@@ -165,12 +168,12 @@
     defaults: ["libhealthd_charger_ui_defaults"],
 
     static_libs: [
-        "android.hardware.health-V3-ndk",
+        "android.hardware.health-V4-ndk",
         "android.hardware.health-translate-ndk",
     ],
 
     export_static_lib_headers: [
-        "android.hardware.health-V3-ndk",
+        "android.hardware.health-V4-ndk",
     ],
 }
 
@@ -242,7 +245,7 @@
     static_libs: [
         // common
         "android.hardware.health@1.0-convert",
-        "android.hardware.health-V3-ndk",
+        "android.hardware.health-V4-ndk",
         "libbatterymonitor",
         "libcharger_sysprop",
         "libhealthd_charger_nops",
@@ -287,8 +290,8 @@
                 "libminui",
                 "libsuspend",
             ],
-        }
-    }
+        },
+    },
 }
 
 cc_test {
@@ -307,7 +310,7 @@
     defaults: ["charger_defaults"],
     srcs: [
         "AnimationParser_test.cpp",
-        "healthd_mode_charger_test.cpp"
+        "healthd_mode_charger_test.cpp",
     ],
     static_libs: [
         "android.hardware.health@1.0",
diff --git a/init/README.md b/init/README.md
index c9742ad..6a66f14 100644
--- a/init/README.md
+++ b/init/README.md
@@ -971,26 +971,13 @@
 
 Bootcharting
 ------------
-This version of init contains code to perform "bootcharting": generating log
-files that can be later processed by the tools provided by <http://www.bootchart.org/>.
+Bootchart provides CPU and I/O load breakdown of all processes for the whole system.
+Refer to the instructions at
+ <https://source.android.com/docs/core/perf/boot-times#bootchart>.
 
 On the emulator, use the -bootchart _timeout_ option to boot with bootcharting
 activated for _timeout_ seconds.
 
-On a device:
-
-    adb shell 'touch /data/bootchart/enabled'
-
-Don't forget to delete this file when you're done collecting data!
-
-The log files are written to /data/bootchart/. A script is provided to
-retrieve them and create a bootchart.tgz file that can be used with the
-bootchart command-line utility:
-
-    sudo apt-get install pybootchartgui
-    # grab-bootchart.sh uses $ANDROID_SERIAL.
-    $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
-
 One thing to watch for is that the bootchart will show init as if it started
 running at 0s. You'll have to look at dmesg to work out when the kernel
 actually started init.
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
index 009b639..b299b7d 100755
--- a/init/compare-bootcharts.py
+++ b/init/compare-bootcharts.py
@@ -47,7 +47,7 @@
 def analyze_process_maps(process_map1, process_map2, jiffy_record):
     # List interesting processes here
     processes_of_interest = [
-        '/init',
+        '/system/bin/init',
         '/system/bin/surfaceflinger',
         '/system/bin/bootanimation',
         'zygote64',
diff --git a/init/libprefetch/prefetch/prefetch.rc b/init/libprefetch/prefetch/prefetch.rc
index bdcbbc6..56fb827 100644
--- a/init/libprefetch/prefetch/prefetch.rc
+++ b/init/libprefetch/prefetch/prefetch.rc
@@ -30,7 +30,7 @@
     disabled
     oneshot
 
-service prefetch_replay /system/bin/prefetch replay --io-depth ${ro.prefetch_boot.io_depth:-2} --max-fds ${ro.prefetch_boot.max_fds:-128}
+service prefetch_replay /system/bin/prefetch replay --io-depth ${ro.prefetch_boot.io_depth:-2} --max-fds ${ro.prefetch_boot.max_fds:-1024}
     user root
     group root system
     disabled
diff --git a/init/libprefetch/prefetch/src/arch/android.rs b/init/libprefetch/prefetch/src/arch/android.rs
index a87502d..7d446ba 100644
--- a/init/libprefetch/prefetch/src/arch/android.rs
+++ b/init/libprefetch/prefetch/src/arch/android.rs
@@ -12,6 +12,12 @@
 
 const PREFETCH_RECORD_PROPERTY_STOP: &str = "prefetch_boot.record_stop";
 
+fn is_prefetch_enabled() -> Result<bool, Error> {
+    rustutils::system_properties::read_bool("ro.prefetch_boot.enabled", false).map_err(|e| {
+        Error::Custom { error: format!("Failed to read ro.prefetch_boot.enabled: {}", e) }
+    })
+}
+
 fn wait_for_property_true(
     property_name: &str,
     timeout: Option<Duration>,
@@ -31,6 +37,10 @@
 /// Checks if we can perform replay phase.
 /// Ensure that the pack file exists and is up-to-date, returns false otherwise.
 pub fn can_perform_replay(pack_path: &Path, fingerprint_path: &Path) -> Result<bool, Error> {
+    if !is_prefetch_enabled()? {
+        return Ok(false);
+    }
+
     if !pack_path.exists() || !fingerprint_path.exists() {
         return Ok(false);
     }
@@ -54,6 +64,10 @@
     pack_path: &Path,
     fingerprint_path: &Path,
 ) -> Result<bool, Error> {
+    if !is_prefetch_enabled()? {
+        return Ok(false);
+    }
+
     if !ready_path.exists() {
         File::create(ready_path)
             .map_err(|_| Error::Custom { error: "File Creation failed".to_string() })?;
diff --git a/init/libprefetch/prefetch/src/tracer/mem.rs b/init/libprefetch/prefetch/src/tracer/mem.rs
index f69ae80..42120da 100644
--- a/init/libprefetch/prefetch/src/tracer/mem.rs
+++ b/init/libprefetch/prefetch/src/tracer/mem.rs
@@ -320,8 +320,8 @@
     // Convenience function to create regex. Used once per life of `record` but multiple times in
     // case of tests.
     pub fn get_trace_line_regex() -> Result<Regex, Error> {
-        // TODO: Fix this Regex expression for 5.15 kernels. This expression
-        // works only on 6.1+. Prior to 6.1, "<page>" was present in the output.
+        // `page=[hex]` entry exists in 5.x kernel format but not in 6.x.
+        // Conversely, `order=[digit]` entry exists in 6.x kernel format but not in 5.x.
         Regex::new(concat!(
             r"^\s+(?P<cmd_pid>\S+)",
             r"\s+(?P<cpu>\S+)",
@@ -330,9 +330,10 @@
             r"\s+mm_filemap_add_to_page_cache:",
             r"\s+dev\s+(?P<major>[0-9]+):(?P<minor>[0-9]+)",
             r"\s+ino\s+(?P<ino>\S+)",
-            //r"\s+(?P<page>\S+)",
+            r"(?:\s+(?P<page>page=\S+))?",
             r"\s+(?P<pfn>\S+)",
-            r"\s+ofs=(?P<offset>[0-9]+)"
+            r"\s+ofs=(?P<offset>[0-9]+)",
+            r"(?:\s+(?P<order>\S+))?"
         ))
         .map_err(|e| Error::Custom {
             error: format!("create regex for tracing failed with: {}", e),
@@ -682,22 +683,30 @@
 
     use super::*;
 
-    static TRACE_BUFFER: &str = r#"
- Settingide-502  [001] ....   484.360292: mm_filemap_add_to_page_CACHE: dev 254:6 ino cf1 page=68d477 pfn=59833 ofs=32768
- Settingide-502  [001] ....   484.360311: mm_filemap_add_to_page_cache: dev 254:6 ino cf1 page=759458 pfn=59827 ofs=57344
- BOX_ENTDED-3071 [001] ....   485.276715: mm_filemap_add_to_pag_ecache: dev 254:6 ino 1 page=00cc1c pfn=81748 ofs=13574144
- BOX_ENTDED-3071 [001] ....   485.276990: mm_filemap_add_to_page_cache: dev 254:6 ino cf2 page=36540b pfn=60952 ofs=0
- .gms.peent-843  [001] ....   485.545516: mm_filemap_add_to_page_cache: dev 254:6 ino 1 page=002e8b pfn=58928 ofs=13578240
- .gms.peent-843  [001] ....   485.545820: mm_filemap_add_to_page_cache: dev 254:6 ino cf3 page=6233ce pfn=58108 ofs=0
-      an.bg-459  [001] ....   494.029396: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=c5b5c7 pfn=373933 ofs=1310720
-      an.bg-459  [001] ....   494.029398: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=b8b9ec pfn=410074 ofs=1314816
-       "#;
+    static TRACE_BUFFER: &str = concat!(
+        // kernel 5.x
+        " Settingide-502  [001] ....   484.360292: mm_filemap_add_to_page_CACHE: dev 254:6 ino cf1 page=68d477 pfn=59833 ofs=32768\n",
+        " Settingide-502  [001] ....   484.360311: mm_filemap_add_to_page_cache: dev 254:6 ino cf1 page=759458 pfn=59827 ofs=57344\n",
+        " BOX_ENTDED-3071 [001] ....   485.276715: mm_filemap_add_to_pag_ecache: dev 254:6 ino 1 page=00cc1c pfn=81748 ofs=13574144\n",
+        " BOX_ENTDED-3071 [001] ....   485.276990: mm_filemap_add_to_page_cache: dev 254:6 ino cf2 page=36540b pfn=60952 ofs=0\n",
+        " .gms.peent-843  [001] ....   485.545516: mm_filemap_add_to_page_cache: dev 254:6 ino 1 page=002e8b pfn=58928 ofs=13578240\n",
+        " .gms.peent-843  [001] ....   485.545820: mm_filemap_add_to_page_cache: dev 254:6 ino cf3 page=6233ce pfn=58108 ofs=0\n",
+        "      an.bg-459  [001] ....   494.029396: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=c5b5c7 pfn=373933 ofs=1310720\n",
+        "      an.bg-459  [001] ....   494.029398: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=b8b9ec pfn=410074 ofs=1314816\n",
+
+        // kernel 6.x
+        " logcat-686     [006] ..... 148216.040320: mm_filemap_add_to_page_CACHE: dev 254:85 ino 3f15 pfn=0x213bc2 ofs=528384 order=0\n",
+        " logcat-686     [001] ..... 148217.776227: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x21d306 ofs=532480 order=0\n",
+        " logcat-686     [003] ..... 148219.044389: mm_filemap_add_to_pag_ecache: dev 254:85 ino 3f15 pfn=0x224b8d ofs=536576 order=0\n",
+        " logcat-686     [001] ..... 148220.780964: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x1bfe0a ofs=540672 order=0\n",
+        " logcat-686     [001] ..... 148223.046560: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x1f3d29 ofs=544768 order=0",
+    );
 
     fn sample_mem_traces() -> (String, Vec<Option<TraceLineInfo>>) {
         (
             TRACE_BUFFER.to_owned(),
             vec![
-                None,
+                // 5.x
                 None,
                 Some(TraceLineInfo::from_fields(254, 6, 0xcf1, 57344, 484360311000)),
                 None,
@@ -706,7 +715,12 @@
                 Some(TraceLineInfo::from_fields(254, 6, 0xcf3, 0, 485545820000)),
                 Some(TraceLineInfo::from_fields(254, 3, 0x7cf, 1310720, 494029396000)),
                 Some(TraceLineInfo::from_fields(254, 3, 0x7cf, 1314816, 494029398000)),
+                // 6.x
                 None,
+                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 532480, 148217776227000)),
+                None,
+                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 540672, 148220780964000)),
+                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 544768, 148223046560000)),
             ],
         )
     }
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index f8821a0..8d9a046 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -98,7 +98,17 @@
         // Look up the filesystems that were mounted under /sys before we wiped
         // it and attempt to restore them.
         for (const auto& entry : mounts) {
-            if (entry.mount_point.starts_with("/sys/")) {
+            // Never mount /sys/kernel/debug/tracing. This is the *one* mount
+            // that is special within Linux kernel: for backward compatibility
+            // tracefs gets auto-mounted there whenever one mounts debugfs [1].
+            //
+            // Attempting to mount the filesystem here will cause SELinux
+            // denials, because unlike *all other* filesystems in Android, it's
+            // not init who mounted it so there's no policy that would allow it.
+            //
+            // [1] https://lore.kernel.org/lkml/20150204143755.694479564@goodmis.org/
+            if (entry.mount_point.starts_with("/sys/") &&
+                entry.mount_point != "/sys/kernel/debug/tracing") {
                 if (mount(entry.blk_device.c_str(), entry.mount_point.c_str(),
                           entry.fs_type.c_str(), entry.flags, "")) {
                     LOG(WARNING) << "Could not mount(" << entry.mount_point
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 00a1114..0d1b7fe 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -72,20 +72,14 @@
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "product/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "product/apex/*/bin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
-    { 00750, AID_ROOT,         AID_SYSTEM,       0, "system/apex/com.android.tethering/bin/for-system" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
-    { 00750, AID_ROOT,         AID_SYSTEM,       0, "system_ext/apex/com.android.tethering/bin/for-system" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/apex/*/bin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     {},
         // clang-format on
@@ -182,8 +176,6 @@
 
     // the following files have enhanced capabilities and ARE included
     // in user builds.
-    { 06755, AID_CLAT,      AID_CLAT,      0, "system/apex/com.android.tethering/bin/for-system/clatd" },
-    { 06755, AID_CLAT,      AID_CLAT,      0, "system_ext/apex/com.android.tethering/bin/for-system/clatd" },
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
@@ -214,23 +206,19 @@
     { 00644, AID_ROOT,      AID_ROOT,      0, "odm/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "odm/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "product/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "product/apex/*bin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "product/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "product/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "product/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/apex/*/bin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system_ext/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system_ext/app/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "system_ext/priv-app/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/apex/*bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "vendor/framework/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, "vendor/app/*" },
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index dbf736a..e9345a5 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -36,7 +36,7 @@
         "Controller": "memory",
         "Path": ".",
         "NeedsActivation": true,
-        "MaxActivationDepth": 0,
+        "MaxActivationDepth": 3,
         "Optional": true
       }
     ]
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 720cb30..42cdb91 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -597,7 +597,7 @@
           "Params":
           {
             "Name": "MemSoftLimit",
-            "Value": "16MB"
+            "Value": "16M"
           }
         },
         {
@@ -619,7 +619,7 @@
           "Params":
           {
             "Name": "MemSoftLimit",
-            "Value": "512MB"
+            "Value": "512M"
           }
         },
         {
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index 3204a9f..c0d31d9 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -118,6 +118,12 @@
 }
 
 prebuilt_etc {
+    name: "init-mmd-prop.rc",
+    src: "init-mmd-prop.rc",
+    sub_dir: "init",
+}
+
+prebuilt_etc {
     name: "asan.options",
     src: "asan.options",
 }
diff --git a/rootdir/init-mmd-prop.rc b/rootdir/init-mmd-prop.rc
new file mode 100644
index 0000000..6e9191c
--- /dev/null
+++ b/rootdir/init-mmd-prop.rc
@@ -0,0 +1,19 @@
+on property:sys.boot_completed=1
+    # When mmd package is not included in the image, we need to initialize
+    # `mmd.enabled_aconfig` sysprop instead of `mmd --set-property`.
+    #
+    # This is because of the consideration for devices in Trunkfood and Nextfood
+    # under mmd being launched via AConfig flag. The devices set up zram with
+    # mmd if `mmd_enabled` AConfig flag is enabled, otherwise set up zram with
+    # swapon_all init command. Since AConfig does not support any init script
+    # integration, we use `mmd.enabled_aconfig` copied by `mmd --set-property`
+    # instead of AConfig flag itself and we need mmd.enabled_aconfig to be empty
+    # by default, to let swapon_all command wait until aconfig flag value is
+    # loaded to the system property.
+    # Devices in Trunkfood and Nextfood needs to execute swapon_all command on
+    # `on property:mmd.enabled_aconfig=*` trigger. So initializing
+    # `mmd.enabled_aconfig` sysprop is required on images without mmd package.
+    #
+    # Note that this init file must not be in the image if mmd is built into the
+    # image.
+    setprop mmd.enabled_aconfig false
\ No newline at end of file
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 357c0e6..3358742 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,7 +24,7 @@
     shared_libs: [
         "android.hardware.health@1.0",
         "android.hardware.health@2.0",
-        "android.hardware.health-V3-ndk",
+        "android.hardware.health-V4-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
@@ -47,7 +47,7 @@
         "-Wall",
         "-Werror",
         "-Wextra",
-        "-Wno-unused-parameter"
+        "-Wno-unused-parameter",
     ],
 }
 
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 9910aee..121837d 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -55,8 +55,6 @@
 "}"
 /* clang-format on */
 
-#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
-
 static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
 static const char *echo_name = "com.android.ipc-unittest.srv.echo";
 static const char *ta_only_name = "com.android.ipc-unittest.srv.ta_only";
@@ -906,14 +904,12 @@
 
 static int send_fd_test(const struct tipc_test_params* params) {
     int ret;
-    int dma_buf[] = {-1, -1, -1};
+    int dma_buf = -1;
     int fd = -1;
-    volatile char* buf[countof(dma_buf)] = {MAP_FAILED, MAP_FAILED, MAP_FAILED};
+    volatile char* buf = MAP_FAILED;
     BufferAllocator* allocator = NULL;
-    uint i;
 
     const size_t num_chunks = 10;
-    const size_t buf_size = memref_chunk_size * num_chunks;
 
     fd = tipc_connect(params->dev_name, receiver_name);
     if (fd < 0) {
@@ -929,86 +925,56 @@
         goto cleanup;
     }
 
-    for (i = 0; i < countof(dma_buf); i++) {
-        ret = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
-        if (ret < 0) {
-            fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
-            goto cleanup;
-        }
-        dma_buf[i] = ret;
+    size_t buf_size = memref_chunk_size * num_chunks;
+    dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
+    if (dma_buf < 0) {
+        ret = dma_buf;
+        fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
+        goto cleanup;
     }
 
-    for (i = 0; i < countof(dma_buf); i++) {
-        buf[i] = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf[i], 0);
-        if (buf[i] == MAP_FAILED) {
-            fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
-            ret = -1;
-            goto cleanup;
-        }
-
-        strcpy((char*)buf[i], "From NS");
+    buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (buf == MAP_FAILED) {
+        fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
+        ret = -1;
+        goto cleanup;
     }
 
-    struct trusty_shm shm[] = {
-            {
-                    .fd = dma_buf[0],
-                    .transfer = TRUSTY_SHARE,
-            },
-            {
-                    .fd = dma_buf[0],
-                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
-            },
-            {
-                    .fd = dma_buf[1],
-                    .transfer = TRUSTY_LEND,
-            },
-            {
-                    .fd = dma_buf[1],
-                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
-            },
-            {
-                    .fd = dma_buf[2],
-                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
-            },
+    strcpy((char*)buf, "From NS");
+
+    struct trusty_shm shm = {
+            .fd = dma_buf,
+            .transfer = TRUSTY_SHARE,
     };
 
-    for (i = 0; i < countof(shm); i++) {
-        ssize_t rc = tipc_send(fd, NULL, 0, &shm[i], 1);
-        if (rc < 0) {
-            fprintf(stderr, "tipc_send failed: %zd\n", rc);
-            ret = rc;
-            goto cleanup;
-        }
-        char c;
-        read(fd, &c, 1);
+    ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
+    if (rc < 0) {
+        fprintf(stderr, "tipc_send failed: %zd\n", rc);
+        ret = rc;
+        goto cleanup;
     }
+    char c;
+    read(fd, &c, 1);
+    tipc_close(fd);
 
     ret = 0;
-    for (i = 0; i < countof(buf); i++) {
-        for (size_t skip = 0; skip < num_chunks; skip++) {
-            int cmp = strcmp("Hello from Trusty!", (const char*)&buf[i][skip * memref_chunk_size])
-                              ? (-1)
-                              : 0;
-            if (cmp) fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
-            ret |= cmp;
-        }
+    for (size_t skip = 0; skip < num_chunks; skip++) {
+        int cmp = strcmp("Hello from Trusty!",
+                         (const char*)&buf[skip * memref_chunk_size]) ? (-1) : 0;
+        if (cmp)
+            fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
+        ret |= cmp;
     }
 
 cleanup:
-    for (i = 0; i < countof(dma_buf); i++) {
-        if (buf[i] != MAP_FAILED) {
-            munmap((char*)buf[i], buf_size);
-        }
-        if (dma_buf[i] >= 0) {
-            close(dma_buf[i]);
-        }
+    if (buf != MAP_FAILED) {
+        munmap((char*)buf, buf_size);
     }
+    close(dma_buf);
     if (allocator) {
         FreeDmabufHeapBufferAllocator(allocator);
     }
-    if (fd >= 0) {
-        tipc_close(fd);
-    }
+    tipc_close(fd);
     return ret;
 }