Merge "aconfig_storage: fix aconfig storage dir permissions" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7d20995..439218d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -21,8 +21,11 @@
     local_include_dirs: ["include"],
     product_variables: {
         debuggable: {
-            cflags: ["-UANDROID_DEBUGGABLE", "-DANDROID_DEBUGGABLE=1"],
-        }
+            cflags: [
+                "-UANDROID_DEBUGGABLE",
+                "-DANDROID_DEBUGGABLE=1",
+            ],
+        },
     },
 }
 
@@ -256,7 +259,7 @@
     ],
 
     static_libs: [
-        "libdexfile_support",  // libunwindstack dependency
+        "libdexfile_support", // libunwindstack dependency
         "libunwindstack",
         "liblzma",
         "libbase",
@@ -299,7 +302,7 @@
         },
         android: {
             runtime_libs: [
-                "libdexfile",           // libdexfile_support dependency
+                "libdexfile", // libdexfile_support dependency
             ],
         },
     },
@@ -494,7 +497,7 @@
 
     header_libs: [
         "bionic_libc_platform_headers",
-        "libdebuggerd_common_headers"
+        "libdebuggerd_common_headers",
     ],
 
     static_libs: [
@@ -552,7 +555,6 @@
     },
 }
 
-
 // This installs the "other" architecture (so 32-bit on 64-bit device).
 prebuilt_etc {
     name: "crash_dump.policy_other",
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index fe1689c..4c6a400 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -15,7 +15,7 @@
         "-fstack-protector-all",
         "-Wno-date-time",
     ],
-    tidy: false,  // crasher.cpp tests many memory access errors
+    tidy: false, // crasher.cpp tests many memory access errors
     srcs: ["crasher.cpp"],
     arch: {
         arm: {
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 7be5d61..7b9e780 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -52,4 +52,3 @@
     sdk_version: "current",
     static_libs: ["libprotobuf-java-lite"],
 }
-
diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp
index 2007f39..bf19bb7 100644
--- a/debuggerd/rust/tombstoned_client/Android.bp
+++ b/debuggerd/rust/tombstoned_client/Android.bp
@@ -8,7 +8,7 @@
         "wrapper.cpp",
     ],
     generated_sources: [
-        "libtombstoned_client_rust_bridge_code"
+        "libtombstoned_client_rust_bridge_code",
     ],
     header_libs: [
         "libbase_headers",
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 972a575..dc751da 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,8 +25,8 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
-sysinfo: 1
 setsockopt: 1
+sysinfo: 1
 
 process_vm_readv: 1
 
@@ -53,20 +53,29 @@
 
 #if 0
 libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
-Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility.
+Define values for PROT_READ, PROT_WRITE and PROT_MTE ourselves to maintain backwards compatibility.
 #else
 #define PROT_READ 0x1
 #define PROT_WRITE 0x2
+#define PROT_MTE 0x20
 #endif
 
 madvise: 1
+#if defined(__aarch64__)
+mprotect: arg2 in PROT_READ|PROT_WRITE|PROT_MTE
+#else
 mprotect: arg2 in PROT_READ|PROT_WRITE
+#endif
 munmap: 1
 
 #if defined(__LP64__)
 getuid: 1
 fstat: 1
+#if defined(__aarch64__)
+mmap: arg2 in PROT_READ|PROT_WRITE|PROT_MTE
+#else
 mmap: arg2 in PROT_READ|PROT_WRITE
+#endif
 #else
 getuid32: 1
 fstat64: 1
diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp
index d3f7520..0ad3243 100644
--- a/debuggerd/test_permissive_mte/Android.bp
+++ b/debuggerd/test_permissive_mte/Android.bp
@@ -17,22 +17,28 @@
 }
 
 cc_binary {
-  name: "mte_crash",
-  tidy: false,
-  srcs: ["mte_crash.cpp"],
-  sanitize: {
-    memtag_heap: true,
-    diag: {
-      memtag_heap: true,
+    name: "mte_crash",
+    tidy: false,
+    srcs: ["mte_crash.cpp"],
+    sanitize: {
+        memtag_heap: true,
+        diag: {
+            memtag_heap: true,
+        },
     },
-  },
 }
 
 java_test_host {
     name: "permissive_mte_test",
     libs: ["tradefed"],
-    static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
-    srcs:  ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
+    static_libs: [
+        "frameworks-base-hostutils",
+        "cts-install-lib-host",
+    ],
+    srcs: [
+        "src/**/PermissiveMteTest.java",
+        ":libtombstone_proto-src",
+    ],
     data: [":mte_crash"],
     test_config: "AndroidTest.xml",
     test_suites: ["general-tests"],
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index cf7904f..e2a67a2 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -61,7 +61,6 @@
 
 struct CrashArtifact {
   unique_fd fd;
-  std::optional<std::string> temporary_path;
 
   static CrashArtifact devnull() {
     CrashArtifact result;
@@ -145,16 +144,13 @@
     std::optional<std::string> path;
     result.fd.reset(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0660));
     if (result.fd == -1) {
-      // We might not have O_TMPFILE. Try creating with an arbitrary filename instead.
-      static size_t counter = 0;
-      std::string tmp_filename = StringPrintf(".temporary%zu", counter++);
-      result.fd.reset(openat(dir_fd_, tmp_filename.c_str(),
-                             O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0660));
-      if (result.fd == -1) {
-        PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
-      }
+      PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
+    }
 
-      result.temporary_path = std::move(tmp_filename);
+    // We need to fchmodat after creating to avoid getting the umask applied.
+    std::string fd_path = StringPrintf("/proc/self/fd/%d", result.fd.get());
+    if (fchmodat(dir_fd_, fd_path.c_str(), 0664, 0) != 0) {
+      PLOG(ERROR) << "Failed to make tombstone world-readable";
     }
 
     return std::move(result);
@@ -417,6 +413,7 @@
     return false;
   }
 
+  // This fd is created inside of dirfd in CrashQueue::create_temporary_file.
   std::string fd_path = StringPrintf("/proc/self/fd/%d", fd.get());
   rc = linkat(AT_FDCWD, fd_path.c_str(), dirfd.get(), path.c_str(), AT_SYMLINK_FOLLOW);
   if (rc != 0) {
@@ -471,20 +468,6 @@
       rename_tombstone_fd(crash->output.proto->fd, queue->dir_fd(), *paths.proto);
     }
   }
-
-  // If we don't have O_TMPFILE, we need to clean up after ourselves.
-  if (crash->output.text.temporary_path) {
-    rc = unlinkat(queue->dir_fd().get(), crash->output.text.temporary_path->c_str(), 0);
-    if (rc != 0) {
-      PLOG(ERROR) << "failed to unlink temporary tombstone at " << paths.text;
-    }
-  }
-  if (crash->output.proto && crash->output.proto->temporary_path) {
-    rc = unlinkat(queue->dir_fd().get(), crash->output.proto->temporary_path->c_str(), 0);
-    if (rc != 0) {
-      PLOG(ERROR) << "failed to unlink temporary proto tombstone";
-    }
-  }
 }
 
 static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 90d91a0..1f6bd1a 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -61,6 +61,10 @@
     return block_device_ + " " + std::to_string(physical_sector_);
 }
 
+std::string DmTargetStripe::GetParameterString() const {
+    return "2 " + std::to_string(chunksize) + " " + block_device0_ + " 0 " + block_device1_ + " 0";
+}
+
 DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
                                const std::string& block_device, const std::string& hash_device,
                                uint32_t data_block_size, uint32_t hash_block_size,
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index a0129c2..d043be6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -181,6 +181,13 @@
     ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
 }
 
+TEST_F(DmTest, StripeArgs) {
+    DmTargetStripe target(0, 4096, 1024, "/dev/loop0", "/dev/loop1");
+    ASSERT_EQ(target.name(), "striped");
+    ASSERT_TRUE(target.Valid());
+    ASSERT_EQ(target.GetParameterString(), "2 1024 /dev/loop0 0 /dev/loop1 0");
+}
+
 TEST_F(DmTest, DmVerityArgsAvb2) {
     std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
     std::string algorithm = "sha1";
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 09fe200..97f3c13 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -116,6 +116,24 @@
     uint64_t physical_sector_;
 };
 
+class DmTargetStripe final : public DmTarget {
+  public:
+    DmTargetStripe(uint64_t start, uint64_t length, uint64_t chunksize,
+                   const std::string& block_device0, const std::string& block_device1)
+        : DmTarget(start, length),
+          chunksize(chunksize),
+          block_device0_(block_device0),
+          block_device1_(block_device1) {}
+
+    std::string name() const override { return "striped"; }
+    std::string GetParameterString() const override;
+
+  private:
+    uint64_t chunksize;
+    std::string block_device0_;
+    std::string block_device1_;
+};
+
 class DmTargetVerity final : public DmTarget {
   public:
     DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 30c5135..de2e528 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -603,41 +603,47 @@
 std::vector<CowWriterV3::CompressedBuffer> CowWriterV3::ProcessBlocksWithThreadedCompression(
         const size_t num_blocks, const void* data, CowOperationType type) {
     const size_t num_threads = num_compress_threads_;
-    const size_t blocks_per_thread = DivRoundUp(num_blocks, num_threads);
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
 
+    // We will alternate which thread to send compress work to. E.g. alternate between T1 and T2
+    // until all blocks are processed
     std::vector<CompressedBuffer> compressed_vec;
-    // Submit the blocks per thread. The retrieval of
-    // compressed buffers has to be done in the same order.
-    // We should not poll for completed buffers in a different order as the
-    // buffers are tightly coupled with block ordering.
-    for (size_t i = 0; i < num_threads; i++) {
-        CompressWorker* worker = compress_threads_[i].get();
-        auto blocks_in_batch = std::min(num_blocks - i * blocks_per_thread, blocks_per_thread);
-        // Enqueue the blocks to be compressed for each thread.
-        while (blocks_in_batch) {
-            CompressedBuffer buffer;
+    int iteration = 0;
+    int blocks_to_compress = static_cast<int>(num_blocks);
+    while (blocks_to_compress) {
+        CompressedBuffer buffer;
+        CompressWorker* worker = compress_threads_[iteration % num_threads].get();
 
-            const size_t compression_factor = GetCompressionFactor(blocks_in_batch, type);
-            size_t num_blocks = compression_factor / header_.block_size;
+        const size_t compression_factor = GetCompressionFactor(blocks_to_compress, type);
+        size_t num_blocks = compression_factor / header_.block_size;
 
-            buffer.compression_factor = compression_factor;
-            worker->EnqueueCompressBlocks(iter, compression_factor, 1);
-            compressed_vec.push_back(std::move(buffer));
-            blocks_in_batch -= num_blocks;
-            iter += compression_factor;
-        }
+        worker->EnqueueCompressBlocks(iter, compression_factor, 1);
+        buffer.compression_factor = compression_factor;
+        compressed_vec.push_back(std::move(buffer));
+
+        iteration++;
+        iter += compression_factor;
+        blocks_to_compress -= num_blocks;
     }
 
-    // Fetch compressed buffers from the threads
     std::vector<std::vector<uint8_t>> compressed_buf;
+    std::vector<std::vector<std::vector<uint8_t>>> worker_buffers(num_threads);
     compressed_buf.clear();
     for (size_t i = 0; i < num_threads; i++) {
         CompressWorker* worker = compress_threads_[i].get();
-        if (!worker->GetCompressedBuffers(&compressed_buf)) {
+        if (!worker->GetCompressedBuffers(&worker_buffers[i])) {
             return {};
         }
     }
+    // compressed_vec | CB 1 | CB 2 | CB 3 | CB 4 | <-compressed buffers
+    //                   t1     t2     t1     t2    <- processed by these threads
+    // Ordering is important here. We need to retrieve the compressed data in the same order we
+    // processed it and assume that that we submit data beginning with the first thread and then
+    // round robin the consecutive data calls. We need to Fetch compressed buffers from the threads
+    // via the same ordering
+    for (size_t i = 0; i < compressed_vec.size(); i++) {
+        compressed_buf.emplace_back(worker_buffers[i % num_threads][i / num_threads]);
+    }
 
     if (compressed_vec.size() != compressed_buf.size()) {
         LOG(ERROR) << "Compressed buffer size: " << compressed_buf.size()
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index cbe8285..e6c4de6 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -508,8 +508,6 @@
         // When snapshots are on current slot, we determine the size
         // of block device based on the number of COW operations. We cannot
         // use base device as it will be from older image.
-        size_t num_ops = 0;
-        uint64_t dev_sz = 0;
         unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
         if (fd < 0) {
             PLOG(ERROR) << "Failed to open " << cow_file;
@@ -522,13 +520,18 @@
             return false;
         }
 
+        uint64_t dev_sz = 0;
         const auto& header = reader.GetHeader();
-        if (header.prefix.major_version > 2) {
-            LOG(ERROR) << "COW format not supported";
-            return false;
+        if (header.prefix.major_version == 2) {
+            const size_t num_ops = reader.get_num_total_data_ops();
+            dev_sz = (num_ops * header.block_size);
+        } else {
+            // create_snapshot will skip in-place copy ops. Hence, fetch this
+            // information directly from v3 header.
+            const auto& v3_header = reader.header_v3();
+            dev_sz = v3_header.op_count_max * v3_header.block_size;
         }
-        num_ops = reader.get_num_total_data_ops();
-        dev_sz = (num_ops * header.block_size);
+
         base_sectors = dev_sz >> 9;
     } else {
         // For userspace snapshots, the size of the base device is taken as the
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7273087..9dc8c24 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -115,6 +115,21 @@
             std::string block_device = NextArg();
             return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
                                                            block_device);
+        } else if (target_type == "striped") {
+            if (!HasArgs(3)) {
+                std::cerr << "Expected \"striped\" <block_device0> <block_device1> <chunksize>"
+                          << std::endl;
+                return nullptr;
+            }
+            std::string block_device0 = NextArg();
+            std::string block_device1 = NextArg();
+            uint64_t chunk_size;
+            if (!android::base::ParseUint(NextArg(), &chunk_size)) {
+                std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+                return nullptr;
+            }
+            return std::make_unique<DmTargetStripe>(start_sector, num_sectors, chunk_size,
+                                                    block_device0, block_device1);
         } else if (target_type == "bow") {
             if (!HasArgs(1)) {
                 std::cerr << "Expected \"bow\" <block_device>" << std::endl;
diff --git a/init/Android.bp b/init/Android.bp
index 2d16f60..c3abefe 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -597,7 +597,6 @@
     },
     generated_headers: [
         "generated_stub_builtin_function_map",
-        "generated_android_ids",
     ],
     target: {
         android: {
@@ -613,6 +612,9 @@
     name: "host_init_verifier",
     defaults: ["init_host_defaults"],
     srcs: ["host_init_verifier.cpp"] + init_common_sources + init_host_sources,
+    generated_headers: [
+        "generated_android_ids",
+    ],
 }
 
 cc_library_host_static {
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 54d559b..0239b68 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_CALLSTACK_H
-#define ANDROID_CALLSTACK_H
+#pragma once
 
 #include <memory>
 
@@ -27,14 +26,14 @@
 #include <sys/types.h>
 
 #if !defined(__APPLE__) && !defined(_WIN32)
-# define WEAKS_AVAILABLE 1
+# define CALLSTACK_WEAKS_AVAILABLE 1
 #endif
 #ifndef CALLSTACK_WEAK
-# ifdef WEAKS_AVAILABLE
+# ifdef CALLSTACK_WEAKS_AVAILABLE
 #   define CALLSTACK_WEAK __attribute__((weak))
-# else // !WEAKS_AVAILABLE
+# else // !CALLSTACK_WEAKS_AVAILABLE
 #   define CALLSTACK_WEAK
-# endif // !WEAKS_AVAILABLE
+# endif // !CALLSTACK_WEAKS_AVAILABLE
 #endif // CALLSTACK_WEAK predefined
 
 #ifndef CALLSTACK_ALWAYS_INLINE
@@ -91,7 +90,7 @@
     //
     // DO NOT USE THESE. They will disappear.
     struct StackDeleter {
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
         void operator()(CallStack* stack) {
             deleteStack(stack);
         }
@@ -103,7 +102,7 @@
     typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
 
     // Return current call stack if possible, nullptr otherwise.
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
     static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
         if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
             ALOGW("CallStack::getCurrentInternal not linked, returning null");
@@ -112,13 +111,13 @@
             return getCurrentInternal(ignoreDepth);
         }
     }
-#else // !WEAKS_AVAILABLE
+#else // !CALLSTACK_WEAKS_AVAILABLE
     static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t = 1) {
         return CallStackUPtr(nullptr);
     }
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
 
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
     static void CALLSTACK_ALWAYS_INLINE logStack(const char* logtag,
                                                  CallStack* stack = getCurrent().get(),
                                                  android_LogPriority priority = ANDROID_LOG_DEBUG) {
@@ -135,9 +134,9 @@
                                                  android_LogPriority = ANDROID_LOG_DEBUG) {
         ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
     }
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
 
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
     static String8 CALLSTACK_ALWAYS_INLINE
     stackToString(const char* prefix = nullptr, const CallStack* stack = getCurrent().get()) {
         if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
@@ -146,15 +145,15 @@
             return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
         }
     }
-#else // !WEAKS_AVAILABLE
+#else // !CALLSTACK_WEAKS_AVAILABLE
     static String8 CALLSTACK_ALWAYS_INLINE stackToString(const char* prefix = nullptr,
                                                          const CallStack* = getCurrent().get()) {
         return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
     }
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
 
   private:
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
     static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
     static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
                                                 android_LogPriority priority);
@@ -162,13 +161,13 @@
     // The deleter is only invoked on non-null pointers. Hence it will never be
     // invoked if CallStack is not linked.
     static void CALLSTACK_WEAK deleteStack(CallStack* stack);
-#endif // WEAKS_AVAILABLE
+#endif // CALLSTACK_WEAKS_AVAILABLE
 
     Vector<String8> mFrameLines;
 };
 
 }  // namespace android
 
+#undef CALLSTACK_WEAKS_AVAILABLE
+#undef CALLSTACK_WEAK
 #undef CALLSTACK_ALWAYS_INLINE
-
-#endif // ANDROID_CALLSTACK_H
diff --git a/libvendorsupport/Android.bp b/libvendorsupport/Android.bp
index b4457b1..e87959e 100644
--- a/libvendorsupport/Android.bp
+++ b/libvendorsupport/Android.bp
@@ -34,3 +34,32 @@
         "liblog",
     ],
 }
+
+cc_library_headers {
+    name: "libvendorsupport_llndk_headers",
+    host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    native_bridge_supported: true,
+
+    export_include_dirs: ["include_llndk"],
+    llndk: {
+        llndk_headers: true,
+    },
+
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "apex_inherit",
+
+    system_shared_libs: [],
+    stl: "none",
+
+    // This header library is used for libc and must be available to any sdk
+    // versions.
+    // Setting sdk_version to the lowest version allows the dependencies.
+    sdk_version: "1",
+}
diff --git a/libvendorsupport/include_llndk/android/llndk-versioning.h b/libvendorsupport/include_llndk/android/llndk-versioning.h
new file mode 100644
index 0000000..7c408c9
--- /dev/null
+++ b/libvendorsupport/include_llndk/android/llndk-versioning.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+#if defined(__ANDROID_VENDOR__)
+
+// LLNDK (https://source.android.com/docs/core/architecture/vndk/build-system#ll-ndk) is similar to
+// NDK, but uses its own versioning of YYYYMM format for vendor builds. The LLNDK symbols are
+// enabled when the vendor api level is equal to or newer than the ro.board.api_level.
+#define __INTRODUCED_IN_LLNDK(vendor_api_level)                                             \
+    _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wgcc-compat\"")   \
+            __attribute__((enable_if(                                                       \
+                    __ANDROID_VENDOR_API__ >= vendor_api_level,                             \
+                    "available in vendor API level " #vendor_api_level " that "             \
+                    "is newer than the current vendor API level. Guard the API "            \
+                    "call with '#if (__ANDROID_VENDOR_API__ >= " #vendor_api_level ")'."))) \
+            _Pragma("clang diagnostic pop")
+
+// For the vendor libraries, __INTRODUCED_IN must be ignored because they are only for NDKs but not
+// for LLNDKs.
+#undef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+
+#else  // __ANDROID_VENDOR__
+
+// For non-vendor libraries, __INTRODUCED_IN_LLNDK must be ignored because it must not change
+// symbols of NDK or the system side of the treble boundary.
+#define __INTRODUCED_IN_LLNDK(vendor_api_level)
+
+#endif  // __ANDROID_VENDOR__
+
+__END_DECLS
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 164d411..8a2ed9f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -709,7 +709,7 @@
 
     # Start tombstoned early to be able to store tombstones.
     mkdir /data/anr 0775 system system encryption=Require
-    mkdir /data/tombstones 0771 system system encryption=Require
+    mkdir /data/tombstones 0775 system system encryption=Require
     mkdir /data/vendor/tombstones 0771 root root
     mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
     start tombstoned