Merge "healthd: Add support for the new Health HAL v3 properties." into main
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 3563436..0899111 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -317,6 +317,7 @@
       process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
       process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
       process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
+      process_info->scudo_stack_depot_size = crash_info->data.d.scudo_stack_depot_size;
       process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
       process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer;
       process_info->scudo_ring_buffer_size = crash_info->data.d.scudo_ring_buffer_size;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 01365f2..ea07ce2 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -395,6 +395,7 @@
     ASSERT_SAME_OFFSET(scudo_region_info, scudo_region_info);
     ASSERT_SAME_OFFSET(scudo_ring_buffer, scudo_ring_buffer);
     ASSERT_SAME_OFFSET(scudo_ring_buffer_size, scudo_ring_buffer_size);
+    ASSERT_SAME_OFFSET(scudo_stack_depot_size, scudo_stack_depot_size);
     ASSERT_SAME_OFFSET(recoverable_gwp_asan_crash, recoverable_gwp_asan_crash);
 #undef ASSERT_SAME_OFFSET
 
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index ebb5372..de12fc6 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -44,6 +44,7 @@
   const char* scudo_region_info;
   const char* scudo_ring_buffer;
   size_t scudo_ring_buffer_size;
+  size_t scudo_stack_depot_size;
   bool recoverable_gwp_asan_crash;
 };
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 5a2a7ab..075b12c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -51,6 +51,7 @@
   uintptr_t scudo_region_info = 0;
   uintptr_t scudo_ring_buffer = 0;
   size_t scudo_ring_buffer_size = 0;
+  size_t scudo_stack_depot_size = 0;
 
   bool has_fault_address = false;
   uintptr_t untagged_fault_address = 0;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index ea8dff4..cff43f8 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -41,8 +41,6 @@
     return;
   }
 
-  auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
-                                       __scudo_get_stack_depot_size());
   auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
                                        __scudo_get_region_info_size());
   std::unique_ptr<char[]> ring_buffer;
@@ -50,7 +48,12 @@
     ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
                                     process_info.scudo_ring_buffer_size);
   }
-  if (!stack_depot || !region_info) {
+  std::unique_ptr<char[]> stack_depot;
+  if (process_info.scudo_stack_depot_size != 0) {
+    stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+                                    process_info.scudo_stack_depot_size);
+  }
+  if (!region_info) {
     return;
   }
 
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 212d6dc..793428a 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -99,6 +99,7 @@
   uintptr_t scudo_region_info;
   uintptr_t scudo_ring_buffer;
   size_t scudo_ring_buffer_size;
+  size_t scudo_stack_depot_size;
   bool recoverable_gwp_asan_crash;
 };
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index ac2a20f..1bc7b75 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -402,7 +402,7 @@
         transport = open_device(device.c_str(), false, false);
 
         if (print) {
-            PrintDevice(device.c_str(), transport ? "offline" : "fastboot");
+            PrintDevice(device.c_str(), transport ? "fastboot" : "offline");
         }
 
         if (transport) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index 2c4d40f..b0eb723 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -14,7 +14,6 @@
 // limitations under the License.
 //
 
-#include <inttypes.h>
 #include <libsnapshot/cow_format.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 27accdc..44b7344 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -97,7 +97,7 @@
     options.op_count_max = 20;
     auto writer = CreateCowWriter(3, options, GetCowFd());
     ASSERT_FALSE(writer->AddZeroBlocks(1, 21));
-    ASSERT_FALSE(writer->AddZeroBlocks(1, 1));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, 20));
     std::string data = "This is some data, believe it";
     data.resize(options.block_size, '\0');
 
@@ -184,7 +184,7 @@
     std::string data;
     data.resize(options.block_size * 5);
     for (int i = 0; i < data.size(); i++) {
-        data[i] = char(rand() % 256);
+        data[i] = static_cast<char>('A' + i / options.block_size);
     }
 
     ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
@@ -205,19 +205,20 @@
     ASSERT_FALSE(iter->AtEnd());
 
     size_t i = 0;
-    std::string sink(data.size(), '\0');
 
     while (!iter->AtEnd()) {
         auto op = iter->Get();
+        std::string sink(options.block_size, '\0');
         ASSERT_EQ(op->type(), kCowReplaceOp);
         ASSERT_EQ(op->data_length, options.block_size);
         ASSERT_EQ(op->new_block, 5 + i);
-        ASSERT_TRUE(
-                ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+        ASSERT_TRUE(ReadData(reader, op, sink.data(), options.block_size));
+        ASSERT_EQ(std::string_view(sink),
+                  std::string_view(data).substr(i * options.block_size, options.block_size))
+                << " readback data for " << i << "th block does not match";
         iter->Next();
         i++;
     }
-    ASSERT_EQ(sink, data);
 
     ASSERT_EQ(i, 5);
 }
@@ -372,41 +373,33 @@
     ASSERT_NE(iter, nullptr);
     ASSERT_FALSE(iter->AtEnd());
 
-    size_t i = 0;
-
-    while (i < 5) {
+    for (size_t i = 0; i < 5; i++) {
         auto op = iter->Get();
         ASSERT_EQ(op->type(), kCowZeroOp);
         ASSERT_EQ(op->new_block, 10 + i);
         iter->Next();
-        i++;
     }
-    i = 0;
-    while (i < 5) {
+    for (size_t i = 0; i < 5; i++) {
         auto op = iter->Get();
         ASSERT_EQ(op->type(), kCowCopyOp);
         ASSERT_EQ(op->new_block, 15 + i);
         ASSERT_EQ(op->source(), 3 + i);
         iter->Next();
-        i++;
     }
-    i = 0;
     std::string sink(data.size(), '\0');
 
-    while (i < 5) {
+    for (size_t i = 0; i < 5; i++) {
         auto op = iter->Get();
         ASSERT_EQ(op->type(), kCowReplaceOp);
         ASSERT_EQ(op->new_block, 18 + i);
-        ASSERT_TRUE(
-                ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+        ASSERT_EQ(reader.ReadData(op, sink.data() + (i * options.block_size), options.block_size),
+                  options.block_size);
         iter->Next();
-        i++;
     }
     ASSERT_EQ(sink, data);
 
-    i = 0;
     std::fill(sink.begin(), sink.end(), '\0');
-    while (i < 5) {
+    for (size_t i = 0; i < 5; i++) {
         auto op = iter->Get();
         ASSERT_EQ(op->type(), kCowXorOp);
         ASSERT_EQ(op->new_block, 50 + i);
@@ -414,7 +407,6 @@
         ASSERT_TRUE(
                 ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
         iter->Next();
-        i++;
     }
     ASSERT_EQ(sink, data);
 }
@@ -671,5 +663,25 @@
     ASSERT_LE(writer.GetCowSize(), cow_size);
 }
 
+TEST_F(CowTestV3, CopyOpMany) {
+    CowOptions options;
+    options.op_count_max = 100;
+    CowWriterV3 writer(options, GetCowFd());
+    writer.Initialize();
+    ASSERT_TRUE(writer.AddCopy(100, 50, 50));
+    ASSERT_TRUE(writer.AddCopy(150, 100, 50));
+    ASSERT_TRUE(writer.Finalize());
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(GetCowFd()));
+    auto it = reader.GetOpIter();
+    for (size_t i = 0; i < 100; i++) {
+        ASSERT_FALSE(it->AtEnd()) << " op iterator ended at " << i;
+        const auto op = *it->Get();
+        ASSERT_EQ(op.type(), kCowCopyOp);
+        ASSERT_EQ(op.new_block, 100 + i);
+        it->Next();
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 07f6f00..4df0e76 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -113,7 +113,13 @@
     }
 
     compression_.algorithm = *algorithm;
-    compressor_ = ICompressor::Create(compression_, header_.block_size);
+    if (compression_.algorithm != kCowCompressNone) {
+        compressor_ = ICompressor::Create(compression_, header_.block_size);
+        if (compressor_ == nullptr) {
+            LOG(ERROR) << "Failed to create compressor for " << compression_.algorithm;
+            return false;
+        }
+    }
     return true;
 }
 
@@ -207,14 +213,15 @@
 }
 
 bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+    std::vector<CowOperationV3> ops(num_blocks);
     for (size_t i = 0; i < num_blocks; i++) {
-        CowOperationV3 op{};
+        CowOperationV3& op = ops[i];
         op.set_type(kCowCopyOp);
         op.new_block = new_block + i;
         op.set_source(old_block + i);
-        if (!WriteOperation(op)) {
-            return false;
-        }
+    }
+    if (!WriteOperation({ops.data(), ops.size()}, {})) {
+        return false;
     }
 
     return true;
@@ -231,12 +238,37 @@
 
 bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
                              uint64_t old_block, uint16_t offset, CowOperationType type) {
+    if (compression_.algorithm != kCowCompressNone && compressor_ == nullptr) {
+        LOG(ERROR) << "Compression algorithm is " << compression_.algorithm
+                   << " but compressor is uninitialized.";
+        return false;
+    }
     const size_t num_blocks = (size / header_.block_size);
+    if (compression_.algorithm == kCowCompressNone) {
+        std::vector<CowOperationV3> ops(num_blocks);
+        for (size_t i = 0; i < num_blocks; i++) {
+            CowOperation& op = ops[i];
+            op.new_block = new_block_start + i;
+
+            op.set_type(type);
+            if (type == kCowXorOp) {
+                op.set_source((old_block + i) * header_.block_size + offset);
+            } else {
+                op.set_source(next_data_pos_ + header_.block_size * i);
+            }
+            op.data_length = header_.block_size;
+        }
+        return WriteOperation({ops.data(), ops.size()},
+                              {reinterpret_cast<const uint8_t*>(data), size});
+    }
+
+    const auto saved_op_count = header_.op_count;
+    const auto saved_data_pos = next_data_pos_;
     for (size_t i = 0; i < num_blocks; i++) {
         const uint8_t* const iter =
                 reinterpret_cast<const uint8_t*>(data) + (header_.block_size * i);
 
-        CowOperation op = {};
+        CowOperation op{};
         op.new_block = new_block_start + i;
 
         op.set_type(type);
@@ -245,25 +277,21 @@
         } else {
             op.set_source(next_data_pos_);
         }
-        std::basic_string<uint8_t> compressed_data;
         const void* out_data = iter;
 
         op.data_length = header_.block_size;
 
-        if (compression_.algorithm) {
-            if (!compressor_) {
-                PLOG(ERROR) << "Compressor not initialized";
-                return false;
-            }
-            compressed_data = compressor_->Compress(out_data, header_.block_size);
-            if (compressed_data.size() < op.data_length) {
-                out_data = compressed_data.data();
-                op.data_length = compressed_data.size();
-            }
+        const std::basic_string<uint8_t> compressed_data =
+                compressor_->Compress(out_data, header_.block_size);
+        if (compressed_data.size() < op.data_length) {
+            out_data = compressed_data.data();
+            op.data_length = compressed_data.size();
         }
         if (!WriteOperation(op, out_data, op.data_length)) {
             PLOG(ERROR) << "AddRawBlocks with compression: write failed. new block: "
                         << new_block_start << " compression: " << compression_.algorithm;
+            header_.op_count = saved_op_count;
+            next_data_pos_ = saved_data_pos;
             return false;
         }
     }
@@ -272,13 +300,14 @@
 }
 
 bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+    std::vector<CowOperationV3> ops(num_blocks);
     for (uint64_t i = 0; i < num_blocks; i++) {
-        CowOperationV3 op{};
+        CowOperationV3& op = ops[i];
         op.set_type(kCowZeroOp);
         op.new_block = new_block_start + i;
-        if (!WriteOperation(op)) {
-            return false;
-        }
+    }
+    if (!WriteOperation({ops.data(), ops.size()}, {})) {
+        return false;
     }
     return true;
 }
@@ -324,43 +353,48 @@
     return true;
 }
 
-bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
+bool CowWriterV3::WriteOperation(std::basic_string_view<CowOperationV3> ops,
+                                 std::basic_string_view<uint8_t> data) {
     if (IsEstimating()) {
-        header_.op_count++;
+        header_.op_count += ops.size();
         if (header_.op_count > header_.op_count_max) {
             // If we increment op_count_max, the offset of data section would
             // change. So need to update |next_data_pos_|
             next_data_pos_ += (header_.op_count - header_.op_count_max) * sizeof(CowOperationV3);
             header_.op_count_max = header_.op_count;
         }
-        next_data_pos_ += op.data_length;
+        next_data_pos_ += data.size();
         return true;
     }
 
-    if (header_.op_count + 1 > header_.op_count_max) {
-        LOG(ERROR) << "Maximum number of ops reached: " << header_.op_count_max;
+    if (header_.op_count + ops.size() > header_.op_count_max) {
+        LOG(ERROR) << "Current op count " << header_.op_count << ", attempting to write "
+                   << ops.size() << " ops will exceed the max of " << header_.op_count_max;
         return false;
     }
 
     const off_t offset = GetOpOffset(header_.op_count, header_);
-    if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) {
-        PLOG(ERROR) << "write failed for " << op << " at " << offset;
+    if (!android::base::WriteFullyAtOffset(fd_, ops.data(), ops.size() * sizeof(ops[0]), offset)) {
+        PLOG(ERROR) << "Write failed for " << ops.size() << " ops at " << offset;
         return false;
     }
-    if (data && size > 0) {
-        if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
-            PLOG(ERROR) << "write failed for data of size: " << size
+    if (!data.empty()) {
+        if (!android::base::WriteFullyAtOffset(fd_, data.data(), data.size(), next_data_pos_)) {
+            PLOG(ERROR) << "write failed for data of size: " << data.size()
                         << " at offset: " << next_data_pos_;
             return false;
         }
     }
-    header_.op_count++;
-    next_data_pos_ += op.data_length;
-    next_op_pos_ += sizeof(CowOperationV3);
+    header_.op_count += ops.size();
+    next_data_pos_ += data.size();
 
     return true;
 }
 
+bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
+    return WriteOperation({&op, 1}, {reinterpret_cast<const uint8_t*>(data), size});
+}
+
 bool CowWriterV3::Finalize() {
     CHECK_GE(header_.prefix.header_size, sizeof(CowHeaderV3));
     CHECK_LE(header_.prefix.header_size, sizeof(header_));
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index 340218f..02b4e61 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <android-base/logging.h>
+#include <string_view>
 
 #include "writer_base.h"
 
@@ -44,6 +45,8 @@
     bool ParseOptions();
     bool OpenForWrite();
     bool OpenForAppend(uint64_t label);
+    bool WriteOperation(std::basic_string_view<CowOperationV3> op,
+                        std::basic_string_view<uint8_t> data);
     bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0);
     bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
                     uint16_t offset, CowOperationType type);
@@ -59,7 +62,6 @@
     // Resume points contain a laebl + cow_op_index.
     std::shared_ptr<std::vector<ResumePoint>> resume_points_;
 
-    uint64_t next_op_pos_ = 0;
     uint64_t next_data_pos_ = 0;
     std::vector<std::basic_string<uint8_t>> compressed_buf_;