Merge "Always provide metadata blob for hdr frames"
diff --git a/.clang-format b/.clang-format
index 6725a1f..f63f670 100644
--- a/.clang-format
+++ b/.clang-format
@@ -12,3 +12,6 @@
 PenaltyBreakBeforeFirstCallParameter: 100000
 SpacesBeforeTrailingComments: 1
 IncludeBlocks: Preserve
+
+DerivePointerAlignment: false
+PointerAlignment: Left
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 28369d6..e70a98d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -517,6 +517,7 @@
     visibility: [
         ":__subpackages__",
         "//packages/modules/Virtualization:__subpackages__",
+        "//device/google/cuttlefish/shared/minidroid:__subpackages__",
     ],
 }
 
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 5e725a9..da5affb 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -407,8 +407,10 @@
         AutoMutex lock(e->mLock);
         if (mRecordingOn) {
             Parcel emptyReply;
+            timespec ts;
+            timespec_get(&ts, TIME_UTC);
             auto transaction =
-                    android::binder::debug::RecordedTransaction::fromDetails(code, flags, data,
+                    android::binder::debug::RecordedTransaction::fromDetails(code, flags, ts, data,
                                                                              reply ? *reply
                                                                                    : emptyReply,
                                                                              err);
diff --git a/libs/binder/BinderRecordReplay.cpp b/libs/binder/BinderRecordReplay.cpp
index 9463785..58bb106 100644
--- a/libs/binder/BinderRecordReplay.cpp
+++ b/libs/binder/BinderRecordReplay.cpp
@@ -18,6 +18,7 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/BinderRecordReplay.h>
+#include <sys/mman.h>
 #include <algorithm>
 
 using android::Parcel;
@@ -42,34 +43,38 @@
 //
 // A RecordedTransaction is written to a file as a sequence of Chunks.
 //
-// A Chunk consists of a ChunkDescriptor, Data, and Padding.
+// A Chunk consists of a ChunkDescriptor, Data, Padding, and a Checksum.
 //
-// Data and Padding may each be zero-length as specified by the
-// ChunkDescriptor.
+// The ChunkDescriptor identifies the type of Data in the chunk, and the size
+// of the Data.
 //
-// The ChunkDescriptor identifies the type of data in the chunk, the size of
-// the data in bytes, and the number of zero-bytes padding to land on an
-// 8-byte boundary by the end of the Chunk.
+// The Data may be any uint32 number of bytes in length in [0-0xfffffff0].
+//
+// Padding is between [0-7] zero-bytes after the Data such that the Chunk ends
+// on an 8-byte boundary. The ChunkDescriptor's dataSize does not include the
+// size of Padding.
+//
+// The checksum is a 64-bit wide XOR of all previous data from the start of the
+// ChunkDescriptor to the end of Padding.
 //
 // ┌───────────────────────────┐
 // │Chunk                      │
+// │┌────────────────────────┐ │
+// ││ChunkDescriptor         │ │
+// ││┌───────────┬──────────┐│ │
+// │││chunkType  │dataSize  ├┼─┼─┐
+// │││uint32_t   │uint32_t  ││ │ │
+// ││└───────────┴──────────┘│ │ │
+// │└────────────────────────┘ │ │
+// │┌─────────────────────────┐│ │
+// ││Data                     ││ │
+// ││bytes * dataSize         │◀─┘
+// ││   ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤│
+// ││           Padding       ││
+// │└───┴─────────────────────┘│
 // │┌─────────────────────────┐│
-// ││ChunkDescriptor          ││
-// ││┌───────────┬───────────┐││
-// │││chunkType  │paddingSize│││
-// │││uint32_t   │uint32_t   ├┼┼───┐
-// ││├───────────┴───────────┤││   │
-// │││dataSize               │││   │
-// │││uint64_t               ├┼┼─┐ │
-// ││└───────────────────────┘││ │ │
-// │└─────────────────────────┘│ │ │
-// │┌─────────────────────────┐│ │ │
-// ││Data                     ││ │ │
-// ││bytes * dataSize         │◀─┘ │
-// │└─────────────────────────┘│   │
-// │┌─────────────────────────┐│   │
-// ││Padding                  ││   │
-// ││bytes * paddingSize      │◀───┘
+// ││checksum                 ││
+// ││uint64_t                 ││
 // │└─────────────────────────┘│
 // └───────────────────────────┘
 //
@@ -85,42 +90,48 @@
 // ║      End Chunk       ║
 // ╚══════════════════════╝
 //
-// On reading a RecordedTransaction, an unrecognized chunk is skipped using
-// the size information in the ChunkDescriptor. Chunks are read and either
-// assimilated or skipped until an End Chunk is encountered. This has three
-// notable implications:
+// On reading a RecordedTransaction, an unrecognized chunk is checksummed
+// then skipped according to size information in the ChunkDescriptor. Chunks
+// are read and either assimilated or skipped until an End Chunk is
+// encountered. This has three notable implications:
 //
 // 1. Older and newer implementations should be able to read one another's
 //    Transactions, though there will be loss of information.
-// 2. With the exception of the End Chunk, Chunks can appear in any
-//    order and even repeat, though this is not recommended.
+// 2. With the exception of the End Chunk, Chunks can appear in any order
+//    and even repeat, though this is not recommended.
 // 3. If any Chunk is repeated, old values will be overwritten by versions
 //    encountered later in the file.
 //
 // No effort is made to ensure the expected chunks are present. A single
-// End Chunk may therefore produce a empty, meaningless RecordedTransaction.
+// End Chunk may therefore produce an empty, meaningless RecordedTransaction.
 
 RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
-    mHeader = {t.getCode(), t.getFlags(), t.getReturnedStatus(), t.getVersion()};
+    mHeader = t.mHeader;
     mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize());
     mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize());
 }
 
 std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags,
+                                                                    timespec timestamp,
                                                                     const Parcel& dataParcel,
                                                                     const Parcel& replyParcel,
                                                                     status_t err) {
     RecordedTransaction t;
-    t.mHeader = {code, flags, static_cast<int32_t>(err),
-                 dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)};
+    t.mHeader = {code,
+                 flags,
+                 static_cast<int32_t>(err),
+                 dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0),
+                 static_cast<int64_t>(timestamp.tv_sec),
+                 static_cast<int32_t>(timestamp.tv_nsec),
+                 0};
 
     if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set sent parcel data.";
+        LOG(ERROR) << "Failed to set sent parcel data.";
         return std::nullopt;
     }
 
     if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set reply parcel data.";
+        LOG(ERROR) << "Failed to set reply parcel data.";
         return std::nullopt;
     }
 
@@ -128,93 +139,111 @@
 }
 
 enum {
-    HEADER_CHUNK = 0x00000001,
-    DATA_PARCEL_CHUNK = 0x00000002,
-    REPLY_PARCEL_CHUNK = 0x00000003,
-    INVALID_CHUNK = 0x00fffffe,
+    HEADER_CHUNK = 1,
+    DATA_PARCEL_CHUNK = 2,
+    REPLY_PARCEL_CHUNK = 3,
     END_CHUNK = 0x00ffffff,
 };
 
 struct ChunkDescriptor {
     uint32_t chunkType = 0;
-    uint32_t padding = 0;
     uint32_t dataSize = 0;
-    uint32_t reserved = 0; // Future checksum
 };
+static_assert(sizeof(ChunkDescriptor) % 8 == 0);
 
-static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut) {
+constexpr uint32_t kMaxChunkDataSize = 0xfffffff0;
+typedef uint64_t transaction_checksum_t;
+
+static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut,
+                                             transaction_checksum_t* sum) {
     if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) {
-        LOG(INFO) << "Failed to read Chunk Descriptor from fd " << fd.get();
+        LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get();
         return android::UNKNOWN_ERROR;
     }
-    if (PADDING8(chunkOut->dataSize) != chunkOut->padding) {
-        chunkOut->chunkType = INVALID_CHUNK;
-        LOG(INFO) << "Chunk data and padding sizes do not align." << fd.get();
-        return android::BAD_VALUE;
-    }
+
+    *sum ^= *reinterpret_cast<transaction_checksum_t*>(chunkOut);
     return android::NO_ERROR;
 }
 
 std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
     RecordedTransaction t;
     ChunkDescriptor chunk;
-
+    const long pageSize = sysconf(_SC_PAGE_SIZE);
     do {
-        if (NO_ERROR != readChunkDescriptor(fd, &chunk)) {
-            LOG(INFO) << "Failed to read chunk descriptor.";
+        transaction_checksum_t checksum = 0;
+        if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
+            LOG(ERROR) << "Failed to read chunk descriptor.";
             return std::nullopt;
         }
+        off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+        off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
+        off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
+
+        if (chunk.dataSize > kMaxChunkDataSize) {
+            LOG(ERROR) << "Chunk data exceeds maximum size.";
+            return std::nullopt;
+        }
+
+        size_t chunkPayloadSize =
+                chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
+
+        if (PADDING8(chunkPayloadSize) != 0) {
+            LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
+            return std::nullopt;
+        }
+
+        transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>(
+                mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED,
+                     fd.get(), mmapPageAlignedStart));
+        payloadMap += mmapPayloadStartOffset /
+                sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
+                                                // page-alignment
+        if (payloadMap == MAP_FAILED) {
+            LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " "
+                       << strerror(errno);
+            return std::nullopt;
+        }
+        for (size_t checksumIndex = 0;
+             checksumIndex < chunkPayloadSize / sizeof(transaction_checksum_t); checksumIndex++) {
+            checksum ^= payloadMap[checksumIndex];
+        }
+        if (checksum != 0) {
+            LOG(ERROR) << "Checksum failed.";
+            return std::nullopt;
+        }
+        lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+
         switch (chunk.chunkType) {
             case HEADER_CHUNK: {
                 if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
-                    LOG(INFO) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
-                              << sizeof(TransactionHeader) << ".";
+                    LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
+                               << sizeof(TransactionHeader) << ".";
                     return std::nullopt;
                 }
-                if (!android::base::ReadFully(fd, &t.mHeader, chunk.dataSize)) {
-                    LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get();
-                    return std::nullopt;
-                }
+                t.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap);
                 break;
             }
             case DATA_PARCEL_CHUNK: {
-                std::vector<uint8_t> bytes;
-                bytes.resize(chunk.dataSize);
-                if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) {
-                    LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get();
+                if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
+                                    chunk.dataSize) != android::NO_ERROR) {
+                    LOG(ERROR) << "Failed to set sent parcel data.";
                     return std::nullopt;
                 }
-                if (t.mSent.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) {
-                    LOG(INFO) << "Failed to set sent parcel data.";
-                    return std::nullopt;
-                }
-                lseek(fd.get(), chunk.padding, SEEK_CUR);
                 break;
             }
             case REPLY_PARCEL_CHUNK: {
-                std::vector<uint8_t> bytes;
-                bytes.resize(chunk.dataSize);
-                if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) {
-                    LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get();
+                if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
+                                     chunk.dataSize) != android::NO_ERROR) {
+                    LOG(ERROR) << "Failed to set reply parcel data.";
                     return std::nullopt;
                 }
-                if (t.mReply.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) {
-                    LOG(INFO) << "Failed to set reply parcel data.";
-                    return std::nullopt;
-                }
-                lseek(fd.get(), chunk.padding, SEEK_CUR);
                 break;
             }
-            case INVALID_CHUNK:
-                LOG(INFO) << "Invalid chunk.";
-                return std::nullopt;
             case END_CHUNK:
-                LOG(INFO) << "Read end chunk";
-                FALLTHROUGH_INTENDED;
-            default:
-                // Unrecognized or skippable chunk
-                lseek(fd.get(), chunk.dataSize + chunk.padding, SEEK_CUR);
                 break;
+            default:
+                LOG(INFO) << "Unrecognized chunk.";
+                continue;
         }
     } while (chunk.chunkType != END_CHUNK);
 
@@ -223,36 +252,37 @@
 
 android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
                                                   size_t byteCount, const uint8_t* data) const {
-    // Write Chunk Descriptor
-    // - Chunk Type
-    if (!android::base::WriteFully(fd, &chunkType, sizeof(uint32_t))) {
-        LOG(INFO) << "Failed to write chunk header to fd " << fd.get();
-        return UNKNOWN_ERROR;
+    if (byteCount > kMaxChunkDataSize) {
+        LOG(ERROR) << "Chunk data exceeds maximum size";
+        return BAD_VALUE;
     }
-    // - Chunk Data Padding Size
-    uint32_t additionalPaddingCount = static_cast<uint32_t>(PADDING8(byteCount));
-    if (!android::base::WriteFully(fd, &additionalPaddingCount, sizeof(uint32_t))) {
-        LOG(INFO) << "Failed to write chunk padding size to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    // - Chunk Data Size
-    uint64_t byteCountToWrite = (uint64_t)byteCount;
-    if (!android::base::WriteFully(fd, &byteCountToWrite, sizeof(uint64_t))) {
-        LOG(INFO) << "Failed to write chunk size to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    if (byteCount == 0) {
-        return NO_ERROR;
+    ChunkDescriptor descriptor = {.chunkType = chunkType,
+                                  .dataSize = static_cast<uint32_t>(byteCount)};
+    // Prepare Chunk content as byte *
+    const std::byte* descriptorBytes = reinterpret_cast<const std::byte*>(&descriptor);
+    const std::byte* dataBytes = reinterpret_cast<const std::byte*>(data);
+
+    // Add Chunk to intermediate buffer, except checksum
+    std::vector<std::byte> buffer;
+    buffer.insert(buffer.end(), descriptorBytes, descriptorBytes + sizeof(ChunkDescriptor));
+    buffer.insert(buffer.end(), dataBytes, dataBytes + byteCount);
+    std::byte zero{0};
+    buffer.insert(buffer.end(), PADDING8(byteCount), zero);
+
+    // Calculate checksum from buffer
+    transaction_checksum_t* checksumData = reinterpret_cast<transaction_checksum_t*>(buffer.data());
+    transaction_checksum_t checksumValue = 0;
+    for (size_t idx = 0; idx < (buffer.size() / sizeof(transaction_checksum_t)); idx++) {
+        checksumValue ^= checksumData[idx];
     }
 
-    if (!android::base::WriteFully(fd, data, byteCount)) {
-        LOG(INFO) << "Failed to write chunk data to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
+    // Write checksum to buffer
+    std::byte* checksumBytes = reinterpret_cast<std::byte*>(&checksumValue);
+    buffer.insert(buffer.end(), checksumBytes, checksumBytes + sizeof(transaction_checksum_t));
 
-    const uint8_t zeros[7] = {0};
-    if (!android::base::WriteFully(fd, zeros, additionalPaddingCount)) {
-        LOG(INFO) << "Failed to write chunk padding to fd " << fd.get();
+    // Write buffer to file
+    if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
+        LOG(ERROR) << "Failed to write chunk fd " << fd.get();
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
@@ -262,19 +292,19 @@
     if (NO_ERROR !=
         writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader),
                    reinterpret_cast<const uint8_t*>(&mHeader))) {
-        LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get();
+        LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
     if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) {
-        LOG(INFO) << "Failed to write sent Parcel to fd " << fd.get();
+        LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
     if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) {
-        LOG(INFO) << "Failed to write reply Parcel to fd " << fd.get();
+        LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
     if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
-        LOG(INFO) << "Failed to write end chunk to fd " << fd.get();
+        LOG(ERROR) << "Failed to write end chunk to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
@@ -292,6 +322,12 @@
     return mHeader.statusReturned;
 }
 
+timespec RecordedTransaction::getTimestamp() const {
+    time_t sec = mHeader.timestampSeconds;
+    int32_t nsec = mHeader.timestampNanoseconds;
+    return (timespec){.tv_sec = sec, .tv_nsec = nsec};
+}
+
 uint32_t RecordedTransaction::getVersion() const {
     return mHeader.version;
 }
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6d64e1e..da58251 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1221,6 +1221,10 @@
         return NO_ERROR;
     }
 
+    ALOGE_IF(mProcess->mDriverFD >= 0,
+             "Driver returned error (%s). This is a bug in either libbinder or the driver. This "
+             "thread's connection to %s will no longer work.",
+             statusToString(err).c_str(), mProcess->mDriverName.c_str());
     return err;
 }
 
diff --git a/libs/binder/include/binder/BinderRecordReplay.h b/libs/binder/include/binder/BinderRecordReplay.h
index 609e5be..ff983f0 100644
--- a/libs/binder/include/binder/BinderRecordReplay.h
+++ b/libs/binder/include/binder/BinderRecordReplay.h
@@ -34,8 +34,8 @@
     static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd);
     // Filled with the arguments.
     static std::optional<RecordedTransaction> fromDetails(uint32_t code, uint32_t flags,
-                                                          const Parcel& data, const Parcel& reply,
-                                                          status_t err);
+                                                          timespec timestamp, const Parcel& data,
+                                                          const Parcel& reply, status_t err);
     RecordedTransaction(RecordedTransaction&& t) noexcept;
 
     [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const;
@@ -43,6 +43,7 @@
     uint32_t getCode() const;
     uint32_t getFlags() const;
     int32_t getReturnedStatus() const;
+    timespec getTimestamp() const;
     uint32_t getVersion() const;
     const Parcel& getDataParcel() const;
     const Parcel& getReplyParcel() const;
@@ -60,9 +61,12 @@
         uint32_t flags = 0;
         int32_t statusReturned = 0;
         uint32_t version = 0; // !0 iff Rpc
+        int64_t timestampSeconds = 0;
+        int32_t timestampNanoseconds = 0;
+        int32_t reserved = 0;
     };
 #pragma clang diagnostic pop
-    static_assert(sizeof(TransactionHeader) == 16);
+    static_assert(sizeof(TransactionHeader) == 32);
     static_assert(sizeof(TransactionHeader) % 8 == 0);
 
     TransactionHeader mHeader;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2af512e..c78f870 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -198,7 +198,10 @@
 {
     const sp<IServiceManager> sm = defaultServiceManager();
     if (sm != nullptr) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         *outService = interface_cast<INTERFACE>(sm->getService(name));
+#pragma clang diagnostic pop // getService deprecation
         if ((*outService) != nullptr) return NO_ERROR;
     }
     return NAME_NOT_FOUND;
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 9896fd7..08d8e43 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -42,6 +42,7 @@
     android::status_t writeToParcel(android::Parcel* parcel) const override;
     android::status_t readFromParcel(const android::Parcel* parcel) override;
 
+    inline std::string toString() const { return "ParcelFileDescriptor:" + std::to_string(get()); }
     inline bool operator!=(const ParcelFileDescriptor& rhs) const {
         return mFd.get() != rhs.mFd.get();
     }
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 88790a8..40fd30a 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -111,6 +111,11 @@
 
     Stability getStability() const override { return mStability; }
 
+    inline std::string toString() const {
+        return "ParcelableHolder:" +
+                (mParcelableName ? std::string(String8(mParcelableName.value()).c_str())
+                                 : "<parceled>");
+    }
     inline bool operator!=(const ParcelableHolder& rhs) const {
         return this != &rhs;
     }
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 2a00736..9b0d222 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -49,12 +49,17 @@
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #define HAS_NDK_INTERFACE
-#else
+#endif
+
+// TODO: some things include libbinder without having access to libbase. This is
+// due to frameworks/native/include, which symlinks to libbinder headers, so even
+// though we don't use it here, we detect a different header, so that we are more
+// confident libbase will be included
+#if __has_include(<binder/RpcSession.h>)
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <binder/ParcelableHolder.h>
-#endif  //_has_include
+#define HAS_CPP_INTERFACE
+#endif
 
 namespace android {
 namespace internal {
@@ -104,10 +109,12 @@
             IsInstantiationOf<_U, sp>::value ||  // for IBinder and interface types in the C++
                                                  // backend
 #endif
-                    IsInstantiationOf<_U, std::optional>::value ||  // for @nullable types in the
-                                                                    // C++/NDK backends
-                    IsInstantiationOf<_U, std::shared_ptr>::value,  // for interface types in the
-                                                                    // NDK backends
+                    IsInstantiationOf<_U, std::optional>::value ||    // for @nullable types in the
+                                                                      // C++/NDK backends
+                    IsInstantiationOf<_U, std::unique_ptr>::value ||  // for @nullable(heap=true)
+                                                                      // in C++/NDK backends
+                    IsInstantiationOf<_U, std::shared_ptr>::value,    // for interface types in the
+                                                                      // NDK backends
 
             std::true_type>
     _test(int);
@@ -134,19 +141,19 @@
 template <typename _T>
 class ToEmptyString {
     template <typename _U>
-    static std::enable_if_t<
+    static std::enable_if_t<false
 #ifdef HAS_NDK_INTERFACE
-            std::is_base_of_v<::ndk::ICInterface, _U>
+                                    || std::is_base_of_v<::ndk::ICInterface, _U>
 #if __ANDROID_API__ >= 31
-                    || std::is_same_v<::ndk::AParcelableHolder, _U>
+                                    || std::is_same_v<::ndk::AParcelableHolder, _U>
 #endif
-#else
-            std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
-                    std::is_same_v<os::ParcelFileDescriptor, _U> ||
-                    std::is_same_v<os::ParcelableHolder, _U>
+#endif  // HAS_NDK_INTERFACE
+#ifdef HAS_CPP_INTERFACE
+                                    || std::is_base_of_v<IInterface, _U> ||
+                                    std::is_same_v<IBinder, _U>
 #endif
-            ,
-            std::true_type>
+                            ,
+                            std::true_type>
     _test(int);
     template <typename _U>
     static std::false_type _test(...);
@@ -155,6 +162,11 @@
     enum { value = decltype(_test<_T>(0))::value };
 };
 
+template <typename _T>
+struct TypeDependentFalse {
+    enum { value = false };
+};
+
 }  // namespace details
 
 template <typename _T>
@@ -214,11 +226,27 @@
         out << "]";
         return out.str();
     } else {
-        return "{no toString() implemented}";
+        static_assert(details::TypeDependentFalse<_T>::value, "no toString implemented, huh?");
     }
 }
 
 }  // namespace internal
 }  // namespace android
 
+#ifdef HAS_STRONG_POINTER
+#undef HAS_STRONG_POINTER
+#endif
+
+#ifdef HAS_STRING16
+#undef HAS_STRING16
+#endif
+
+#ifdef HAS_NDK_INTERFACE
+#undef HAS_NDK_INTERFACE
+#endif
+
+#ifdef HAS_CPP_INTERFACE
+#undef HAS_CPP_INTERFACE
+#endif
+
 /** @} */
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
index 67e482d..67553fc 100644
--- a/libs/binder/tests/binderRecordedTransactionTest.cpp
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -16,6 +16,7 @@
 
 #include <binder/BinderRecordReplay.h>
 #include <gtest/gtest.h>
+#include <utils/Errors.h>
 
 using android::Parcel;
 using android::status_t;
@@ -28,7 +29,8 @@
     d.writeInt64(2);
     Parcel r;
     r.writeInt32(99);
-    auto transaction = RecordedTransaction::fromDetails(1, 42, d, r, 0);
+    timespec ts = {1232456, 567890};
+    auto transaction = RecordedTransaction::fromDetails(1, 42, ts, d, r, 0);
 
     auto file = std::tmpfile();
     auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
@@ -42,6 +44,8 @@
 
     EXPECT_EQ(retrievedTransaction->getCode(), 1);
     EXPECT_EQ(retrievedTransaction->getFlags(), 42);
+    EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec);
+    EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec);
     EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12);
     EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4);
     EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0);
@@ -51,3 +55,80 @@
     EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2);
     EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99);
 }
+
+TEST(BinderRecordedTransaction, Checksum) {
+    Parcel d;
+    d.writeInt32(12);
+    d.writeInt64(2);
+    Parcel r;
+    r.writeInt32(99);
+    timespec ts = {1232456, 567890};
+    auto transaction = RecordedTransaction::fromDetails(1, 42, ts, d, r, 0);
+
+    auto file = std::tmpfile();
+    auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
+
+    status_t status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+
+    lseek(fd.get(), 9, SEEK_SET);
+    uint32_t badData = 0xffffffff;
+    write(fd.get(), &badData, sizeof(uint32_t));
+    std::rewind(file);
+
+    auto retrievedTransaction = RecordedTransaction::fromFile(fd);
+
+    EXPECT_FALSE(retrievedTransaction.has_value());
+}
+
+TEST(BinderRecordedTransaction, PayloadsExceedPageBoundaries) {
+    // File contents are read with mmap.
+    // This test verifies that transactions are read from portions
+    // of files that cross page boundaries and don't start at a
+    // page boundary offset of the fd.
+    const size_t pageSize = sysconf(_SC_PAGE_SIZE);
+    const size_t largeDataSize = pageSize + 100;
+    std::vector<uint8_t> largePayload;
+    uint8_t filler = 0xaa;
+    largePayload.insert(largePayload.end(), largeDataSize, filler);
+    Parcel d;
+    d.writeInt32(12);
+    d.writeInt64(2);
+    d.writeByteVector(largePayload);
+    Parcel r;
+    r.writeInt32(99);
+    timespec ts = {1232456, 567890};
+    auto transaction = RecordedTransaction::fromDetails(1, 42, ts, d, r, 0);
+
+    auto file = std::tmpfile();
+    auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
+
+    // Write to file twice
+    status_t status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+    status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+
+    std::rewind(file);
+
+    for (int i = 0; i < 2; i++) {
+        auto retrievedTransaction = RecordedTransaction::fromFile(fd);
+
+        EXPECT_EQ(retrievedTransaction->getCode(), 1);
+        EXPECT_EQ(retrievedTransaction->getFlags(), 42);
+        EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec);
+        EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec);
+        EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), d.dataSize());
+        EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4);
+        EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0);
+        EXPECT_EQ(retrievedTransaction->getVersion(), 0);
+
+        EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12);
+        EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2);
+        std::optional<std::vector<uint8_t>> payloadOut;
+        EXPECT_EQ(retrievedTransaction->getDataParcel().readByteVector(&payloadOut), android::OK);
+        EXPECT_EQ(payloadOut.value(), largePayload);
+
+        EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99);
+    }
+}
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 4330e3e..25e286c 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -37,17 +37,24 @@
     EXPECT_EQ(result->stdoutStr, "foo\n");
 }
 
+template <typename T>
+auto millisSince(std::chrono::time_point<T> now) {
+    auto elapsed = std::chrono::system_clock::now() - now;
+    return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+}
+
 TEST(UtilsHost, ExecuteLongRunning) {
-    auto now = std::chrono::system_clock::now();
+    auto start = std::chrono::system_clock::now();
 
     {
-        std::vector<std::string> args{"sh", "-c",
-                                      "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"};
-        auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+        std::vector<std::string>
+                args{"sh", "-c", "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 100"};
+        auto result = execute(std::move(args), [&](const CommandResult& commandResult) {
+            std::cout << millisSince(start)
+                      << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl;
             return android::base::EndsWith(commandResult.stdoutStr, "\n");
         });
-        auto elapsed = std::chrono::system_clock::now() - now;
-        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+        auto elapsedMs = millisSince(start);
         EXPECT_GE(elapsedMs, 1000);
         EXPECT_LT(elapsedMs, 2000);
 
@@ -58,22 +65,21 @@
 
     // ~CommandResult() called, child process is killed.
     // Assert that the second sleep does not finish.
-    auto elapsed = std::chrono::system_clock::now() - now;
-    auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
-    EXPECT_LT(elapsedMs, 2000);
+    EXPECT_LT(millisSince(start), 2000);
 }
 
 TEST(UtilsHost, ExecuteLongRunning2) {
-    auto now = std::chrono::system_clock::now();
+    auto start = std::chrono::system_clock::now();
 
     {
         std::vector<std::string> args{"sh", "-c",
-                                      "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"};
-        auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+                                      "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 100"};
+        auto result = execute(std::move(args), [&](const CommandResult& commandResult) {
+            std::cout << millisSince(start)
+                      << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl;
             return android::base::EndsWith(commandResult.stdoutStr, "\n");
         });
-        auto elapsed = std::chrono::system_clock::now() - now;
-        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+        auto elapsedMs = millisSince(start);
         EXPECT_GE(elapsedMs, 4000);
         EXPECT_LT(elapsedMs, 6000);
 
@@ -84,9 +90,7 @@
 
     // ~CommandResult() called, child process is killed.
     // Assert that the second sleep does not finish.
-    auto elapsed = std::chrono::system_clock::now() - now;
-    auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
-    EXPECT_LT(elapsedMs, 6000);
+    EXPECT_LT(millisSince(start), 6000);
 }
 
 TEST(UtilsHost, KillWithSigKill) {
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index fa0cc81..08eb27a 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -213,8 +213,9 @@
             size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
             std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
             const uint8_t* buffer = parcelData.data();
+            const size_t bufferLen = parcelData.size();
             NdkParcelAdapter adapter;
-            binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, len);
+            binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen);
             FUZZ_LOG() << "status: " << status;
         },
 
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
index 0a4873c..3fb33b4 100644
--- a/libs/ui/tests/colorspace_test.cpp
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -111,6 +111,7 @@
     EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
     EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
 
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
         ASSERT_TRUE(v >= sRGB.getEOTF()(v));
         ASSERT_TRUE(v <= sRGB.getOETF()(v));
@@ -118,6 +119,7 @@
 
     float previousEOTF = std::numeric_limits<float>::lowest();
     float previousOETF = std::numeric_limits<float>::lowest();
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
         ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
         previousEOTF = sRGB.getEOTF()(v);
@@ -131,6 +133,7 @@
           {0.3127f, 0.3290f}
           // linear transfer functions
     );
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
         ASSERT_EQ(v, sRGB2.getEOTF()(v));
         ASSERT_EQ(v, sRGB2.getOETF()(v));