Merge "binder: Move RpcCertificateVerifier to include_tls."
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index be2c702..8f1c01a 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -52,7 +52,7 @@
 }
 
 struct SecurityContext_Delete {
-    void operator()(security_context_t p) const {
+    void operator()(char* p) const {
         freecon(p);
     }
 };
@@ -108,7 +108,7 @@
         }
         if (is_selinux_enabled() && seLinuxContext.size() > 0) {
             String8 seLinuxContext8(seLinuxContext);
-            security_context_t tmp = nullptr;
+            char* tmp = nullptr;
             getfilecon(fullPath.string(), &tmp);
             Unique_SecurityContext context(tmp);
             if (checkWrite) {
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index b197a6a..ecf13dc 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -59,19 +59,4 @@
     }
 }
 
-android::base::Result<bool> FdTrigger::isTriggeredPolled() {
-    pollfd pfd{.fd = mRead.get(), .events = 0, .revents = 0};
-    int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, 0));
-    if (ret < 0) {
-        return android::base::ErrnoError() << "FdTrigger::isTriggeredPolled: Error in poll()";
-    }
-    if (ret == 0) {
-        return false;
-    }
-    if (pfd.revents & POLLHUP) {
-        return true;
-    }
-    return android::base::Error() << "FdTrigger::isTriggeredPolled: poll() returns " << pfd.revents;
-}
-
 } // namespace android
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a428417..a545d6c 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -35,9 +35,13 @@
     void trigger();
 
     /**
-     * Check whether this has been triggered by checking the write end.
+     * Check whether this has been triggered by checking the write end. Note:
+     * this has no internal locking, and it is inherently racey, but this is
+     * okay, because if we accidentally return false when a trigger has already
+     * happened, we can imagine that instead, the scheduler actually executed
+     * the code which is polling isTriggered earlier.
      */
-    bool isTriggered();
+    [[nodiscard]] bool isTriggered();
 
     /**
      * Poll for a read event.
@@ -48,17 +52,7 @@
      *   true - time to read!
      *   false - trigger happened
      */
-    status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
-
-    /**
-     * Check whether this has been triggered by poll()ing the read end.
-     *
-     * Return:
-     *   true - triggered
-     *   false - not triggered
-     *   error - error when polling
-     */
-    android::base::Result<bool> isTriggeredPolled();
+    [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
 
 private:
     base::unique_fd mWrite;
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index c012df8..41f4a9f 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -35,20 +35,6 @@
 class RpcTransportRaw : public RpcTransport {
 public:
     explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
-    Result<size_t> send(const void* buf, size_t size) {
-        ssize_t ret = TEMP_FAILURE_RETRY(::send(mSocket.get(), buf, size, MSG_NOSIGNAL));
-        if (ret < 0) {
-            return ErrnoError() << "send()";
-        }
-        return ret;
-    }
-    Result<size_t> recv(void* buf, size_t size) {
-        ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_NOSIGNAL));
-        if (ret < 0) {
-            return ErrnoError() << "recv()";
-        }
-        return ret;
-    }
     Result<size_t> peek(void *buf, size_t size) override {
         ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
         if (ret < 0) {
@@ -65,15 +51,17 @@
 
         status_t status;
         while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLOUT)) == OK) {
-            auto writeSize = this->send(buffer, end - buffer);
-            if (!writeSize.ok()) {
-                LOG_RPC_DETAIL("RpcTransport::send(): %s", writeSize.error().message().c_str());
-                return writeSize.error().code() == 0 ? UNKNOWN_ERROR : -writeSize.error().code();
+            ssize_t writeSize =
+                    TEMP_FAILURE_RETRY(::send(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+            if (writeSize < 0) {
+                int savedErrno = errno;
+                LOG_RPC_DETAIL("RpcTransport send(): %s", strerror(savedErrno));
+                return -savedErrno;
             }
 
-            if (*writeSize == 0) return DEAD_OBJECT;
+            if (writeSize == 0) return DEAD_OBJECT;
 
-            buffer += *writeSize;
+            buffer += writeSize;
             if (buffer == end) return OK;
         }
         return status;
@@ -87,15 +75,17 @@
 
         status_t status;
         while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLIN)) == OK) {
-            auto readSize = this->recv(buffer, end - buffer);
-            if (!readSize.ok()) {
-                LOG_RPC_DETAIL("RpcTransport::recv(): %s", readSize.error().message().c_str());
-                return readSize.error().code() == 0 ? UNKNOWN_ERROR : -readSize.error().code();
+            ssize_t readSize =
+                    TEMP_FAILURE_RETRY(::recv(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+            if (readSize < 0) {
+                int savedErrno = errno;
+                LOG_RPC_DETAIL("RpcTransport recv(): %s", strerror(savedErrno));
+                return -savedErrno;
             }
 
-            if (*readSize == 0) return DEAD_OBJECT; // EOF
+            if (readSize == 0) return DEAD_OBJECT; // EOF
 
-            buffer += *readSize;
+            buffer += readSize;
             if (buffer == end) return OK;
         }
         return status;
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 63f9339..23088ad 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -319,8 +319,6 @@
 private:
     android::base::unique_fd mSocket;
     Ssl mSsl;
-
-    static status_t isTriggered(FdTrigger* fdTrigger);
 };
 
 // Error code is errno.
@@ -341,15 +339,6 @@
     return ret;
 }
 
-status_t RpcTransportTls::isTriggered(FdTrigger* fdTrigger) {
-    auto ret = fdTrigger->isTriggeredPolled();
-    if (!ret.ok()) {
-        ALOGE("%s: %s", __PRETTY_FUNCTION__, ret.error().message().c_str());
-        return ret.error().code() == 0 ? UNKNOWN_ERROR : -ret.error().code();
-    }
-    return *ret ? -ECANCELED : OK;
-}
-
 status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
                                                   size_t size) {
     auto buffer = reinterpret_cast<const uint8_t*>(data);
@@ -359,7 +348,7 @@
 
     // Before doing any I/O, check trigger once. This ensures the trigger is checked at least
     // once. The trigger is also checked via triggerablePoll() after every SSL_write().
-    if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+    if (fdTrigger->isTriggered()) return -ECANCELED;
 
     while (buffer < end) {
         size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
@@ -390,7 +379,7 @@
 
     // Before doing any I/O, check trigger once. This ensures the trigger is checked at least
     // once. The trigger is also checked via triggerablePoll() after every SSL_write().
-    if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+    if (fdTrigger->isTriggered()) return -ECANCELED;
 
     while (buffer < end) {
         size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 59334b7..27cc563 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -74,12 +74,12 @@
               result->toString().c_str());
         return std::nullopt;
     }
-    if (!result->stderr.empty()) {
+    if (!result->stderrStr.empty()) {
         LOG_HOST("`adb forward tcp:0 tcp:%d` writes to stderr: %s", devicePort,
-                 result->stderr.c_str());
+                 result->stderrStr.c_str());
     }
 
-    unsigned int hostPort = parsePortNumber(result->stdout, "host port");
+    unsigned int hostPort = parsePortNumber(result->stdoutStr, "host port");
     if (hostPort == 0) return std::nullopt;
 
     return AdbForwarder(hostPort);
@@ -105,9 +105,9 @@
               result->toString().c_str());
         return;
     }
-    if (!result->stderr.empty()) {
+    if (!result->stderrStr.empty()) {
         LOG_HOST("`adb forward --remove tcp:%d` writes to stderr: %s", *mPort,
-                 result->stderr.c_str());
+                 result->stderrStr.c_str());
     }
 
     LOG_HOST("Successfully run `adb forward --remove tcp:%d`", *mPort);
@@ -139,8 +139,8 @@
         ALOGE("Command exits with: %s", result->toString().c_str());
         return nullptr;
     }
-    if (!result->stderr.empty()) {
-        LOG_HOST("servicedispatcher writes to stderr: %s", result->stderr.c_str());
+    if (!result->stderrStr.empty()) {
+        LOG_HOST("servicedispatcher writes to stderr: %s", result->stderrStr.c_str());
     }
 
     if (!result->stdoutEndsWithNewLine()) {
@@ -148,7 +148,7 @@
         return nullptr;
     }
 
-    unsigned int devicePort = parsePortNumber(result->stdout, "device port");
+    unsigned int devicePort = parsePortNumber(result->stdoutStr, "device port");
     if (devicePort == 0) return nullptr;
 
     auto forwardResult = AdbForwarder::forward(devicePort);
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index d121ce2..52b8f69 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -63,7 +63,7 @@
     if (res.exitCode) os << "code=" << *res.exitCode;
     if (res.signal) os << "signal=" << *res.signal;
     if (res.pid) os << ", pid=" << *res.pid;
-    return os << ", stdout=" << res.stdout << ", stderr=" << res.stderr;
+    return os << ", stdout=" << res.stdoutStr << ", stderr=" << res.stderrStr;
 }
 
 std::string CommandResult::toString() const {
@@ -142,9 +142,9 @@
         int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
         if (pollRet == -1) return android::base::ErrnoError() << "poll()";
 
-        if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdout))
+        if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr))
             return android::base::ErrnoError() << "read(stdout)";
-        if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderr))
+        if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr))
             return android::base::ErrnoError() << "read(stderr)";
 
         if (end && end(ret)) return ret;
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index 0f29f60..98ac4e0 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -43,8 +43,8 @@
     std::optional<int32_t> exitCode;
     std::optional<int32_t> signal;
     std::optional<pid_t> pid;
-    std::string stdout;
-    std::string stderr;
+    std::string stdoutStr;
+    std::string stderrStr;
 
     android::base::unique_fd outPipe;
     android::base::unique_fd errPipe;
@@ -55,15 +55,15 @@
         std::swap(exitCode, other.exitCode);
         std::swap(signal, other.signal);
         std::swap(pid, other.pid);
-        std::swap(stdout, other.stdout);
-        std::swap(stderr, other.stderr);
+        std::swap(stdoutStr, other.stdoutStr);
+        std::swap(stderrStr, other.stderrStr);
         return *this;
     }
     ~CommandResult();
     [[nodiscard]] std::string toString() const;
 
     [[nodiscard]] bool stdoutEndsWithNewLine() const {
-        return !stdout.empty() && stdout.back() == '\n';
+        return !stdoutStr.empty() && stdoutStr.back() == '\n';
     }
 
 private:
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 1c0bb18..4fe2324 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -38,7 +38,7 @@
     virtual ~RpcTransport() = default;
 
     // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
-    virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+    [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
 
     /**
      * Read (or write), but allow to be interrupted by a trigger.
@@ -47,9 +47,10 @@
      *   OK - succeeded in completely processing 'size'
      *   error - interrupted (failure or trigger)
      */
-    virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
-                                             size_t size) = 0;
-    virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf, size_t size) = 0;
+    [[nodiscard]] virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
+                                                           size_t size) = 0;
+    [[nodiscard]] virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf,
+                                                          size_t size) = 0;
 
 protected:
     RpcTransport() = default;
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 23db59e..83bf9be 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -373,6 +373,12 @@
     return clazz->getInterfaceDescriptorUtf8();
 }
 
+AIBinder_DeathRecipient::TransferDeathRecipient::~TransferDeathRecipient() {
+    if (mOnUnlinked != nullptr) {
+        mOnUnlinked(mCookie);
+    }
+}
+
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
     CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
                        << " (" << mWho.get_refs() << ")";
@@ -394,7 +400,7 @@
 }
 
 AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied)
-    : mOnDied(onDied) {
+    : mOnDied(onDied), mOnUnlinked(nullptr) {
     CHECK(onDied != nullptr);
 }
 
@@ -412,10 +418,12 @@
     std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
 
     sp<TransferDeathRecipient> recipient =
-            new TransferDeathRecipient(binder, cookie, this, mOnDied);
+            new TransferDeathRecipient(binder, cookie, this, mOnDied, mOnUnlinked);
 
     status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/);
     if (status != STATUS_OK) {
+        // When we failed to link, the destructor of TransferDeathRecipient runs here, which
+        // ensures that mOnUnlinked is called before we return with an error from this method.
         return PruneStatusT(status);
     }
 
@@ -448,6 +456,10 @@
     return STATUS_NAME_NOT_FOUND;
 }
 
+void AIBinder_DeathRecipient::setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) {
+    mOnUnlinked = onUnlinked;
+}
+
 // start of C-API methods
 
 AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
@@ -689,6 +701,15 @@
     return ret;
 }
 
+void AIBinder_DeathRecipient_setOnUnlinked(AIBinder_DeathRecipient* recipient,
+                                           AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) {
+    if (recipient == nullptr) {
+        return;
+    }
+
+    recipient->setOnUnlinked(onUnlinked);
+}
+
 void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) {
     if (recipient == nullptr) {
         return;
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 6509545..9fb5c1d 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -148,8 +148,14 @@
     struct TransferDeathRecipient : ::android::IBinder::DeathRecipient {
         TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie,
                                const ::android::wp<AIBinder_DeathRecipient>& parentRecipient,
-                               const AIBinder_DeathRecipient_onBinderDied onDied)
-            : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {}
+                               const AIBinder_DeathRecipient_onBinderDied onDied,
+                               const AIBinder_DeathRecipient_onBinderUnlinked onUnlinked)
+            : mWho(who),
+              mCookie(cookie),
+              mParentRecipient(parentRecipient),
+              mOnDied(onDied),
+              mOnUnlinked(onUnlinked) {}
+        ~TransferDeathRecipient();
 
         void binderDied(const ::android::wp<::android::IBinder>& who) override;
 
@@ -165,11 +171,13 @@
         // This is kept separately from AIBinder_DeathRecipient in case the death recipient is
         // deleted while the death notification is fired
         const AIBinder_DeathRecipient_onBinderDied mOnDied;
+        const AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked;
     };
 
     explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
     binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
     binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
+    void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked);
 
    private:
     // When the user of this API deletes a Bp object but not the death recipient, the
@@ -180,4 +188,5 @@
     std::mutex mDeathRecipientsMutex;
     std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients;
     AIBinder_DeathRecipient_onBinderDied mOnDied;
+    AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked;
 };
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index b881c2c..43533c5 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -319,9 +319,9 @@
 /**
  * Registers for notifications that the associated binder is dead. The same death recipient may be
  * associated with multiple different binders. If the binder is local, then no death recipient will
- * be given (since if the local process dies, then no recipient will exist to recieve a
+ * be given (since if the local process dies, then no recipient will exist to receive a
  * transaction). The cookie is passed to recipient in the case that this binder dies and can be
- * null. The exact cookie must also be used to unlink this transaction (see AIBinder_linkToDeath).
+ * null. The exact cookie must also be used to unlink this transaction (see AIBinder_unlinkToDeath).
  * This function may return a binder transaction failure. The cookie can be used both for
  * identification and holding user data.
  *
@@ -348,6 +348,10 @@
  * If the binder dies, it will automatically unlink. If the binder is deleted, it will be
  * automatically unlinked.
  *
+ * Be aware that it is not safe to immediately deallocate the cookie when this call returns. If you
+ * need to clean up the cookie, you should do so in the onUnlinked callback, which can be set using
+ * AIBinder_DeathRecipient_setOnUnlinked.
+ *
  * Available since API level 29.
  *
  * \param binder the binder object to remove a previously linked death recipient from.
@@ -568,6 +572,22 @@
 typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29);
 
 /**
+ * This function is intended for cleaning up the data in the provided cookie, and it is executed
+ * when the DeathRecipient is unlinked. When the DeathRecipient is unlinked due to a death receipt,
+ * this method is called after the call to onBinderDied.
+ *
+ * This method is called once for each binder that is unlinked. Hence, if the same cookie is passed
+ * to multiple binders, then the caller is responsible for reference counting the cookie.
+ *
+ * See also AIBinder_linkToDeath/AIBinder_unlinkToDeath.
+ *
+ * Available since API level 33.
+ *
+ * \param cookie the cookie passed to AIBinder_linkToDeath.
+ */
+typedef void (*AIBinder_DeathRecipient_onBinderUnlinked)(void* cookie) __INTRODUCED_IN(33);
+
+/**
  * Creates a new binder death recipient. This can be attached to multiple different binder objects.
  *
  * Available since API level 29.
@@ -580,9 +600,47 @@
         AIBinder_DeathRecipient_onBinderDied onBinderDied) __INTRODUCED_IN(29);
 
 /**
+ * Set the callback to be called when this DeathRecipient is unlinked from a binder. The callback is
+ * called in the following situations:
+ *
+ *  1. If the binder died, shortly after the call to onBinderDied.
+ *  2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
+ *     AIBinder_DeathRecipient_delete.
+ *  3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
+ *
+ * It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
+ * process is aborted before the binder is unlinked.
+ *
+ * Be aware that when the binder is explicitly unlinked, it is not guaranteed that onUnlinked has
+ * been called before the call to AIBinder_unlinkToDeath or AIBinder_DeathRecipient_delete returns.
+ * For example, if the binder dies concurrently with a call to AIBinder_unlinkToDeath, the binder is
+ * not unlinked until after the death notification is delivered, even if AIBinder_unlinkToDeath
+ * returns before that happens.
+ *
+ * This method should be called before linking the DeathRecipient to a binder because the function
+ * pointer is cached. If you change it after linking to a binder, it is unspecified whether the old
+ * binder will call the old or new onUnlinked callback.
+ *
+ * The onUnlinked argument may be null. In this case, no notification is given when the binder is
+ * unlinked.
+ *
+ * Available since API level 33.
+ *
+ * \param recipient the DeathRecipient to set the onUnlinked callback for.
+ * \param onUnlinked the callback to call when a binder is unlinked from recipient.
+ */
+void AIBinder_DeathRecipient_setOnUnlinked(AIBinder_DeathRecipient* recipient,
+                                           AIBinder_DeathRecipient_onBinderUnlinked onUnlinked)
+        __INTRODUCED_IN(33);
+
+/**
  * Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before
  * calling this as these will all be automatically unlinked.
  *
+ * Be aware that it is not safe to immediately deallocate the cookie when this call returns. If you
+ * need to clean up the cookie, you should do so in the onUnlinked callback, which can be set using
+ * AIBinder_DeathRecipient_setOnUnlinked.
+ *
  * Available since API level 29.
  *
  * \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new).
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a2f5c93..8457581 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1167,6 +1167,8 @@
 /**
  * Marshals the raw bytes of the Parcel to a buffer.
  *
+ * Available since API level 33.
+ *
  * The parcel must not contain any binders or file descriptors.
  *
  * The data you retrieve here must not be placed in any kind of persistent storage. (on local disk,
@@ -1189,6 +1191,8 @@
 /**
  * Set the data in the parcel to the raw bytes from the buffer.
  *
+ * Available since API level 33.
+ *
  * \param parcel The parcel to set data.
  * \param buffer The data buffer to set.
  * \param len The size of the data to set.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index ac892db..8605686 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -144,6 +144,7 @@
 LIBBINDER_NDK33 { # introduced=33
   global:
     AIBinder_Class_disableInterfaceTokenHeader;
+    AIBinder_DeathRecipient_setOnUnlinked;
     AParcel_marshal;
     AParcel_unmarshal;
 };
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index b5c06e9..d1ff4de 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -375,9 +375,16 @@
             << "Service failed to shut down.";
 }
 
+struct DeathRecipientCookie {
+    std::function<void(void)>*onDeath, *onUnlink;
+};
 void LambdaOnDeath(void* cookie) {
-    auto onDeath = static_cast<std::function<void(void)>*>(cookie);
-    (*onDeath)();
+    auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+    (*funcs->onDeath)();
+};
+void LambdaOnUnlink(void* cookie) {
+    auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+    (*funcs->onUnlink)();
 };
 TEST(NdkBinder, DeathRecipient) {
     using namespace std::chrono_literals;
@@ -389,26 +396,46 @@
 
     std::mutex deathMutex;
     std::condition_variable deathCv;
-    bool deathRecieved = false;
+    bool deathReceived = false;
 
     std::function<void(void)> onDeath = [&] {
         std::cerr << "Binder died (as requested)." << std::endl;
-        deathRecieved = true;
+        deathReceived = true;
         deathCv.notify_one();
     };
 
-    AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+    std::mutex unlinkMutex;
+    std::condition_variable unlinkCv;
+    bool unlinkReceived = false;
+    bool wasDeathReceivedFirst = false;
 
-    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
+    std::function<void(void)> onUnlink = [&] {
+        std::cerr << "Binder unlinked (as requested)." << std::endl;
+        wasDeathReceivedFirst = deathReceived;
+        unlinkReceived = true;
+        unlinkCv.notify_one();
+    };
+
+    DeathRecipientCookie cookie = {&onDeath, &onUnlink};
+
+    AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+    AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink);
+
+    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie)));
 
     // the binder driver should return this if the service dies during the transaction
     EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
 
     foo = nullptr;
 
-    std::unique_lock<std::mutex> lock(deathMutex);
-    EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
-    EXPECT_TRUE(deathRecieved);
+    std::unique_lock<std::mutex> lockDeath(deathMutex);
+    EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; }));
+    EXPECT_TRUE(deathReceived);
+
+    std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+    EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; }));
+    EXPECT_TRUE(unlinkReceived);
+    EXPECT_TRUE(wasDeathReceivedFirst);
 
     AIBinder_DeathRecipient_delete(recipient);
     AIBinder_decStrong(binder);
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index dd0c7b8..41ceee5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -152,20 +152,46 @@
     /// available.
     fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
 
+    /// Create a Parcel that can be used with `submit_transact`.
+    fn prepare_transact(&self) -> Result<Parcel>;
+
     /// Perform a generic operation with the object.
     ///
+    /// The provided [`Parcel`] must have been created by a call to
+    /// `prepare_transact` on the same binder.
+    ///
+    /// # Arguments
+    ///
+    /// * `code` - Transaction code for the operation.
+    /// * `data` - [`Parcel`] with input data.
+    /// * `flags` - Transaction flags, e.g. marking the transaction as
+    ///   asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
+    fn submit_transact(
+        &self,
+        code: TransactionCode,
+        data: Parcel,
+        flags: TransactionFlags,
+    ) -> Result<Parcel>;
+
+    /// Perform a generic operation with the object. This is a convenience
+    /// method that internally calls `prepare_transact` followed by
+    /// `submit_transact.
+    ///
     /// # Arguments
     /// * `code` - Transaction code for the operation
-    /// * `data` - [`Parcel`] with input data
-    /// * `reply` - Optional [`Parcel`] for reply data
     /// * `flags` - Transaction flags, e.g. marking the transaction as
     ///   asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
+    /// * `input_callback` A callback for building the `Parcel`.
     fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
         &self,
         code: TransactionCode,
         flags: TransactionFlags,
         input_callback: F,
-    ) -> Result<Parcel>;
+    ) -> Result<Parcel> {
+        let mut parcel = self.prepare_transact()?;
+        input_callback(&mut parcel)?;
+        self.submit_transact(code, parcel, flags)
+    }
 }
 
 /// Interface of binder local or remote objects.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index a0e991c..dad89ec 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -25,6 +25,7 @@
 use std::convert::TryInto;
 use std::mem::ManuallyDrop;
 use std::ptr;
+use std::fmt;
 
 mod file_descriptor;
 mod parcelable;
@@ -96,6 +97,13 @@
         let _ = ManuallyDrop::new(self);
         ptr
     }
+
+    pub(crate) fn is_owned(&self) -> bool {
+        match *self {
+            Self::Owned(_) => true,
+            Self::Borrowed(_) => false,
+        }
+    }
 }
 
 // Data serialization methods
@@ -412,6 +420,13 @@
     }
 }
 
+impl fmt::Debug for Parcel {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Parcel")
+            .finish()
+    }
+}
+
 #[cfg(test)]
 impl Parcel {
     /// Create a new parcel tied to a bogus binder. TESTING ONLY!
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index b03ed49..68fa34b 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -31,8 +31,10 @@
 use std::convert::TryInto;
 use std::ffi::{c_void, CString};
 use std::fmt;
+use std::mem;
 use std::os::unix::io::AsRawFd;
 use std::ptr;
+use std::sync::Arc;
 
 /// A strong reference to a Binder remote object.
 ///
@@ -233,13 +235,7 @@
 }
 
 impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
-    /// Perform a binder transaction
-    fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
-        &self,
-        code: TransactionCode,
-        flags: TransactionFlags,
-        input_callback: F,
-    ) -> Result<Parcel> {
+    fn prepare_transact(&self) -> Result<Parcel> {
         let mut input = ptr::null_mut();
         let status = unsafe {
             // Safety: `SpIBinder` guarantees that `self` always contains a
@@ -252,15 +248,25 @@
             // pointer, or null.
             sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
         };
+
         status_result(status)?;
-        let mut input = unsafe {
+
+        unsafe {
             // Safety: At this point, `input` is either a valid, owned `AParcel`
             // pointer, or null. `Parcel::owned` safely handles both cases,
             // taking ownership of the parcel.
-            Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)?
-        };
-        input_callback(&mut input)?;
+            Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+        }
+    }
+
+    fn submit_transact(
+        &self,
+        code: TransactionCode,
+        data: Parcel,
+        flags: TransactionFlags,
+    ) -> Result<Parcel> {
         let mut reply = ptr::null_mut();
+        assert!(data.is_owned());
         let status = unsafe {
             // Safety: `SpIBinder` guarantees that `self` always contains a
             // valid pointer to an `AIBinder`. Although `IBinder::transact` is
@@ -275,13 +281,13 @@
             // only providing `on_transact` with an immutable reference to
             // `self`.
             //
-            // This call takes ownership of the `input` parcel pointer, and
+            // This call takes ownership of the `data` parcel pointer, and
             // passes ownership of the `reply` out parameter to its caller. It
             // does not affect ownership of the `binder` parameter.
             sys::AIBinder_transact(
                 self.as_native() as *mut sys::AIBinder,
                 code,
-                &mut input.into_raw(),
+                &mut data.into_raw(),
                 &mut reply,
                 flags,
             )
@@ -378,13 +384,17 @@
             // Safety: `SpIBinder` guarantees that `self` always contains a
             // valid pointer to an `AIBinder`. `recipient` can always be
             // converted into a valid pointer to an
-            // `AIBinder_DeathRecipient`. Any value is safe to pass as the
-            // cookie, although we depend on this value being set by
-            // `get_cookie` when the death recipient callback is called.
+            // `AIBinder_DeathRecipient`.
+            //
+            // The cookie is also the correct pointer, and by calling new_cookie,
+            // we have created a new ref-count to the cookie, which linkToDeath
+            // takes ownership of. Once the DeathRecipient is unlinked for any
+            // reason (including if this call fails), the onUnlinked callback
+            // will consume that ref-count.
             sys::AIBinder_linkToDeath(
                 self.as_native_mut(),
                 recipient.as_native_mut(),
-                recipient.get_cookie(),
+                recipient.new_cookie(),
             )
         })
     }
@@ -552,10 +562,20 @@
 }
 
 /// Rust wrapper around DeathRecipient objects.
+///
+/// The cookie in this struct represents an Arc<F> for the owned callback.
+/// This struct owns a ref-count of it, and so does every binder that we
+/// have been linked with.
 #[repr(C)]
 pub struct DeathRecipient {
     recipient: *mut sys::AIBinder_DeathRecipient,
-    callback: Box<dyn Fn() + Send + 'static>,
+    cookie: *mut c_void,
+    vtable: &'static DeathRecipientVtable,
+}
+
+struct DeathRecipientVtable {
+    cookie_incr_refcount: unsafe extern "C" fn(*mut c_void),
+    cookie_decr_refcount: unsafe extern "C" fn(*mut c_void),
 }
 
 impl DeathRecipient {
@@ -563,9 +583,9 @@
     /// associated object dies.
     pub fn new<F>(callback: F) -> DeathRecipient
     where
-        F: Fn() + Send + 'static,
+        F: Fn() + Send + Sync + 'static,
     {
-        let callback = Box::new(callback);
+        let callback: *const F = Arc::into_raw(Arc::new(callback));
         let recipient = unsafe {
             // Safety: The function pointer is a valid death recipient callback.
             //
@@ -574,34 +594,85 @@
             // no longer needed.
             sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
         };
+        unsafe {
+            // Safety: The function pointer is a valid onUnlinked callback.
+            //
+            // All uses of linkToDeath in this file correctly increment the
+            // ref-count that this onUnlinked callback will decrement.
+            sys::AIBinder_DeathRecipient_setOnUnlinked(recipient, Some(Self::cookie_decr_refcount::<F>));
+        }
         DeathRecipient {
             recipient,
-            callback,
+            cookie: callback as *mut c_void,
+            vtable: &DeathRecipientVtable {
+                cookie_incr_refcount: Self::cookie_incr_refcount::<F>,
+                cookie_decr_refcount: Self::cookie_decr_refcount::<F>,
+            },
         }
     }
 
+    /// Increment the ref-count for the cookie and return it.
+    ///
+    /// # Safety
+    ///
+    /// The caller must handle the returned ref-count correctly.
+    unsafe fn new_cookie(&self) -> *mut c_void {
+        (self.vtable.cookie_incr_refcount)(self.cookie);
+
+        // Return a raw pointer with ownership of a ref-count
+        self.cookie
+    }
+
     /// Get the opaque cookie that identifies this death recipient.
     ///
     /// This cookie will be used to link and unlink this death recipient to a
     /// binder object and will be passed to the `binder_died` callback as an
     /// opaque userdata pointer.
     fn get_cookie(&self) -> *mut c_void {
-        &*self.callback as *const _ as *mut c_void
+        self.cookie
     }
 
     /// Callback invoked from C++ when the binder object dies.
     ///
     /// # Safety
     ///
-    /// The `cookie` parameter must have been created with the `get_cookie`
-    /// method of this object.
+    /// The `cookie` parameter must be the cookie for an Arc<F> and
+    /// the caller must hold a ref-count to it.
     unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
     where
-        F: Fn() + Send + 'static,
+        F: Fn() + Send + Sync + 'static,
     {
-        let callback = (cookie as *mut F).as_ref().unwrap();
+        let callback = (cookie as *const F).as_ref().unwrap();
         callback();
     }
+
+    /// Callback that decrements the ref-count.
+    /// This is invoked from C++ when a binder is unlinked.
+    ///
+    /// # Safety
+    ///
+    /// The `cookie` parameter must be the cookie for an Arc<F> and
+    /// the owner must give up a ref-count to it.
+    unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+    where
+        F: Fn() + Send + Sync + 'static,
+    {
+        drop(Arc::from_raw(cookie as *const F));
+    }
+
+    /// Callback that increments the ref-count.
+    ///
+    /// # Safety
+    ///
+    /// The `cookie` parameter must be the cookie for an Arc<F> and
+    /// the owner must handle the created ref-count properly.
+    unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void)
+    where
+        F: Fn() + Send + Sync + 'static,
+    {
+        let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F));
+        mem::forget(Arc::clone(&arc));
+    }
 }
 
 /// # Safety
@@ -627,6 +698,12 @@
             // `AIBinder_DeathRecipient_new` when `self` was created. This
             // delete method can only be called once when `self` is dropped.
             sys::AIBinder_DeathRecipient_delete(self.recipient);
+
+            // Safety: We own a ref-count to the cookie, and so does every
+            // linked binder. This call gives up our ref-count. The linked
+            // binders should already have given up their ref-count, or should
+            // do so shortly.
+            (self.vtable.cookie_decr_refcount)(self.cookie)
         }
     }
 }
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index da8907d..335e8d8 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -363,13 +363,58 @@
         );
     }
 
-    fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
+    struct Bools {
+        binder_died: Arc<AtomicBool>,
+        binder_dealloc: Arc<AtomicBool>,
+    }
+
+    impl Bools {
+        fn is_dead(&self) -> bool {
+            self.binder_died.load(Ordering::Relaxed)
+        }
+        fn assert_died(&self) {
+            assert!(
+                self.is_dead(),
+                "Did not receive death notification"
+            );
+        }
+        fn assert_dropped(&self) {
+            assert!(
+                self.binder_dealloc.load(Ordering::Relaxed),
+                "Did not dealloc death notification"
+            );
+        }
+        fn assert_not_dropped(&self) {
+            assert!(
+                !self.binder_dealloc.load(Ordering::Relaxed),
+                "Dealloc death notification too early"
+            );
+        }
+    }
+
+    fn register_death_notification(binder: &mut SpIBinder) -> (Bools, DeathRecipient) {
         let binder_died = Arc::new(AtomicBool::new(false));
+        let binder_dealloc = Arc::new(AtomicBool::new(false));
+
+        struct SetOnDrop {
+            binder_dealloc: Arc<AtomicBool>,
+        }
+        impl Drop for SetOnDrop {
+            fn drop(&mut self) {
+                self.binder_dealloc.store(true, Ordering::Relaxed);
+            }
+        }
 
         let mut death_recipient = {
             let flag = binder_died.clone();
+            let set_on_drop = SetOnDrop {
+                binder_dealloc: binder_dealloc.clone(),
+            };
             DeathRecipient::new(move || {
                 flag.store(true, Ordering::Relaxed);
+                // Force the closure to take ownership of set_on_drop. When the closure is
+                // dropped, the destructor of `set_on_drop` will run.
+                let _ = &set_on_drop;
             })
         };
 
@@ -377,7 +422,12 @@
             .link_to_death(&mut death_recipient)
             .expect("link_to_death failed");
 
-        (binder_died, death_recipient)
+        let bools = Bools {
+            binder_died,
+            binder_dealloc,
+        };
+
+        (bools, death_recipient)
     }
 
     /// Killing a remote service should unregister the service and trigger
@@ -390,7 +440,7 @@
         let service_process = ScopedServiceProcess::new(service_name);
         let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
 
-        let (binder_died, _recipient) = register_death_notification(&mut remote);
+        let (bools, recipient) = register_death_notification(&mut remote);
 
         drop(service_process);
         remote
@@ -400,10 +450,12 @@
         // Pause to ensure any death notifications get delivered
         thread::sleep(Duration::from_secs(1));
 
-        assert!(
-            binder_died.load(Ordering::Relaxed),
-            "Did not receive death notification"
-        );
+        bools.assert_died();
+        bools.assert_not_dropped();
+
+        drop(recipient);
+
+        bools.assert_dropped();
     }
 
     /// Test unregistering death notifications.
@@ -415,7 +467,7 @@
         let service_process = ScopedServiceProcess::new(service_name);
         let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
 
-        let (binder_died, mut recipient) = register_death_notification(&mut remote);
+        let (bools, mut recipient) = register_death_notification(&mut remote);
 
         remote
             .unlink_to_death(&mut recipient)
@@ -430,9 +482,13 @@
         thread::sleep(Duration::from_secs(1));
 
         assert!(
-            !binder_died.load(Ordering::Relaxed),
+            !bools.is_dead(),
             "Received unexpected death notification after unlinking",
         );
+
+        bools.assert_not_dropped();
+        drop(recipient);
+        bools.assert_dropped();
     }
 
     /// Dropping a remote handle should unregister any death notifications.
@@ -444,7 +500,7 @@
         let service_process = ScopedServiceProcess::new(service_name);
         let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
 
-        let (binder_died, _recipient) = register_death_notification(&mut remote);
+        let (bools, recipient) = register_death_notification(&mut remote);
 
         // This should automatically unregister our death notification.
         drop(remote);
@@ -457,9 +513,13 @@
         // We dropped the remote handle, so we should not receive the death
         // notification when the remote process dies here.
         assert!(
-            !binder_died.load(Ordering::Relaxed),
+            !bools.is_dead(),
             "Received unexpected death notification after dropping remote handle"
         );
+
+        bools.assert_not_dropped();
+        drop(recipient);
+        bools.assert_dropped();
     }
 
     /// Test IBinder interface methods not exercised elsewhere.
@@ -637,4 +697,27 @@
         assert!(!(service1 > service1));
         assert_eq!(service1 < service2, !(service2 < service1));
     }
+
+    #[test]
+    fn binder_parcel_mixup() {
+        let service1 = BnTest::new_binder(
+            TestService::new("testing_service1"),
+            BinderFeatures::default(),
+        );
+        let service2 = BnTest::new_binder(
+            TestService::new("testing_service2"),
+            BinderFeatures::default(),
+        );
+
+        let service1 = service1.as_binder();
+        let service2 = service2.as_binder();
+
+        let parcel = service1.prepare_transact().unwrap();
+        let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+
+        match res {
+            Ok(_) => panic!("submit_transact should fail"),
+            Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE),
+        }
+    }
 }
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 3f72b8f..eec3b44 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -75,10 +75,10 @@
         auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
         ASSERT_THAT(debuggableResult, Ok());
         ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
-        auto debuggableBool = ParseBool(Trim(debuggableResult->stdout));
-        ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdout);
+        auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr));
+        ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr);
         if (debuggableBool == ParseBoolResult::kFalse) {
-            GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdout);
+            GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdoutStr);
         }
 
         auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index e430c28..55aa57b 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -211,10 +211,9 @@
     for (size_t tries = 0; tries < 5; tries++) {
         usleep(10000);
         status = gSession->setupUnixDomainClient(addr.c_str());
-        if (status == OK) goto success;
+        if (status == OK) break;
     }
-    LOG(FATAL) << "Could not connect: " << statusToString(status).c_str();
-success:
+    CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status).c_str();
 
     ::benchmark::RunSpecifiedBenchmarks();
     return 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 2fd63a3..6bcf102 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -615,25 +615,20 @@
                     status = session->setupPreconnectedClient({}, [=]() {
                         return connectTo(UnixSocketAddress(addr.c_str()));
                     });
-                    if (status == OK) goto success;
                     break;
                 case SocketType::UNIX:
                     status = session->setupUnixDomainClient(addr.c_str());
-                    if (status == OK) goto success;
                     break;
                 case SocketType::VSOCK:
                     status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
-                    if (status == OK) goto success;
                     break;
                 case SocketType::INET:
                     status = session->setupInetClient("127.0.0.1", serverInfo.port);
-                    if (status == OK) goto success;
                     break;
                 default:
                     LOG_ALWAYS_FATAL("Unknown socket type");
             }
-            LOG_ALWAYS_FATAL("Could not connect %s", statusToString(status).c_str());
-        success:
+            CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
             ret.sessions.push_back({session, session->getRootObject()});
         }
         return ret;
@@ -1436,13 +1431,32 @@
                         BinderRpcSimple::PrintTestParam);
 
 class RpcTransportTest
-      : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, RpcCertificateFormat>> {
+      : public ::testing::TestWithParam<
+                std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>> {
 public:
     using ConnectToServer = std::function<base::unique_fd()>;
     static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
         auto [socketType, rpcSecurity, certificateFormat] = info.param;
-        return PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString() + "_" +
-                PrintToString(certificateFormat);
+        auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
+        if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat);
+        return ret;
+    }
+    static std::vector<ParamType> getRpcTranportTestParams() {
+        std::vector<RpcTransportTest::ParamType> ret;
+        for (auto socketType : testSocketTypes(false /* hasPreconnected */)) {
+            for (auto rpcSecurity : RpcSecurityValues()) {
+                switch (rpcSecurity) {
+                    case RpcSecurity::RAW: {
+                        ret.emplace_back(socketType, rpcSecurity, std::nullopt);
+                    } break;
+                    case RpcSecurity::TLS: {
+                        ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM);
+                        ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER);
+                    } break;
+                }
+            }
+        }
+        return ret;
     }
     void TearDown() override {
         for (auto& server : mServers) server->shutdownAndWait();
@@ -1642,8 +1656,9 @@
     status_t trust(A* a, B* b) {
         auto [socketType, rpcSecurity, certificateFormat] = GetParam();
         if (rpcSecurity != RpcSecurity::TLS) return OK;
-        auto bCert = b->getCtx()->getCertificate(certificateFormat);
-        return a->getCertVerifier()->addTrustedPeerCertificate(certificateFormat, bCert);
+        LOG_ALWAYS_FATAL_IF(!certificateFormat.has_value());
+        auto bCert = b->getCtx()->getCertificate(*certificateFormat);
+        return a->getCertVerifier()->addTrustedPeerCertificate(*certificateFormat, bCert);
     }
 
     static constexpr const char* kMessage = "hello";
@@ -1799,40 +1814,29 @@
 
     server->setPostConnect(serverPostConnect);
 
-    // Start server
     server->start();
     // connect() to server and do handshake
     ASSERT_TRUE(client.setUpTransport());
-    // read the first message. This confirms that server has finished handshake and start handling
-    // client fd. Server thread should pause at waitForWriteBarrier.
+    // read the first message. This ensures that server has finished handshake and start handling
+    // client fd. Server thread should pause at writeCv.wait_for().
     ASSERT_TRUE(client.readMessage(kMessage));
     // Trigger server shutdown after server starts handling client FD. This ensures that the second
     // write is on an FdTrigger that has been shut down.
     server->shutdown();
     // Continues server thread to write the second message.
     {
-        std::unique_lock<std::mutex> lock(writeMutex);
+        std::lock_guard<std::mutex> lock(writeMutex);
         shouldContinueWriting = true;
-        lock.unlock();
-        writeCv.notify_all();
     }
+    writeCv.notify_all();
     // After this line, server thread unblocks and attempts to write the second message, but
     // shutdown is triggered, so write should failed with -ECANCELLED. See |serverPostConnect|.
     // On the client side, second read fails with DEAD_OBJECT
     ASSERT_FALSE(client.readMessage(msg2));
 }
 
-std::vector<RpcCertificateFormat> testRpcCertificateFormats() {
-    return {
-            RpcCertificateFormat::PEM,
-            RpcCertificateFormat::DER,
-    };
-}
-
 INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest,
-                        ::testing::Combine(::testing::ValuesIn(testSocketTypes(false)),
-                                           ::testing::ValuesIn(RpcSecurityValues()),
-                                           ::testing::ValuesIn(testRpcCertificateFormats())),
+                        ::testing::ValuesIn(RpcTransportTest::getRpcTranportTestParams()),
                         RpcTransportTest::PrintParamInfo);
 
 } // namespace android
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index fb24836..4330e3e 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -34,7 +34,7 @@
     auto result = execute({"echo", "foo"}, nullptr);
     ASSERT_THAT(result, Ok());
     EXPECT_THAT(result->exitCode, Optional(EX_OK));
-    EXPECT_EQ(result->stdout, "foo\n");
+    EXPECT_EQ(result->stdoutStr, "foo\n");
 }
 
 TEST(UtilsHost, ExecuteLongRunning) {
@@ -44,7 +44,7 @@
         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) {
-            return android::base::EndsWith(commandResult.stdout, "\n");
+            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();
@@ -53,7 +53,7 @@
 
         ASSERT_THAT(result, Ok());
         EXPECT_EQ(std::nullopt, result->exitCode);
-        EXPECT_EQ(result->stdout, "foo\n");
+        EXPECT_EQ(result->stdoutStr, "foo\n");
     }
 
     // ~CommandResult() called, child process is killed.
@@ -70,7 +70,7 @@
         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) {
-            return android::base::EndsWith(commandResult.stdout, "\n");
+            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();
@@ -79,7 +79,7 @@
 
         ASSERT_THAT(result, Ok());
         EXPECT_EQ(std::nullopt, result->exitCode);
-        EXPECT_EQ(result->stdout, "foo\n");
+        EXPECT_EQ(result->stdoutStr, "foo\n");
     }
 
     // ~CommandResult() called, child process is killed.
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index 95582bf..20c5569 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -54,10 +54,9 @@
     for (size_t tries = 0; tries < 5; tries++) {
         usleep(10000);
         status = session->setupUnixDomainClient(addr.c_str());
-        if (status == OK) goto success;
+        if (status == OK) break;
     }
-    LOG(FATAL) << "Unable to connect";
-success:
+    CHECK_EQ(status, OK) << "Unable to connect";
 
     sp<BpBinder> bpBinder = session->getRootObject()->remoteBinder();