Merge "binderUtilsHostTest: more debuggable"
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index a80da4e..12a7cff 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -8,6 +8,7 @@
     socket dumpstate stream 0660 shell log
     disabled
     oneshot
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
 
 # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
 # it is finished.
@@ -16,9 +17,11 @@
     class main
     disabled
     oneshot
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
 
 # bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
 service bugreportd /system/bin/dumpstate -w
     class main
     disabled
     oneshot
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
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 90c02a8..8ba18a8 100644
--- a/libs/binder/BinderRecordReplay.cpp
+++ b/libs/binder/BinderRecordReplay.cpp
@@ -16,10 +16,12 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <binder/BinderRecordReplay.h>
 #include <algorithm>
 
 using android::Parcel;
+using android::base::borrowed_fd;
 using android::base::unique_fd;
 using android::binder::debug::RecordedTransaction;
 
@@ -30,42 +32,100 @@
 static_assert(PADDING8(7) == 1);
 static_assert(PADDING8(8) == 0);
 
-// Transactions are sequentially recorded to the file descriptor in the following format:
+// Transactions are sequentially recorded to a file descriptor.
 //
-// RecordedTransaction.TransactionHeader  (32 bytes)
-// Sent Parcel data                       (getDataSize() bytes)
-// padding                                (enough bytes to align the reply Parcel data to 8 bytes)
-// Reply Parcel data                      (getReplySize() bytes)
-// padding                                (enough bytes to align the next header to 8 bytes)
-// [repeats with next transaction]
+// An individual RecordedTransaction is written with the following format:
 //
-// Warning: This format is non-stable
+// WARNING: Though the following format is designed to be stable and
+// extensible, it is under active development and should be considered
+// unstable until this warning is removed.
+//
+// A RecordedTransaction is written to a file as a sequence of Chunks.
+//
+// A Chunk consists of a ChunkDescriptor, Data, and Padding.
+//
+// Data and Padding may each be zero-length as specified by the
+// ChunkDescriptor.
+//
+// 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.
+//
+// ┌───────────────────────────┐
+// │Chunk                      │
+// │┌─────────────────────────┐│
+// ││ChunkDescriptor          ││
+// ││┌───────────┬───────────┐││
+// │││chunkType  │paddingSize│││
+// │││uint32_t   │uint32_t   ├┼┼───┐
+// ││├───────────┴───────────┤││   │
+// │││dataSize               │││   │
+// │││uint64_t               ├┼┼─┐ │
+// ││└───────────────────────┘││ │ │
+// │└─────────────────────────┘│ │ │
+// │┌─────────────────────────┐│ │ │
+// ││Data                     ││ │ │
+// ││bytes * dataSize         │◀─┘ │
+// │└─────────────────────────┘│   │
+// │┌─────────────────────────┐│   │
+// ││Padding                  ││   │
+// ││bytes * paddingSize      │◀───┘
+// │└─────────────────────────┘│
+// └───────────────────────────┘
+//
+// A RecordedTransaction is written as a Header Chunk with fields about the
+// transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk.
+// ┌──────────────────────┐
+// │     Header Chunk     │
+// ├──────────────────────┤
+// │  Sent Parcel Chunk   │
+// ├──────────────────────┤
+// │  Reply Parcel Chunk  │
+// ├──────────────────────┤
+// ║      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:
+//
+// 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.
+// 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.
 
 RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
-    mHeader = {t.getCode(),      t.getFlags(),          t.getDataSize(),
-               t.getReplySize(), t.getReturnedStatus(), t.getVersion()};
-    mSent.setData(t.getDataParcel().data(), t.getDataSize());
-    mReply.setData(t.getReplyParcel().data(), t.getReplySize());
+    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<uint64_t>(dataParcel.dataSize()),
-                 static_cast<uint64_t>(replyParcel.dataSize()),
                  static_cast<int32_t>(err),
-                 dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)};
+                 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(), t.getDataSize()) != android::NO_ERROR) {
+    if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) {
         LOG(INFO) << "Failed to set sent parcel data.";
         return std::nullopt;
     }
 
-    if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) {
+    if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) {
         LOG(INFO) << "Failed to set reply parcel data.";
         return std::nullopt;
     }
@@ -73,80 +133,155 @@
     return std::optional<RecordedTransaction>(std::move(t));
 }
 
+enum {
+    HEADER_CHUNK = 0x00000001,
+    DATA_PARCEL_CHUNK = 0x00000002,
+    REPLY_PARCEL_CHUNK = 0x00000003,
+    INVALID_CHUNK = 0x00fffffe,
+    END_CHUNK = 0x00ffffff,
+};
+
+struct ChunkDescriptor {
+    uint32_t chunkType = 0;
+    uint32_t padding = 0;
+    uint32_t dataSize = 0;
+    uint32_t reserved = 0; // Future checksum
+};
+
+static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut) {
+    if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) {
+        LOG(INFO) << "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;
+    }
+    return android::NO_ERROR;
+}
+
 std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
     RecordedTransaction t;
-    if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) {
-        LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (t.getVersion() != 0) {
-        LOG(INFO) << "File corrupted: transaction version is not 0.";
-        return std::nullopt;
-    }
+    ChunkDescriptor chunk;
 
-    std::vector<uint8_t> bytes;
-    bytes.resize(t.getDataSize());
-    if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) {
-        LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set sent parcel data.";
-        return std::nullopt;
-    }
-
-    uint8_t padding[7];
-    if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) {
-        LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
-        LOG(INFO) << "File corrupted: padding isn't 0.";
-        return std::nullopt;
-    }
-
-    bytes.resize(t.getReplySize());
-    if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) {
-        LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set reply parcel data.";
-        return std::nullopt;
-    }
-
-    if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) {
-        LOG(INFO) << "Failed to read parcel padding from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
-        LOG(INFO) << "File corrupted: padding isn't 0.";
-        return std::nullopt;
-    }
+    do {
+        if (NO_ERROR != readChunkDescriptor(fd, &chunk)) {
+            LOG(INFO) << "Failed to read chunk descriptor.";
+            return std::nullopt;
+        }
+        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) << ".";
+                    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;
+                }
+                lseek(fd.get(), chunk.padding, SEEK_CUR);
+                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();
+                    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();
+                    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;
+        }
+    } while (chunk.chunkType != END_CHUNK);
 
     return std::optional<RecordedTransaction>(std::move(t));
 }
 
+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;
+    }
+    // - 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;
+    }
+
+    if (!android::base::WriteFully(fd, data, byteCount)) {
+        LOG(INFO) << "Failed to write chunk data to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+
+    const uint8_t zeros[7] = {0};
+    if (!android::base::WriteFully(fd, zeros, additionalPaddingCount)) {
+        LOG(INFO) << "Failed to write chunk padding to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
 android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
-    if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) {
+    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();
         return UNKNOWN_ERROR;
     }
-    if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) {
-        LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get();
+    if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) {
+        LOG(INFO) << "Failed to write sent Parcel to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
-    const uint8_t zeros[7] = {0};
-    if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) {
-        LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get();
+    if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) {
+        LOG(INFO) << "Failed to write reply Parcel to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
-    if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) {
-        LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) {
-        LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get();
+    if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
+        LOG(INFO) << "Failed to write end chunk to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
@@ -160,18 +295,16 @@
     return mHeader.flags;
 }
 
-uint64_t RecordedTransaction::getDataSize() const {
-    return mHeader.dataSize;
-}
-
-uint64_t RecordedTransaction::getReplySize() const {
-    return mHeader.replySize;
-}
-
 int32_t RecordedTransaction::getReturnedStatus() const {
     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 25ed5e5..ff983f0 100644
--- a/libs/binder/include/binder/BinderRecordReplay.h
+++ b/libs/binder/include/binder/BinderRecordReplay.h
@@ -34,17 +34,16 @@
     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;
 
     uint32_t getCode() const;
     uint32_t getFlags() const;
-    uint64_t getDataSize() const;
-    uint64_t getReplySize() const;
     int32_t getReturnedStatus() const;
+    timespec getTimestamp() const;
     uint32_t getVersion() const;
     const Parcel& getDataParcel() const;
     const Parcel& getReplyParcel() const;
@@ -52,15 +51,19 @@
 private:
     RecordedTransaction() = default;
 
+    android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType,
+                                 size_t byteCount, const uint8_t* data) const;
+
 #pragma clang diagnostic push
 #pragma clang diagnostic error "-Wpadded"
     struct TransactionHeader {
         uint32_t code = 0;
         uint32_t flags = 0;
-        uint64_t dataSize = 0;
-        uint64_t replySize = 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) == 32);
@@ -69,10 +72,6 @@
     TransactionHeader mHeader;
     Parcel mSent;
     Parcel mReply;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-private-field"
-    uint8_t mReserved[40];
-#pragma clang diagnostic pop
 };
 
 } // namespace binder::debug
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/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 78dae4b..e7943dd 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -112,6 +112,13 @@
         LOG(ERROR) << "Failed to get fd for the socket:" << name;
         return nullptr;
     }
+    // Control socket fds are inherited from init, so they don't have O_CLOEXEC set.
+    // But we don't want any child processes to inherit the socket we are running
+    // the server on, so attempt to set the flag now.
+    if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+        LOG(WARNING) << "Failed to set CLOEXEC on control socket with name " << name
+                     << " error: " << errno;
+    }
     if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
         LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
                    << " error: " << statusToString(status).c_str();
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..e3ead11 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,19 @@
 #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 +111,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 +143,21 @@
 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> ||
+                                    std::is_same_v<os::ParcelFileDescriptor, _U> ||
+                                    std::is_same_v<os::ParcelableHolder, _U>
 #endif
-            ,
-            std::true_type>
+                            ,
+                            std::true_type>
     _test(int);
     template <typename _U>
     static std::false_type _test(...);
@@ -155,6 +166,11 @@
     enum { value = decltype(_test<_T>(0))::value };
 };
 
+template <typename _T>
+struct TypeDependentFalse {
+    enum { value = false };
+};
+
 }  // namespace details
 
 template <typename _T>
@@ -214,11 +230,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/Android.bp b/libs/binder/tests/Android.bp
index a999d59..5db3187 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -100,6 +100,7 @@
         "binderBinderUnitTest.cpp",
         "binderStatusUnitTest.cpp",
         "binderMemoryHeapBaseUnitTest.cpp",
+        "binderRecordedTransactionTest.cpp",
     ],
     shared_libs: [
         "libbinder",
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index 6a6e008..bc40864 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -180,7 +180,11 @@
         mallocs++;
         // Happens to be SM package length. We could switch to forking
         // and registering our own service if it became an issue.
+#if defined(__LP64__)
         EXPECT_EQ(bytes, 78);
+#else
+        EXPECT_EQ(bytes, 70);
+#endif
     });
 
     a_binder->getInterfaceDescriptor();
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
new file mode 100644
index 0000000..2ece315
--- /dev/null
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/BinderRecordReplay.h>
+#include <gtest/gtest.h>
+
+using android::Parcel;
+using android::status_t;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+TEST(BinderRecordedTransaction, RoundTripEncoding) {
+    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);
+
+    std::rewind(file);
+
+    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(), 12);
+    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);
+    EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 53e7de4..08eb27a 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -199,5 +199,25 @@
             binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
         },
+        [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) {
+            FUZZ_LOG() << "about to marshal AParcel";
+            size_t start = provider.ConsumeIntegral<size_t>();
+            // limit 1MB to avoid OOM issues
+            size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1000000);
+            uint8_t buffer[len];
+            binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
+            FUZZ_LOG() << "status: " << status;
+        },
+        [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
+            FUZZ_LOG() << "about to unmarshal AParcel";
+            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, bufferLen);
+            FUZZ_LOG() << "status: " << status;
+        },
+
 };
 // clang-format on
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));