Merge "Capture MSKPs from RE more reliably" into sc-dev am: 84c78d553d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/14722044

Change-Id: I4db717a08fe3edd30844676c41828967eb6b2cad
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index ba1c449..83a52b8 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -24,6 +24,7 @@
 #include <android-base/unique_fd.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
+#include <binder/Stability.h>
 #include <binder/TextOutput.h>
 #include <binderdebug/BinderDebug.h>
 #include <serviceutils/PriorityDumper.h>
@@ -69,12 +70,13 @@
             "         -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
             "         -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
             "         --pid: dump PID instead of usual dump\n"
-            "         --thread: dump thread usage instead of usual dump\n"
             "         --proto: filter services that support dumping data in proto format. Dumps\n"
             "               will be in proto format.\n"
             "         --priority LEVEL: filter services based on specified priority\n"
             "               LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
             "         --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
+            "         --stability: dump binder stability information instead of usual dump\n"
+            "         --thread: dump thread usage instead of usual dump\n"
             "         SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
 }
 
@@ -128,12 +130,13 @@
     Type type = Type::DUMP;
     int timeoutArgMs = 10000;
     int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
-    static struct option longOptions[] = {{"thread", no_argument, 0, 0},
+    static struct option longOptions[] = {{"help", no_argument, 0, 0},
                                           {"pid", no_argument, 0, 0},
                                           {"priority", required_argument, 0, 0},
                                           {"proto", no_argument, 0, 0},
                                           {"skip", no_argument, 0, 0},
-                                          {"help", no_argument, 0, 0},
+                                          {"stability", no_argument, 0, 0},
+                                          {"thread", no_argument, 0, 0},
                                           {0, 0, 0, 0}};
 
     // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
@@ -167,6 +170,8 @@
                 }
             } else if (!strcmp(longOptions[optionIndex].name, "pid")) {
                 type = Type::PID;
+            } else if (!strcmp(longOptions[optionIndex].name, "stability")) {
+                type = Type::STABILITY;
             } else if (!strcmp(longOptions[optionIndex].name, "thread")) {
                 type = Type::THREAD;
             }
@@ -335,6 +340,11 @@
      return OK;
 }
 
+static status_t dumpStabilityToFd(const sp<IBinder>& service, const unique_fd& fd) {
+     WriteStringToFd(internal::Stability::debugToString(service) + "\n", fd);
+     return OK;
+}
+
 static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) {
     pid_t pid;
     status_t status = service->getDebugPid(&pid);
@@ -382,6 +392,9 @@
         case Type::PID:
             err = dumpPidToFd(service, remote_end);
             break;
+        case Type::STABILITY:
+            err = dumpStabilityToFd(service, remote_end);
+            break;
         case Type::THREAD:
             err = dumpThreadsToFd(service, remote_end);
             break;
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 349947c..1b3ae6a 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -52,9 +52,10 @@
     static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags);
 
     enum class Type {
-        DUMP,    // dump using `dump` function
-        PID,     // dump pid of server only
-        THREAD,  // dump thread usage of server only
+        DUMP,      // dump using `dump` function
+        PID,       // dump pid of server only
+        STABILITY, // dump stability information of server
+        THREAD,    // dump thread usage of server only
     };
 
     /**
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index c9d2dbb..277f445 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -582,6 +582,27 @@
     AssertOutput(std::to_string(getpid()) + "\n");
 }
 
+// Tests 'dumpsys --stability'
+TEST_F(DumpsysTest, ListAllServicesWithStability) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet");
+
+    CallMain({"--stability"});
+
+    AssertRunningServices({"Locksmith", "Valet"});
+    AssertOutputContains("stability");
+}
+
+// Tests 'dumpsys --stability service_name'
+TEST_F(DumpsysTest, ListServiceWithStability) {
+    ExpectCheckService("Locksmith");
+
+    CallMain({"--stability", "Locksmith"});
+
+    AssertOutputContains("stability");
+}
+
 // Tests 'dumpsys --thread'
 TEST_F(DumpsysTest, ListAllServicesWithThread) {
     ExpectListServices({"Locksmith", "Valet"});
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index be260e8..4b12927 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -64,6 +64,9 @@
     "PermissionCache.cpp",
     "PermissionController.cpp",
 ]
+libbinder_no_vendor_interface_sources = [
+    ":packagemanager_aidl",
+]
 
 cc_library {
     name: "libbinder",
@@ -120,9 +123,8 @@
         "Status.cpp",
         "TextOutput.cpp",
         "Utils.cpp",
-        ":packagemanager_aidl",
         ":libbinder_aidl",
-    ],
+    ] + libbinder_no_vendor_interface_sources,
 
     target: {
         android: {
@@ -134,7 +136,7 @@
             },
         },
         vendor: {
-            exclude_srcs: libbinder_device_interface_sources,
+            exclude_srcs: libbinder_device_interface_sources + libbinder_no_vendor_interface_sources,
         },
         darwin: {
             enabled: false,
@@ -162,6 +164,10 @@
         binder32bit: {
             cflags: ["-DBINDER_IPC_32BIT=1"],
         },
+
+        debuggable: {
+            cflags: ["-DBINDER_RPC_DEV_SERVERS"],
+        },
     },
 
     shared_libs: [
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d5bdd1c..c83c383 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -17,16 +17,24 @@
 #include <binder/Binder.h>
 
 #include <atomic>
-#include <utils/misc.h>
+
+#include <android-base/unique_fd.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
 #include <binder/IShellCallback.h>
 #include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <private/android_filesystem_config.h>
+#include <utils/misc.h>
 
+#include <inttypes.h>
 #include <linux/sched.h>
 #include <stdio.h>
 
+#include "RpcState.h"
+
 namespace android {
 
 // Service implementations inherit from BBinder and IBinder, and this is frozen
@@ -39,6 +47,12 @@
 static_assert(sizeof(BBinder) == 20);
 #endif
 
+#ifdef BINDER_RPC_DEV_SERVERS
+constexpr const bool kEnableRpcDevServers = true;
+#else
+constexpr const bool kEnableRpcDevServers = false;
+#endif
+
 // ---------------------------------------------------------------------------
 
 IBinder::IBinder()
@@ -136,6 +150,33 @@
     return OK;
 }
 
+status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd, uint32_t maxRpcThreads) {
+    if constexpr (!kEnableRpcDevServers) {
+        ALOGW("setRpcClientDebug disallowed because RPC is not enabled");
+        return INVALID_OPERATION;
+    }
+
+    BBinder* local = this->localBinder();
+    if (local != nullptr) {
+        return local->BBinder::setRpcClientDebug(std::move(socketFd), maxRpcThreads);
+    }
+
+    BpBinder* proxy = this->remoteBinder();
+    LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote");
+
+    Parcel data;
+    Parcel reply;
+    status_t status;
+    if (status = data.writeBool(socketFd.ok()); status != OK) return status;
+    if (socketFd.ok()) {
+        // writeUniqueFileDescriptor currently makes an unnecessary dup().
+        status = data.writeFileDescriptor(socketFd.release(), true /* own */);
+        if (status != OK) return status;
+    }
+    if (status = data.writeUint32(maxRpcThreads); status != OK) return status;
+    return transact(SET_RPC_CLIENT_TRANSACTION, data, &reply);
+}
+
 // ---------------------------------------------------------------------------
 
 class BBinder::Extras
@@ -150,6 +191,7 @@
 
     // for below objects
     Mutex mLock;
+    sp<RpcServer> mRpcServer;
     BpBinder::ObjectManager mObjects;
 };
 
@@ -199,6 +241,10 @@
         case DEBUG_PID_TRANSACTION:
             err = reply->writeInt32(getDebugPid());
             break;
+        case SET_RPC_CLIENT_TRANSACTION: {
+            err = setRpcClientDebug(data);
+            break;
+        }
         default:
             err = onTransact(code, data, reply, flags);
             break;
@@ -368,6 +414,75 @@
     e->mExtension = extension;
 }
 
+status_t BBinder::setRpcClientDebug(const Parcel& data) {
+    if constexpr (!kEnableRpcDevServers) {
+        ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid != AID_ROOT) {
+        ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid);
+        return PERMISSION_DENIED;
+    }
+    status_t status;
+    bool hasSocketFd;
+    android::base::unique_fd clientFd;
+    uint32_t maxRpcThreads;
+
+    if (status = data.readBool(&hasSocketFd); status != OK) return status;
+    if (hasSocketFd) {
+        if (status = data.readUniqueFileDescriptor(&clientFd); status != OK) return status;
+    }
+    if (status = data.readUint32(&maxRpcThreads); status != OK) return status;
+
+    return setRpcClientDebug(std::move(clientFd), maxRpcThreads);
+}
+
+status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, uint32_t maxRpcThreads) {
+    if constexpr (!kEnableRpcDevServers) {
+        ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    const int socketFdForPrint = socketFd.get();
+    LOG_RPC_DETAIL("%s(%d, %" PRIu32 ")", __PRETTY_FUNCTION__, socketFdForPrint, maxRpcThreads);
+
+    if (!socketFd.ok()) {
+        ALOGE("%s: No socket FD provided.", __PRETTY_FUNCTION__);
+        return BAD_VALUE;
+    }
+    if (maxRpcThreads <= 0) {
+        ALOGE("%s: RPC is useless with %" PRIu32 " threads.", __PRETTY_FUNCTION__, maxRpcThreads);
+        return BAD_VALUE;
+    }
+
+    // TODO(b/182914638): RPC and binder should share the same thread pool count.
+    size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxThreadCount();
+    if (binderThreadPoolMaxCount <= 1) {
+        ALOGE("%s: ProcessState thread pool max count is %zu. RPC is disabled for this service "
+              "because RPC requires the service to support multithreading.",
+              __PRETTY_FUNCTION__, binderThreadPoolMaxCount);
+        return INVALID_OPERATION;
+    }
+
+    Extras* e = getOrCreateExtras();
+    AutoMutex _l(e->mLock);
+    if (e->mRpcServer != nullptr) {
+        ALOGE("%s: Already have RPC client", __PRETTY_FUNCTION__);
+        return ALREADY_EXISTS;
+    }
+    e->mRpcServer = RpcServer::make();
+    LOG_ALWAYS_FATAL_IF(e->mRpcServer == nullptr, "RpcServer::make returns null");
+    e->mRpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    // Weak ref to avoid circular dependency: BBinder -> RpcServer -X-> BBinder
+    e->mRpcServer->setRootObjectWeak(wp<BBinder>::fromExisting(this));
+    e->mRpcServer->setupExternalServer(std::move(socketFd));
+    e->mRpcServer->start();
+    LOG_RPC_DETAIL("%s(%d, %" PRIu32 ") successful", __PRETTY_FUNCTION__, socketFdForPrint,
+                   maxRpcThreads);
+    return OK;
+}
+
 BBinder::~BBinder()
 {
     Extras* e = mExtras.load(std::memory_order_relaxed);
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 1dcb94c..5e44a0f 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -273,7 +273,8 @@
 
         status_t status;
         if (CC_UNLIKELY(isRpcBinder())) {
-            status = rpcSession()->transact(rpcAddress(), code, data, reply, flags);
+            status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply,
+                                            flags);
         } else {
             status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
         }
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 445df9e..fa9f3a9 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,19 +366,46 @@
 
 pid_t IPCThreadState::getCallingPid() const
 {
+    checkContextIsBinderForUse(__func__);
     return mCallingPid;
 }
 
 const char* IPCThreadState::getCallingSid() const
 {
+    checkContextIsBinderForUse(__func__);
     return mCallingSid;
 }
 
 uid_t IPCThreadState::getCallingUid() const
 {
+    checkContextIsBinderForUse(__func__);
     return mCallingUid;
 }
 
+const IPCThreadState::SpGuard* IPCThreadState::pushGetCallingSpGuard(const SpGuard* guard) {
+    const SpGuard* orig = mServingStackPointerGuard;
+    mServingStackPointerGuard = guard;
+    return orig;
+}
+
+void IPCThreadState::restoreGetCallingSpGuard(const SpGuard* guard) {
+    mServingStackPointerGuard = guard;
+}
+
+void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
+    if (LIKELY(mServingStackPointerGuard == nullptr)) return;
+
+    if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) {
+        LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).",
+                         mServingStackPointerGuard->context, use, mServingStackPointer,
+                         mServingStackPointerGuard->address);
+    }
+
+    // in the case mServingStackPointer is deeper in the stack than the guard,
+    // we must be serving a binder transaction (maybe nested). This is a binder
+    // context, so we don't abort
+}
+
 int64_t IPCThreadState::clearCallingIdentity()
 {
     // ignore mCallingSid for legacy reasons
@@ -845,6 +872,7 @@
 IPCThreadState::IPCThreadState()
       : mProcess(ProcessState::self()),
         mServingStackPointer(nullptr),
+        mServingStackPointerGuard(nullptr),
         mWorkSource(kUnsetWorkSource),
         mPropagateWorkSource(false),
         mIsLooper(false),
@@ -1226,7 +1254,7 @@
                 tr.offsets_size/sizeof(binder_size_t), freeBuffer);
 
             const void* origServingStackPointer = mServingStackPointer;
-            mServingStackPointer = &origServingStackPointer; // anything on the stack
+            mServingStackPointer = __builtin_frame_address(0);
 
             const pid_t origPid = mCallingPid;
             const char* origSid = mCallingSid;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index f684cf6..47dd32e 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -320,14 +320,18 @@
     const std::string name = String8(name16).c_str();
 
     sp<IBinder> out;
-    if (!mTheRealServiceManager->getService(name, &out).isOk()) {
+    if (Status status = mTheRealServiceManager->getService(name, &out); !status.isOk()) {
+        ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
+              status.toString8().c_str());
         return nullptr;
     }
     if (out != nullptr) return out;
 
     sp<Waiter> waiter = sp<Waiter>::make();
-    if (!mTheRealServiceManager->registerForNotifications(
-            name, waiter).isOk()) {
+    if (Status status = mTheRealServiceManager->registerForNotifications(name, waiter);
+        !status.isOk()) {
+        ALOGW("Failed to registerForNotifications in waitForService for %s: %s", name.c_str(),
+              status.toString8().c_str());
         return nullptr;
     }
     Defer unregister ([&] {
@@ -360,7 +364,9 @@
         // - init gets death signal, but doesn't know it needs to restart
         //   the service
         // - we need to request service again to get it to start
-        if (!mTheRealServiceManager->getService(name, &out).isOk()) {
+        if (Status status = mTheRealServiceManager->getService(name, &out); !status.isOk()) {
+            ALOGW("Failed to getService in waitForService on later try for %s: %s", name.c_str(),
+                  status.toString8().c_str());
             return nullptr;
         }
         if (out != nullptr) return out;
@@ -369,7 +375,10 @@
 
 bool ServiceManagerShim::isDeclared(const String16& name) {
     bool declared;
-    if (!mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared).isOk()) {
+    if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
+        !status.isOk()) {
+        ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+              status.toString8().c_str());
         return false;
     }
     return declared;
@@ -377,7 +386,11 @@
 
 Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
     std::vector<std::string> out;
-    if (!mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out).isOk()) {
+    if (Status status =
+                mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out);
+        !status.isOk()) {
+        ALOGW("Failed to getDeclaredInstances for %s: %s", String8(interface).c_str(),
+              status.toString8().c_str());
         return {};
     }
 
@@ -391,7 +404,10 @@
 
 std::optional<String16> ServiceManagerShim::updatableViaApex(const String16& name) {
     std::optional<std::string> declared;
-    if (!mTheRealServiceManager->updatableViaApex(String8(name).c_str(), &declared).isOk()) {
+    if (Status status = mTheRealServiceManager->updatableViaApex(String8(name).c_str(), &declared);
+        !status.isOk()) {
+        ALOGW("Failed to get updatableViaApex for %s: %s", String8(name).c_str(),
+              status.toString8().c_str());
         return std::nullopt;
     }
     return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index ee834ea..ac8867a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1466,6 +1466,29 @@
     return nullptr;
 }
 
+status_t Parcel::readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const {
+    if (status_t status = readInt32(size); status != OK) return status;
+    if (*size < 0) return OK; // may be null, client to handle
+
+    LOG_ALWAYS_FATAL_IF(elmSize > INT32_MAX, "Cannot have element as big as %zu", elmSize);
+
+    // approximation, can't know max element size (e.g. if it makes heap
+    // allocations)
+    static_assert(sizeof(int) == sizeof(int32_t), "Android is LP64");
+    int32_t allocationSize;
+    if (__builtin_smul_overflow(elmSize, *size, &allocationSize)) return NO_MEMORY;
+
+    // High limit of 1MB since something this big could never be returned. Could
+    // probably scope this down, but might impact very specific usecases.
+    constexpr int32_t kMaxAllocationSize = 1 * 1000 * 1000;
+
+    if (allocationSize >= kMaxAllocationSize) {
+        return NO_MEMORY;
+    }
+
+    return OK;
+}
+
 template<class T>
 status_t Parcel::readAligned(T *pArg) const {
     static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index bade918..650a108 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -359,6 +359,14 @@
     return result;
 }
 
+size_t ProcessState::getThreadPoolMaxThreadCount() const {
+    // may actually be one more than this, if join is called
+    if (mThreadPoolStarted) return mMaxThreads;
+    // must not be initialized or maybe has poll thread setup, we
+    // currently don't track this in libbinder
+    return 0;
+}
+
 status_t ProcessState::enableOnewaySpamDetection(bool enable) {
     uint32_t enableDetection = enable ? 1 : 0;
     if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 9cc6e7f..d8ba2c6 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -26,9 +26,9 @@
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <log/log.h>
-#include "RpcState.h"
 
 #include "RpcSocketAddress.h"
+#include "RpcState.h"
 #include "RpcWireFormat.h"
 
 namespace android {
@@ -37,7 +37,9 @@
 using base::unique_fd;
 
 RpcServer::RpcServer() {}
-RpcServer::~RpcServer() {}
+RpcServer::~RpcServer() {
+    (void)shutdown();
+}
 
 sp<RpcServer> RpcServer::make() {
     return sp<RpcServer>::make();
@@ -99,7 +101,7 @@
 
 void RpcServer::setMaxThreads(size_t threads) {
     LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads");
-    LOG_ALWAYS_FATAL_IF(mStarted, "must be called before started");
+    LOG_ALWAYS_FATAL_IF(mJoinThreadRunning, "Cannot set max threads while running");
     mMaxThreads = threads;
 }
 
@@ -126,16 +128,43 @@
     return ret;
 }
 
+static void joinRpcServer(sp<RpcServer>&& thiz) {
+    thiz->join();
+}
+
+void RpcServer::start() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!");
+    mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
+}
+
 void RpcServer::join() {
-    while (true) {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+        LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join.");
+        LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
+        mJoinThreadRunning = true;
+        mShutdownTrigger = RpcSession::FdTrigger::make();
+        LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Cannot create join signaler");
+    }
+
+    status_t status;
+    while ((status = mShutdownTrigger->triggerablePollRead(mServer)) == OK) {
         (void)acceptOne();
     }
+    LOG_RPC_DETAIL("RpcServer::join exiting with %s", statusToString(status).c_str());
+
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+        mJoinThreadRunning = false;
+    }
+    mShutdownCv.notify_all();
 }
 
 bool RpcServer::acceptOne() {
-    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
-    LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join.");
-
     unique_fd clientFd(
             TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
 
@@ -147,15 +176,46 @@
 
     {
         std::lock_guard<std::mutex> _l(mLock);
-        std::thread thread =
-                std::thread(&RpcServer::establishConnection, this,
-                            std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+        std::thread thread = std::thread(&RpcServer::establishConnection,
+                                         sp<RpcServer>::fromExisting(this), std::move(clientFd));
         mConnectingThreads[thread.get_id()] = std::move(thread);
     }
 
     return true;
 }
 
+bool RpcServer::shutdown() {
+    std::unique_lock<std::mutex> _l(mLock);
+    if (mShutdownTrigger == nullptr) {
+        LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed.");
+        return false;
+    }
+
+    mShutdownTrigger->trigger();
+    while (mJoinThreadRunning || !mConnectingThreads.empty() || !mSessions.empty()) {
+        if (std::cv_status::timeout == mShutdownCv.wait_for(_l, std::chrono::seconds(1))) {
+            ALOGE("Waiting for RpcServer to shut down (1s w/o progress). Join thread running: %d, "
+                  "Connecting threads: "
+                  "%zu, Sessions: %zu. Is your server deadlocked?",
+                  mJoinThreadRunning, mConnectingThreads.size(), mSessions.size());
+        }
+    }
+
+    // At this point, we know join() is about to exit, but the thread that calls
+    // join() may not have exited yet.
+    // If RpcServer owns the join thread (aka start() is called), make sure the thread exits;
+    // otherwise ~thread() may call std::terminate(), which may crash the process.
+    // If RpcServer does not own the join thread (aka join() is called directly),
+    // then the owner of RpcServer is responsible for cleaning up that thread.
+    if (mJoinThread.get()) {
+        mJoinThread->join();
+        mJoinThread.reset();
+    }
+
+    mShutdownTrigger = nullptr;
+    return true;
+}
+
 std::vector<sp<RpcSession>> RpcServer::listSessions() {
     std::lock_guard<std::mutex> _l(mLock);
     std::vector<sp<RpcSession>> sessions;
@@ -172,44 +232,55 @@
 }
 
 void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
-    LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object");
-
     // TODO(b/183988761): cannot trust this simple ID
-    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
-    bool idValid = true;
+    LOG_ALWAYS_FATAL_IF(!server->mAgreedExperimental, "no!");
+
+    // mShutdownTrigger can only be cleared once connection threads have joined.
+    // It must be set before this thread is started
+    LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
+
     int32_t id;
-    if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
-        ALOGE("Could not read ID from fd %d", clientFd.get());
-        idValid = false;
+    status_t status =
+            server->mShutdownTrigger->interruptableReadFully(clientFd.get(), &id, sizeof(id));
+    bool idValid = status == OK;
+    if (!idValid) {
+        ALOGE("Failed to read ID for client connecting to RPC server: %s",
+              statusToString(status).c_str());
+        // still need to cleanup before we can return
     }
 
     std::thread thisThread;
     sp<RpcSession> session;
     {
-        std::lock_guard<std::mutex> _l(mLock);
+        std::unique_lock<std::mutex> _l(server->mLock);
 
-        auto threadId = mConnectingThreads.find(std::this_thread::get_id());
-        LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+        auto threadId = server->mConnectingThreads.find(std::this_thread::get_id());
+        LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(),
                             "Must establish connection on owned thread");
         thisThread = std::move(threadId->second);
-        ScopeGuard detachGuard = [&]() { thisThread.detach(); };
-        mConnectingThreads.erase(threadId);
+        ScopeGuard detachGuard = [&]() {
+            thisThread.detach();
+            _l.unlock();
+            server->mShutdownCv.notify_all();
+        };
+        server->mConnectingThreads.erase(threadId);
 
         if (!idValid) {
             return;
         }
 
         if (id == RPC_SESSION_ID_NEW) {
-            LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
-            mSessionIdCounter++;
+            LOG_ALWAYS_FATAL_IF(server->mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+            server->mSessionIdCounter++;
 
             session = RpcSession::make();
-            session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+            session->setForServer(wp<RpcServer>(server), server->mSessionIdCounter,
+                                  server->mShutdownTrigger);
 
-            mSessions[mSessionIdCounter] = session;
+            server->mSessions[server->mSessionIdCounter] = session;
         } else {
-            auto it = mSessions.find(id);
-            if (it == mSessions.end()) {
+            auto it = server->mSessions.find(id);
+            if (it == server->mSessions.end()) {
                 ALOGE("Cannot add thread, no record of session with ID %d", id);
                 return;
             }
@@ -222,10 +293,6 @@
 
     // avoid strong cycle
     server = nullptr;
-    //
-    //
-    // DO NOT ACCESS MEMBER VARIABLES BELOW
-    //
 
     session->join(std::move(clientFd));
 }
@@ -255,7 +322,10 @@
 
     LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
 
-    mServer = std::move(serverFd);
+    if (!setupExternalServer(std::move(serverFd))) {
+        ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
+        return false;
+    }
     return true;
 }
 
@@ -271,6 +341,11 @@
     (void)mSessions.erase(it);
 }
 
+void RpcServer::onSessionThreadEnding(const sp<RpcSession>& session) {
+    (void)session;
+    mShutdownCv.notify_all();
+}
+
 bool RpcServer::hasServer() {
     LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
     std::lock_guard<std::mutex> _l(mLock);
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 05fa49e..156a834 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -19,10 +19,12 @@
 #include <binder/RpcSession.h>
 
 #include <inttypes.h>
+#include <poll.h>
 #include <unistd.h>
 
 #include <string_view>
 
+#include <android-base/macros.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/Stability.h>
@@ -84,8 +86,7 @@
         return false;
     }
 
-    addClientConnection(std::move(serverFd));
-    return true;
+    return addClientConnection(std::move(serverFd));
 }
 
 sp<IBinder> RpcSession::getRootObject() {
@@ -98,12 +99,12 @@
     return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads);
 }
 
-status_t RpcSession::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+status_t RpcSession::transact(const sp<IBinder>& binder, uint32_t code, const Parcel& data,
                               Parcel* reply, uint32_t flags) {
     ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
                                    (flags & IBinder::FLAG_ONEWAY) ? ConnectionUse::CLIENT_ASYNC
                                                                   : ConnectionUse::CLIENT);
-    return state()->transact(connection.fd(), address, code, data,
+    return state()->transact(connection.fd(), binder, code, data,
                              sp<RpcSession>::fromExisting(this), reply, flags);
 }
 
@@ -113,6 +114,53 @@
     return state()->sendDecStrong(connection.fd(), address);
 }
 
+std::unique_ptr<RpcSession::FdTrigger> RpcSession::FdTrigger::make() {
+    auto ret = std::make_unique<RpcSession::FdTrigger>();
+    if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) return nullptr;
+    return ret;
+}
+
+void RpcSession::FdTrigger::trigger() {
+    mWrite.reset();
+}
+
+status_t RpcSession::FdTrigger::triggerablePollRead(base::borrowed_fd fd) {
+    while (true) {
+        pollfd pfd[]{{.fd = fd.get(), .events = POLLIN | POLLHUP, .revents = 0},
+                     {.fd = mRead.get(), .events = POLLHUP, .revents = 0}};
+        int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+        if (ret < 0) {
+            return -errno;
+        }
+        if (ret == 0) {
+            continue;
+        }
+        if (pfd[1].revents & POLLHUP) {
+            return -ECANCELED;
+        }
+        return pfd[0].revents & POLLIN ? OK : DEAD_OBJECT;
+    }
+}
+
+status_t RpcSession::FdTrigger::interruptableReadFully(base::borrowed_fd fd, void* data,
+                                                       size_t size) {
+    uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
+    uint8_t* end = buffer + size;
+
+    status_t status;
+    while ((status = triggerablePollRead(fd)) == OK) {
+        ssize_t readSize = TEMP_FAILURE_RETRY(recv(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
+        if (readSize == 0) return DEAD_OBJECT; // EOF
+
+        if (readSize < 0) {
+            return -errno;
+        }
+        buffer += readSize;
+        if (buffer == end) return OK;
+    }
+    return status;
+}
+
 status_t RpcSession::readId() {
     {
         std::lock_guard<std::mutex> _l(mMutex);
@@ -150,7 +198,8 @@
                 state()->getAndExecuteCommand(connection->fd, sp<RpcSession>::fromExisting(this));
 
         if (error != OK) {
-            ALOGI("Binder connection thread closing w/ status %s", statusToString(error).c_str());
+            LOG_RPC_DETAIL("Binder connection thread closing w/ status %s",
+                           statusToString(error).c_str());
             break;
         }
     }
@@ -158,12 +207,19 @@
     LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
                         "bad state: connection object guaranteed to be in list");
 
+    sp<RpcServer> server;
     {
         std::lock_guard<std::mutex> _l(mMutex);
         auto it = mThreads.find(std::this_thread::get_id());
         LOG_ALWAYS_FATAL_IF(it == mThreads.end());
         it->second.detach();
         mThreads.erase(it);
+
+        server = mForServer.promote();
+    }
+
+    if (server != nullptr) {
+        server->onSessionThreadEnding(sp<RpcSession>::fromExisting(this));
     }
 }
 
@@ -255,24 +311,36 @@
 
         LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
 
-        addClientConnection(std::move(serverFd));
-        return true;
+        return addClientConnection(std::move(serverFd));
     }
 
     ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
     return false;
 }
 
-void RpcSession::addClientConnection(unique_fd fd) {
+bool RpcSession::addClientConnection(unique_fd fd) {
     std::lock_guard<std::mutex> _l(mMutex);
+
+    if (mShutdownTrigger == nullptr) {
+        mShutdownTrigger = FdTrigger::make();
+        if (mShutdownTrigger == nullptr) return false;
+    }
+
     sp<RpcConnection> session = sp<RpcConnection>::make();
     session->fd = std::move(fd);
     mClientConnections.push_back(session);
+    return true;
 }
 
-void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) {
+void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId,
+                              const std::shared_ptr<FdTrigger>& shutdownTrigger) {
+    LOG_ALWAYS_FATAL_IF(mForServer.unsafe_get() != nullptr);
+    LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr);
+    LOG_ALWAYS_FATAL_IF(shutdownTrigger == nullptr);
+
     mId = sessionId;
     mForServer = server;
+    mShutdownTrigger = shutdownTrigger;
 }
 
 sp<RpcSession::RpcConnection> RpcSession::assignServerToThisThread(unique_fd fd) {
@@ -311,7 +379,7 @@
 
         // CHECK FOR DEDICATED CLIENT SOCKET
         //
-        // A server/looper should always use a dedicated session if available
+        // A server/looper should always use a dedicated connection if available
         findConnection(tid, &exclusive, &available, mSession->mClientConnections,
                        mSession->mClientConnectionsOffset);
 
@@ -339,7 +407,7 @@
                            0 /* index hint */);
         }
 
-        // if our thread is already using a session, prioritize using that
+        // if our thread is already using a connection, prioritize using that
         if (exclusive != nullptr) {
             mConnection = exclusive;
             mReentrant = true;
@@ -352,11 +420,10 @@
 
         // in regular binder, this would usually be a deadlock :)
         LOG_ALWAYS_FATAL_IF(mSession->mClientConnections.size() == 0,
-                            "Not a client of any session. You must create a session to an "
-                            "RPC server to make any non-nested (e.g. oneway or on another thread) "
-                            "calls.");
+                            "Session has no client connections. This is required for an RPC server "
+                            "to make any non-nested (e.g. oneway or on another thread) calls.");
 
-        LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...",
+        LOG_RPC_DETAIL("No available connections (have %zu clients and %zu servers). Waiting...",
                        mSession->mClientConnections.size(), mSession->mServerConnections.size());
         mSession->mAvailableConnectionCv.wait(_l);
     }
@@ -375,13 +442,13 @@
     for (size_t i = 0; i < sockets.size(); i++) {
         sp<RpcConnection>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
 
-        // take first available session (intuition = caching)
+        // take first available connection (intuition = caching)
         if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
             *available = socket;
             continue;
         }
 
-        // though, prefer to take session which is already inuse by this thread
+        // though, prefer to take connection which is already inuse by this thread
         // (nested transactions)
         if (exclusive && socket->exclusiveTid == tid) {
             *exclusive = socket;
@@ -391,7 +458,7 @@
 }
 
 RpcSession::ExclusiveConnection::~ExclusiveConnection() {
-    // reentrant use of a session means something less deep in the call stack
+    // reentrant use of a connection means something less deep in the call stack
     // is using this fd, and it retains the right to it. So, we don't give up
     // exclusive ownership, and no thread is freed.
     if (!mReentrant) {
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 2ba9fa2..2cad2ae 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,7 +18,9 @@
 
 #include "RpcState.h"
 
+#include <android-base/scopeguard.h>
 #include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
 #include <binder/RpcServer.h>
 
 #include "Debug.h"
@@ -28,6 +30,8 @@
 
 namespace android {
 
+using base::ScopeGuard;
+
 RpcState::RpcState() {}
 RpcState::~RpcState() {}
 
@@ -203,53 +207,47 @@
     mData.reset(new (std::nothrow) uint8_t[size]);
 }
 
-bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
+status_t RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data,
+                           size_t size) {
     LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
 
     if (size > std::numeric_limits<ssize_t>::max()) {
         ALOGE("Cannot send %s at size %zu (too big)", what, size);
         terminate();
-        return false;
+        return BAD_VALUE;
     }
 
     ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, MSG_NOSIGNAL));
 
     if (sent < 0 || sent != static_cast<ssize_t>(size)) {
-        ALOGE("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent, size,
-              fd.get(), strerror(errno));
+        int savedErrno = errno;
+        LOG_RPC_DETAIL("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent,
+                       size, fd.get(), strerror(savedErrno));
 
         terminate();
-        return false;
+        return -savedErrno;
     }
 
-    return true;
+    return OK;
 }
 
-bool RpcState::rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size) {
+status_t RpcState::rpcRec(const base::unique_fd& fd, const sp<RpcSession>& session,
+                          const char* what, void* data, size_t size) {
     if (size > std::numeric_limits<ssize_t>::max()) {
         ALOGE("Cannot rec %s at size %zu (too big)", what, size);
         terminate();
-        return false;
+        return BAD_VALUE;
     }
 
-    ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL | MSG_NOSIGNAL));
-
-    if (recd < 0 || recd != static_cast<ssize_t>(size)) {
-        terminate();
-
-        if (recd == 0 && errno == 0) {
-            LOG_RPC_DETAIL("No more data when trying to read %s on fd %d", what, fd.get());
-            return false;
-        }
-
-        ALOGE("Failed to read %s (received %zd of %zu bytes) on fd %d, error: %s", what, recd, size,
-              fd.get(), strerror(errno));
-        return false;
-    } else {
-        LOG_RPC_DETAIL("Received %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
+    if (status_t status = session->mShutdownTrigger->interruptableReadFully(fd.get(), data, size);
+        status != OK) {
+        LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on fd %d, error: %s", what, size, fd.get(),
+                       statusToString(status).c_str());
+        return status;
     }
 
-    return true;
+    LOG_RPC_DETAIL("Received %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
+    return OK;
 }
 
 sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session) {
@@ -257,8 +255,8 @@
     data.markForRpc(session);
     Parcel reply;
 
-    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, session,
-                               &reply, 0);
+    status_t status = transactAddress(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
+                                      session, &reply, 0);
     if (status != OK) {
         ALOGE("Error getting root object: %s", statusToString(status).c_str());
         return nullptr;
@@ -273,8 +271,8 @@
     data.markForRpc(session);
     Parcel reply;
 
-    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
-                               session, &reply, 0);
+    status_t status = transactAddress(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS,
+                                      data, session, &reply, 0);
     if (status != OK) {
         ALOGE("Error getting max threads: %s", statusToString(status).c_str());
         return status;
@@ -298,8 +296,8 @@
     data.markForRpc(session);
     Parcel reply;
 
-    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
-                               session, &reply, 0);
+    status_t status = transactAddress(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID,
+                                      data, session, &reply, 0);
     if (status != OK) {
         ALOGE("Error getting session ID: %s", statusToString(status).c_str());
         return status;
@@ -313,9 +311,31 @@
     return OK;
 }
 
-status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
+status_t RpcState::transact(const base::unique_fd& fd, const sp<IBinder>& binder, uint32_t code,
                             const Parcel& data, const sp<RpcSession>& session, Parcel* reply,
                             uint32_t flags) {
+    if (!data.isForRpc()) {
+        ALOGE("Refusing to send RPC with parcel not crafted for RPC");
+        return BAD_TYPE;
+    }
+
+    if (data.objectsCount() != 0) {
+        ALOGE("Parcel at %p has attached objects but is being used in an RPC call", &data);
+        return BAD_TYPE;
+    }
+
+    RpcAddress address = RpcAddress::zero();
+    if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status;
+
+    return transactAddress(fd, address, code, data, session, reply, flags);
+}
+
+status_t RpcState::transactAddress(const base::unique_fd& fd, const RpcAddress& address,
+                                   uint32_t code, const Parcel& data, const sp<RpcSession>& session,
+                                   Parcel* reply, uint32_t flags) {
+    LOG_ALWAYS_FATAL_IF(!data.isForRpc());
+    LOG_ALWAYS_FATAL_IF(data.objectsCount() != 0);
+
     uint64_t asyncNumber = 0;
 
     if (!address.isZero()) {
@@ -330,16 +350,6 @@
         }
     }
 
-    if (!data.isForRpc()) {
-        ALOGE("Refusing to send RPC with parcel not crafted for RPC");
-        return BAD_TYPE;
-    }
-
-    if (data.objectsCount() != 0) {
-        ALOGE("Parcel at %p has attached objects but is being used in an RPC call", &data);
-        return BAD_TYPE;
-    }
-
     RpcWireTransaction transaction{
             .address = address.viewRawEmbedded(),
             .code = code,
@@ -365,12 +375,12 @@
             .bodySize = static_cast<uint32_t>(transactionData.size()),
     };
 
-    if (!rpcSend(fd, "transact header", &command, sizeof(command))) {
-        return DEAD_OBJECT;
-    }
-    if (!rpcSend(fd, "command body", transactionData.data(), transactionData.size())) {
-        return DEAD_OBJECT;
-    }
+    if (status_t status = rpcSend(fd, "transact header", &command, sizeof(command)); status != OK)
+        return status;
+    if (status_t status =
+                rpcSend(fd, "command body", transactionData.data(), transactionData.size());
+        status != OK)
+        return status;
 
     if (flags & IBinder::FLAG_ONEWAY) {
         return OK; // do not wait for result
@@ -394,24 +404,22 @@
                                 Parcel* reply) {
     RpcWireHeader command;
     while (true) {
-        if (!rpcRec(fd, "command header", &command, sizeof(command))) {
-            return DEAD_OBJECT;
-        }
+        if (status_t status = rpcRec(fd, session, "command header", &command, sizeof(command));
+            status != OK)
+            return status;
 
         if (command.command == RPC_COMMAND_REPLY) break;
 
-        status_t status = processServerCommand(fd, session, command);
-        if (status != OK) return status;
+        if (status_t status = processServerCommand(fd, session, command); status != OK)
+            return status;
     }
 
     CommandData data(command.bodySize);
-    if (!data.valid()) {
-        return NO_MEMORY;
-    }
+    if (!data.valid()) return NO_MEMORY;
 
-    if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) {
-        return DEAD_OBJECT;
-    }
+    if (status_t status = rpcRec(fd, session, "reply body", data.data(), command.bodySize);
+        status != OK)
+        return status;
 
     if (command.bodySize < sizeof(RpcWireReply)) {
         ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!",
@@ -451,9 +459,12 @@
             .command = RPC_COMMAND_DEC_STRONG,
             .bodySize = sizeof(RpcWireAddress),
     };
-    if (!rpcSend(fd, "dec ref header", &cmd, sizeof(cmd))) return DEAD_OBJECT;
-    if (!rpcSend(fd, "dec ref body", &addr.viewRawEmbedded(), sizeof(RpcWireAddress)))
-        return DEAD_OBJECT;
+    if (status_t status = rpcSend(fd, "dec ref header", &cmd, sizeof(cmd)); status != OK)
+        return status;
+    if (status_t status =
+                rpcSend(fd, "dec ref body", &addr.viewRawEmbedded(), sizeof(RpcWireAddress));
+        status != OK)
+        return status;
     return OK;
 }
 
@@ -461,20 +472,35 @@
     LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
 
     RpcWireHeader command;
-    if (!rpcRec(fd, "command header", &command, sizeof(command))) {
-        return DEAD_OBJECT;
-    }
+    if (status_t status = rpcRec(fd, session, "command header", &command, sizeof(command));
+        status != OK)
+        return status;
 
     return processServerCommand(fd, session, command);
 }
 
 status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
                                         const RpcWireHeader& command) {
+    IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
+    IPCThreadState::SpGuard spGuard{
+            .address = __builtin_frame_address(0),
+            .context = "processing binder RPC command",
+    };
+    const IPCThreadState::SpGuard* origGuard;
+    if (kernelBinderState != nullptr) {
+        origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
+    }
+    ScopeGuard guardUnguard = [&]() {
+        if (kernelBinderState != nullptr) {
+            kernelBinderState->restoreGetCallingSpGuard(origGuard);
+        }
+    };
+
     switch (command.command) {
         case RPC_COMMAND_TRANSACT:
             return processTransact(fd, session, command);
         case RPC_COMMAND_DEC_STRONG:
-            return processDecStrong(fd, command);
+            return processDecStrong(fd, session, command);
     }
 
     // We should always know the version of the opposing side, and since the
@@ -494,11 +520,12 @@
     if (!transactionData.valid()) {
         return NO_MEMORY;
     }
-    if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
-        return DEAD_OBJECT;
-    }
+    if (status_t status = rpcRec(fd, session, "transaction body", transactionData.data(),
+                                 transactionData.size());
+        status != OK)
+        return status;
 
-    return processTransactInternal(fd, session, std::move(transactionData));
+    return processTransactInternal(fd, session, std::move(transactionData), nullptr /*targetRef*/);
 }
 
 static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -511,7 +538,7 @@
 }
 
 status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
-                                           CommandData transactionData) {
+                                           CommandData transactionData, sp<IBinder>&& targetRef) {
     if (transactionData.size() < sizeof(RpcWireTransaction)) {
         ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
               sizeof(RpcWireTransaction), transactionData.size());
@@ -527,45 +554,49 @@
     status_t replyStatus = OK;
     sp<IBinder> target;
     if (!addr.isZero()) {
-        std::lock_guard<std::mutex> _l(mNodeMutex);
-
-        auto it = mNodeForAddress.find(addr);
-        if (it == mNodeForAddress.end()) {
-            ALOGE("Unknown binder address %s.", addr.toString().c_str());
-            replyStatus = BAD_VALUE;
+        if (!targetRef) {
+            target = onBinderEntering(session, addr);
         } else {
-            target = it->second.binder.promote();
-            if (target == nullptr) {
-                // This can happen if the binder is remote in this process, and
-                // another thread has called the last decStrong on this binder.
-                // However, for local binders, it indicates a misbehaving client
-                // (any binder which is being transacted on should be holding a
-                // strong ref count), so in either case, terminating the
-                // session.
-                ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
+            target = targetRef;
+        }
+
+        if (target == nullptr) {
+            // This can happen if the binder is remote in this process, and
+            // another thread has called the last decStrong on this binder.
+            // However, for local binders, it indicates a misbehaving client
+            // (any binder which is being transacted on should be holding a
+            // strong ref count), so in either case, terminating the
+            // session.
+            ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
+                  addr.toString().c_str());
+            terminate();
+            replyStatus = BAD_VALUE;
+        } else if (target->localBinder() == nullptr) {
+            ALOGE("Unknown binder address or non-local binder, not address %s. Terminating!",
+                  addr.toString().c_str());
+            terminate();
+            replyStatus = BAD_VALUE;
+        } else if (transaction->flags & IBinder::FLAG_ONEWAY) {
+            std::lock_guard<std::mutex> _l(mNodeMutex);
+            auto it = mNodeForAddress.find(addr);
+            if (it->second.binder.promote() != target) {
+                ALOGE("Binder became invalid during transaction. Bad client? %s",
                       addr.toString().c_str());
-                terminate();
                 replyStatus = BAD_VALUE;
-            } else if (target->localBinder() == nullptr) {
-                ALOGE("Transactions can only go to local binders, not address %s. Terminating!",
-                      addr.toString().c_str());
-                terminate();
-                replyStatus = BAD_VALUE;
-            } else if (transaction->flags & IBinder::FLAG_ONEWAY) {
-                if (transaction->asyncNumber != it->second.asyncNumber) {
-                    // we need to process some other asynchronous transaction
-                    // first
-                    // TODO(b/183140903): limit enqueues/detect overfill for bad client
-                    // TODO(b/183140903): detect when an object is deleted when it still has
-                    //        pending async transactions
-                    it->second.asyncTodo.push(BinderNode::AsyncTodo{
-                            .data = std::move(transactionData),
-                            .asyncNumber = transaction->asyncNumber,
-                    });
-                    LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s", transaction->asyncNumber,
-                                   addr.toString().c_str());
-                    return OK;
-                }
+            } else if (transaction->asyncNumber != it->second.asyncNumber) {
+                // we need to process some other asynchronous transaction
+                // first
+                // TODO(b/183140903): limit enqueues/detect overfill for bad client
+                // TODO(b/183140903): detect when an object is deleted when it still has
+                //        pending async transactions
+                it->second.asyncTodo.push(BinderNode::AsyncTodo{
+                        .ref = target,
+                        .data = std::move(transactionData),
+                        .asyncNumber = transaction->asyncNumber,
+                });
+                LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s", transaction->asyncNumber,
+                               addr.toString().c_str());
+                return OK;
             }
         }
     }
@@ -607,7 +638,7 @@
                         //
                         // sessions associated with servers must have an ID
                         // (hence abort)
-                        int32_t id = session->getPrivateAccessorForId().get().value();
+                        int32_t id = session->mId.value();
                         replyStatus = reply.writeInt32(id);
                         break;
                     }
@@ -659,13 +690,17 @@
                                it->second.asyncNumber, addr.toString().c_str());
 
                 // justification for const_cast (consider avoiding priority_queue):
-                // - AsyncTodo operator< doesn't depend on 'data' object
+                // - AsyncTodo operator< doesn't depend on 'data' or 'ref' objects
                 // - gotta go fast
-                CommandData data = std::move(
-                        const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
+                auto& todo = const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top());
+
+                CommandData nextData = std::move(todo.data);
+                sp<IBinder> nextRef = std::move(todo.ref);
+
                 it->second.asyncTodo.pop();
                 _l.unlock();
-                return processTransactInternal(fd, session, std::move(data));
+                return processTransactInternal(fd, session, std::move(nextData),
+                                               std::move(nextRef));
             }
         }
         return OK;
@@ -693,25 +728,27 @@
             .bodySize = static_cast<uint32_t>(replyData.size()),
     };
 
-    if (!rpcSend(fd, "reply header", &cmdReply, sizeof(RpcWireHeader))) {
-        return DEAD_OBJECT;
-    }
-    if (!rpcSend(fd, "reply body", replyData.data(), replyData.size())) {
-        return DEAD_OBJECT;
-    }
+    if (status_t status = rpcSend(fd, "reply header", &cmdReply, sizeof(RpcWireHeader));
+        status != OK)
+        return status;
+    if (status_t status = rpcSend(fd, "reply body", replyData.data(), replyData.size());
+        status != OK)
+        return status;
     return OK;
 }
 
-status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
+status_t RpcState::processDecStrong(const base::unique_fd& fd, const sp<RpcSession>& session,
+                                    const RpcWireHeader& command) {
     LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
 
     CommandData commandData(command.bodySize);
     if (!commandData.valid()) {
         return NO_MEMORY;
     }
-    if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
-        return DEAD_OBJECT;
-    }
+    if (status_t status =
+                rpcRec(fd, session, "dec ref body", commandData.data(), commandData.size());
+        status != OK)
+        return status;
 
     if (command.bodySize < sizeof(RpcWireAddress)) {
         ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!",
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 31f8a22..8a0610e 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -58,9 +58,13 @@
     status_t getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
                           int32_t* sessionIdOut);
 
-    [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
+    [[nodiscard]] status_t transact(const base::unique_fd& fd, const sp<IBinder>& address,
                                     uint32_t code, const Parcel& data,
                                     const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
+    [[nodiscard]] status_t transactAddress(const base::unique_fd& fd, const RpcAddress& address,
+                                           uint32_t code, const Parcel& data,
+                                           const sp<RpcSession>& session, Parcel* reply,
+                                           uint32_t flags);
     [[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
     [[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
                                                 const sp<RpcSession>& session);
@@ -115,9 +119,10 @@
         size_t mSize;
     };
 
-    [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
-                               size_t size);
-    [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
+    [[nodiscard]] status_t rpcSend(const base::unique_fd& fd, const char* what, const void* data,
+                                   size_t size);
+    [[nodiscard]] status_t rpcRec(const base::unique_fd& fd, const sp<RpcSession>& session,
+                                  const char* what, void* data, size_t size);
 
     [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
                                         Parcel* reply);
@@ -128,8 +133,10 @@
                                            const RpcWireHeader& command);
     [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
                                                    const sp<RpcSession>& session,
-                                                   CommandData transactionData);
+                                                   CommandData transactionData,
+                                                   sp<IBinder>&& targetRef);
     [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
+                                            const sp<RpcSession>& session,
                                             const RpcWireHeader& command);
 
     struct BinderNode {
@@ -163,6 +170,7 @@
 
         // async transaction queue, _only_ for local binder
         struct AsyncTodo {
+            sp<IBinder> ref;
             CommandData data;
             uint64_t asyncNumber = 0;
 
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index f12ef4e..3a5575f 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -79,9 +79,9 @@
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
-void Stability::debugLogStability(const std::string& tag, const sp<IBinder>& binder) {
+std::string Stability::debugToString(const sp<IBinder>& binder) {
     auto stability = getCategory(binder.get());
-    ALOGE("%s: stability is %s", tag.c_str(), stability.debugString().c_str());
+    return stability.debugString();
 }
 
 void Stability::markVndk(IBinder* binder) {
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 7e9be41..754f87c 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -94,6 +94,9 @@
 
     pid_t               getDebugPid();
 
+    [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd,
+                                             uint32_t maxRpcThreads);
+
 protected:
     virtual             ~BBinder();
 
@@ -111,6 +114,8 @@
 
     Extras*             getOrCreateExtras();
 
+    [[nodiscard]] status_t setRpcClientDebug(const Parcel& data);
+
     std::atomic<Extras*> mExtras;
 
     friend ::android::internal::Stability;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 97c826c..ce28d7c 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -60,6 +60,7 @@
         SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
         EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
         DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
+        SET_RPC_CLIENT_TRANSACTION = B_PACK_CHARS('_', 'R', 'P', 'C'),
 
         // See android.os.IBinder.TWEET_TRANSACTION
         // Most importantly, messages can be anything not exceeding 130 UTF-8
@@ -152,6 +153,27 @@
      */
     status_t                getDebugPid(pid_t* outPid);
 
+    /**
+     * Set the RPC client fd to this binder service, for debugging. This is only available on
+     * debuggable builds.
+     *
+     * |maxRpcThreads| must be positive because RPC is useless without threads.
+     *
+     * When this is called on a binder service, the service:
+     * 1. sets up RPC server
+     * 2. spawns 1 new thread that calls RpcServer::join()
+     *    - join() spawns at most |maxRpcThreads| threads that accept() connections; see RpcServer
+     *
+     * setRpcClientDebug() may only be called once.
+     * TODO(b/182914638): If allow to shut down the client, addRpcClient can be called repeatedly.
+     *
+     * Note: A thread is spawned for each accept()'ed fd, which may call into functions of the
+     * interface freely. See RpcServer::join(). To avoid such race conditions, implement the service
+     * functions with multithreading support.
+     */
+    [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd socketFd,
+                                             uint32_t maxRpcThreads);
+
     // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t        transact(   uint32_t code,
                                         const Parcel& data,
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 196a41b..20a9f36 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -62,7 +62,7 @@
              * call. If not in a binder call, this will return getpid. If the
              * call is oneway, this will return 0.
              */
-            pid_t               getCallingPid() const;
+            [[nodiscard]] pid_t getCallingPid() const;
 
             /**
              * Returns the SELinux security identifier of the process which has
@@ -73,13 +73,43 @@
              * This can't be restored once it's cleared, and it does not return the
              * context of the current process when not in a binder call.
              */
-            const char*         getCallingSid() const;
+            [[nodiscard]] const char* getCallingSid() const;
 
             /**
              * Returns the UID of the process which has made the current binder
              * call. If not in a binder call, this will return 0.
              */
-            uid_t               getCallingUid() const;
+            [[nodiscard]] uid_t getCallingUid() const;
+
+            /**
+             * Make it an abort to rely on getCalling* for a section of
+             * execution.
+             *
+             * Usage:
+             *     IPCThreadState::SpGuard guard {
+             *        .address = __builtin_frame_address(0),
+             *        .context = "...",
+             *     };
+             *     const auto* orig = pushGetCallingSpGuard(&guard);
+             *     {
+             *         // will abort if you call getCalling*, unless you are
+             *         // serving a nested binder transaction
+             *     }
+             *     restoreCallingSpGuard(orig);
+             */
+            struct SpGuard {
+                const void* address;
+                const char* context;
+            };
+            const SpGuard* pushGetCallingSpGuard(const SpGuard* guard);
+            void restoreGetCallingSpGuard(const SpGuard* guard);
+            /**
+             * Used internally by getCalling*. Can also be used to assert that
+             * you are in a binder context (getCalling* is valid). This is
+             * intentionally not exposed as a boolean API since code should be
+             * written to know its environment.
+             */
+            void checkContextIsBinderForUse(const char* use) const;
 
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
@@ -197,6 +227,7 @@
             Parcel              mOut;
             status_t            mLastError;
             const void*         mServingStackPointer;
+            const SpGuard* mServingStackPointerGuard;
             pid_t               mCallingPid;
             const char*         mCallingSid;
             uid_t               mCallingUid;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5aaaa0c..02052ad 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -561,6 +561,8 @@
     status_t            flattenBinder(const sp<IBinder>& binder);
     status_t            unflattenBinder(sp<IBinder>* out) const;
 
+    status_t readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const;
+
     template<class T>
     status_t            readAligned(T *pArg) const;
 
@@ -1315,7 +1317,7 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::vector<T>* val) const {
     int32_t size;
-    status_t err = readInt32(&size);
+    status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
     if (err != NO_ERROR) {
         return err;
     }
@@ -1330,7 +1332,7 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
     int32_t size;
-    status_t err = readInt32(&size);
+    status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
     if (err != NO_ERROR) {
         return err;
     }
@@ -1346,7 +1348,7 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
     int32_t size;
-    status_t err = readInt32(&size);
+    status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
     if (err != NO_ERROR) {
         return err;
     }
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index b9db5d7..72c2ab7 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -18,8 +18,8 @@
 
 #include <binder/IBinder.h>
 #include <utils/KeyedVector.h>
-#include <utils/String8.h>
 #include <utils/String16.h>
+#include <utils/String8.h>
 
 #include <utils/threads.h>
 
@@ -30,11 +30,10 @@
 
 class IPCThreadState;
 
-class ProcessState : public virtual RefBase
-{
+class ProcessState : public virtual RefBase {
 public:
-    static  sp<ProcessState>    self();
-    static  sp<ProcessState>    selfOrNull();
+    static sp<ProcessState> self();
+    static sp<ProcessState> selfOrNull();
 
     /* initWithDriver() can be used to configure libbinder to use
      * a different binder driver dev node. It must be called *before*
@@ -44,94 +43,101 @@
      *
      * If this is called with nullptr, the behavior is the same as selfOrNull.
      */
-    static  sp<ProcessState>    initWithDriver(const char *driver);
+    static sp<ProcessState> initWithDriver(const char* driver);
 
-            sp<IBinder>         getContextObject(const sp<IBinder>& caller);
+    sp<IBinder> getContextObject(const sp<IBinder>& caller);
 
-            void                startThreadPool();
+    void startThreadPool();
 
-            bool                becomeContextManager();
+    bool becomeContextManager();
 
-            sp<IBinder>         getStrongProxyForHandle(int32_t handle);
-            void                expungeHandle(int32_t handle, IBinder* binder);
+    sp<IBinder> getStrongProxyForHandle(int32_t handle);
+    void expungeHandle(int32_t handle, IBinder* binder);
 
-            void                spawnPooledThread(bool isMain);
-            
-            status_t            setThreadPoolMaxThreadCount(size_t maxThreads);
-            status_t            enableOnewaySpamDetection(bool enable);
-            void                giveThreadPoolName();
+    void spawnPooledThread(bool isMain);
 
-            String8             getDriverName();
+    status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+    status_t enableOnewaySpamDetection(bool enable);
+    void giveThreadPoolName();
 
-            ssize_t             getKernelReferences(size_t count, uintptr_t* buf);
+    String8 getDriverName();
 
-                                // Only usable by the context manager.
-                                // This refcount includes:
-                                // 1. Strong references to the node by this and other processes
-                                // 2. Temporary strong references held by the kernel during a
-                                //    transaction on the node.
-                                // It does NOT include local strong references to the node
-            ssize_t             getStrongRefCountForNode(const sp<BpBinder>& binder);
+    ssize_t getKernelReferences(size_t count, uintptr_t* buf);
 
-            enum class CallRestriction {
-                // all calls okay
-                NONE,
-                // log when calls are blocking
-                ERROR_IF_NOT_ONEWAY,
-                // abort process on blocking calls
-                FATAL_IF_NOT_ONEWAY,
-            };
-            // Sets calling restrictions for all transactions in this process. This must be called
-            // before any threads are spawned.
-            void setCallRestriction(CallRestriction restriction);
+    // Only usable by the context manager.
+    // This refcount includes:
+    // 1. Strong references to the node by this and other processes
+    // 2. Temporary strong references held by the kernel during a
+    //    transaction on the node.
+    // It does NOT include local strong references to the node
+    ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
+
+    enum class CallRestriction {
+        // all calls okay
+        NONE,
+        // log when calls are blocking
+        ERROR_IF_NOT_ONEWAY,
+        // abort process on blocking calls
+        FATAL_IF_NOT_ONEWAY,
+    };
+    // Sets calling restrictions for all transactions in this process. This must be called
+    // before any threads are spawned.
+    void setCallRestriction(CallRestriction restriction);
+
+    /**
+     * Get the max number of threads that the kernel can start.
+     *
+     * Note: this is the lower bound. Additional threads may be started.
+     */
+    size_t getThreadPoolMaxThreadCount() const;
 
 private:
-    static  sp<ProcessState>    init(const char *defaultDriver, bool requireDefault);
+    static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
 
     friend class IPCThreadState;
     friend class sp<ProcessState>;
 
-            explicit            ProcessState(const char* driver);
-                                ~ProcessState();
+    explicit ProcessState(const char* driver);
+    ~ProcessState();
 
-                                ProcessState(const ProcessState& o);
-            ProcessState&       operator=(const ProcessState& o);
-            String8             makeBinderThreadName();
+    ProcessState(const ProcessState& o);
+    ProcessState& operator=(const ProcessState& o);
+    String8 makeBinderThreadName();
 
-            struct handle_entry {
-                IBinder* binder;
-                RefBase::weakref_type* refs;
-            };
+    struct handle_entry {
+        IBinder* binder;
+        RefBase::weakref_type* refs;
+    };
 
-            handle_entry*       lookupHandleLocked(int32_t handle);
+    handle_entry* lookupHandleLocked(int32_t handle);
 
-            String8             mDriverName;
-            int                 mDriverFD;
-            void*               mVMStart;
+    String8 mDriverName;
+    int mDriverFD;
+    void* mVMStart;
 
-            // Protects thread count and wait variables below.
-            pthread_mutex_t     mThreadCountLock;
-            // Broadcast whenever mWaitingForThreads > 0
-            pthread_cond_t      mThreadCountDecrement;
-            // Number of binder threads current executing a command.
-            size_t              mExecutingThreadsCount;
-            // Number of threads calling IPCThreadState::blockUntilThreadAvailable()
-            size_t              mWaitingForThreads;
-            // Maximum number for binder threads allowed for this process.
-            size_t              mMaxThreads;
-            // Time when thread pool was emptied
-            int64_t             mStarvationStartTimeMs;
+    // Protects thread count and wait variables below.
+    pthread_mutex_t mThreadCountLock;
+    // Broadcast whenever mWaitingForThreads > 0
+    pthread_cond_t mThreadCountDecrement;
+    // Number of binder threads current executing a command.
+    size_t mExecutingThreadsCount;
+    // Number of threads calling IPCThreadState::blockUntilThreadAvailable()
+    size_t mWaitingForThreads;
+    // Maximum number for binder threads allowed for this process.
+    size_t mMaxThreads;
+    // Time when thread pool was emptied
+    int64_t mStarvationStartTimeMs;
 
-    mutable Mutex               mLock;  // protects everything below.
+    mutable Mutex mLock; // protects everything below.
 
-            Vector<handle_entry>mHandleToObject;
+    Vector<handle_entry> mHandleToObject;
 
-            bool                mThreadPoolStarted;
-    volatile int32_t            mThreadPoolSeq;
+    bool mThreadPoolStarted;
+    volatile int32_t mThreadPoolSeq;
 
-            CallRestriction     mCallRestriction;
+    CallRestriction mCallRestriction;
 };
-    
+
 } // namespace android
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 8f0c6fd..a08c401 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -117,17 +117,31 @@
     sp<IBinder> getRootObject();
 
     /**
+     * Runs join() in a background thread. Immediately returns.
+     */
+    void start();
+
+    /**
      * You must have at least one client session before calling this.
      *
-     * TODO(b/185167543): way to shut down?
+     * If a client needs to actively terminate join, call shutdown() in a separate thread.
+     *
+     * At any given point, there can only be one thread calling join().
+     *
+     * Warning: if shutdown is called, this will return while the shutdown is
+     * still occurring. To ensure that the service is fully shutdown, you might
+     * want to call shutdown after 'join' returns.
      */
     void join();
 
     /**
-     * Accept one connection on this server. You must have at least one client
-     * session before calling this.
+     * Shut down any existing join(). Return true if successfully shut down, false otherwise
+     * (e.g. no join() is running). Will wait for the server to be fully
+     * shutdown.
+     *
+     * Warning: this will hang if it is called from its own thread.
      */
-    [[nodiscard]] bool acceptOne();
+    [[nodiscard]] bool shutdown();
 
     /**
      * For debugging!
@@ -140,25 +154,30 @@
     // internal use only
 
     void onSessionTerminating(const sp<RpcSession>& session);
+    void onSessionThreadEnding(const sp<RpcSession>& session);
 
 private:
     friend sp<RpcServer>;
     RpcServer();
 
-    void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
+    static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd);
     bool setupSocketServer(const RpcSocketAddress& address);
+    [[nodiscard]] bool acceptOne();
 
     bool mAgreedExperimental = false;
-    bool mStarted = false; // TODO(b/185167543): support dynamically added clients
     size_t mMaxThreads = 1;
     base::unique_fd mServer; // socket we are accepting sessions on
 
     std::mutex mLock; // for below
+    std::unique_ptr<std::thread> mJoinThread;
+    bool mJoinThreadRunning = false;
     std::map<std::thread::id, std::thread> mConnectingThreads;
     sp<IBinder> mRootObject;
     wp<IBinder> mRootObjectWeak;
     std::map<int32_t, sp<RpcSession>> mSessions;
     int32_t mSessionIdCounter = 0;
+    std::shared_ptr<RpcSession::FdTrigger> mShutdownTrigger;
+    std::condition_variable mShutdownCv;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index bcc213c..4401aaf 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -83,7 +83,7 @@
      */
     status_t getRemoteMaxThreads(size_t* maxThreads);
 
-    [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+    [[nodiscard]] status_t transact(const sp<IBinder>& binder, uint32_t code, const Parcel& data,
                                     Parcel* reply, uint32_t flags);
     [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
 
@@ -94,24 +94,50 @@
     // internal only
     const std::unique_ptr<RpcState>& state() { return mState; }
 
-    class PrivateAccessorForId {
-    private:
-        friend class RpcSession;
-        friend class RpcState;
-        explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {}
-
-        const std::optional<int32_t> get() { return mSession->mId; }
-
-        const RpcSession* mSession;
-    };
-    PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); }
-
 private:
-    friend PrivateAccessorForId;
     friend sp<RpcSession>;
     friend RpcServer;
+    friend RpcState;
     RpcSession();
 
+    /** This is not a pipe. */
+    struct FdTrigger {
+        /** Returns nullptr for error case */
+        static std::unique_ptr<FdTrigger> make();
+
+        /**
+         * poll() on this fd for POLLHUP to get notification when trigger is called
+         */
+        base::borrowed_fd readFd() const { return mRead; }
+
+        /**
+         * Close the write end of the pipe so that the read end receives POLLHUP.
+         */
+        void trigger();
+
+        /**
+         * Poll for a read event.
+         *
+         * Return:
+         *   true - time to read!
+         *   false - trigger happened
+         */
+        status_t triggerablePollRead(base::borrowed_fd fd);
+
+        /**
+         * Read, but allow the read to be interrupted by this trigger.
+         *
+         * Return:
+         *   true - read succeeded at 'size'
+         *   false - interrupted (failure or trigger)
+         */
+        status_t interruptableReadFully(base::borrowed_fd fd, void* data, size_t size);
+
+    private:
+        base::unique_fd mWrite;
+        base::unique_fd mRead;
+    };
+
     status_t readId();
 
     // transfer ownership of thread
@@ -130,8 +156,9 @@
 
     bool setupSocketClient(const RpcSocketAddress& address);
     bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId);
-    void addClientConnection(base::unique_fd fd);
-    void setForServer(const wp<RpcServer>& server, int32_t sessionId);
+    bool addClientConnection(base::unique_fd fd);
+    void setForServer(const wp<RpcServer>& server, int32_t sessionId,
+                      const std::shared_ptr<FdTrigger>& shutdownTrigger);
     sp<RpcConnection> assignServerToThisThread(base::unique_fd fd);
     bool removeServerConnection(const sp<RpcConnection>& connection);
 
@@ -182,6 +209,8 @@
     // TODO(b/183988761): this shouldn't be guessable
     std::optional<int32_t> mId;
 
+    std::shared_ptr<FdTrigger> mShutdownTrigger;
+
     std::unique_ptr<RpcState> mState;
 
     std::mutex mMutex; // for all below
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index f4bfac8..6bc607f 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -100,7 +100,7 @@
     static void markVintf(IBinder* binder);
 
     // WARNING: for debugging only
-    static void debugLogStability(const std::string& tag, const sp<IBinder>& binder);
+    static std::string debugToString(const sp<IBinder>& binder);
 
     // WARNING: This is only ever expected to be called by auto-generated code or tests.
     // You likely want to change or modify the stability of the interface you are using.
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 83190aa..5092d87 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -910,6 +910,9 @@
     if (err != STATUS_OK) return err;
     if (size < 0) return STATUS_UNEXPECTED_NULL;
 
+    // TODO(b/188215728): delegate to libbinder_ndk
+    if (size > 1000000) return STATUS_NO_MEMORY;
+
     vec->resize(static_cast<size_t>(size));
     return STATUS_OK;
 }
@@ -931,6 +934,9 @@
         return STATUS_OK;
     }
 
+    // TODO(b/188215728): delegate to libbinder_ndk
+    if (size > 1000000) return STATUS_NO_MEMORY;
+
     *vec = std::optional<std::vector<T>>(std::vector<T>{});
     (*vec)->resize(static_cast<size_t>(size));
     return STATUS_OK;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index ec7c7d8..b2f21c7 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -46,7 +46,8 @@
 template <typename T>
 using ArraySetter = void (*)(void* arrayData, size_t index, T value);
 
-binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int32_t length) {
+static binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray,
+                                                 int32_t length) {
     // only -1 can be used to represent a null array
     if (length < -1) return STATUS_BAD_VALUE;
 
@@ -61,12 +62,24 @@
 
     Parcel* rawParcel = parcel->get();
 
-    status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+    status_t status = rawParcel->writeInt32(length);
     if (status != STATUS_OK) return PruneStatusT(status);
 
     return STATUS_OK;
 }
 
+static binder_status_t ReadAndValidateArraySize(const AParcel* parcel, int32_t* length) {
+    if (status_t status = parcel->get()->readInt32(length); status != STATUS_OK) {
+        return PruneStatusT(status);
+    }
+
+    if (*length < -1) return STATUS_BAD_VALUE;  // libbinder_ndk reserves these
+    if (*length <= 0) return STATUS_OK;         // null
+    if (static_cast<size_t>(*length) > parcel->get()->dataAvail()) return STATUS_NO_MEMORY;
+
+    return STATUS_OK;
+}
+
 template <typename T>
 binder_status_t WriteArray(AParcel* parcel, const T* array, int32_t length) {
     binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length);
@@ -111,10 +124,9 @@
     const Parcel* rawParcel = parcel->get();
 
     int32_t length;
-    status_t status = rawParcel->readInt32(&length);
-
-    if (status != STATUS_OK) return PruneStatusT(status);
-    if (length < -1) return STATUS_BAD_VALUE;
+    if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+        return status;
+    }
 
     T* array;
     if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
@@ -140,10 +152,9 @@
     const Parcel* rawParcel = parcel->get();
 
     int32_t length;
-    status_t status = rawParcel->readInt32(&length);
-
-    if (status != STATUS_OK) return PruneStatusT(status);
-    if (length < -1) return STATUS_BAD_VALUE;
+    if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+        return status;
+    }
 
     char16_t* array;
     if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
@@ -155,7 +166,7 @@
     if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
 
     for (int32_t i = 0; i < length; i++) {
-        status = rawParcel->readChar(array + i);
+        status_t status = rawParcel->readChar(array + i);
 
         if (status != STATUS_OK) return PruneStatusT(status);
     }
@@ -189,10 +200,9 @@
     const Parcel* rawParcel = parcel->get();
 
     int32_t length;
-    status_t status = rawParcel->readInt32(&length);
-
-    if (status != STATUS_OK) return PruneStatusT(status);
-    if (length < -1) return STATUS_BAD_VALUE;
+    if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+        return status;
+    }
 
     if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
 
@@ -200,7 +210,7 @@
 
     for (int32_t i = 0; i < length; i++) {
         T readTarget;
-        status = (rawParcel->*read)(&readTarget);
+        status_t status = (rawParcel->*read)(&readTarget);
         if (status != STATUS_OK) return PruneStatusT(status);
 
         setter(arrayData, i, readTarget);
@@ -402,13 +412,10 @@
 binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData,
                                         AParcel_stringArrayAllocator allocator,
                                         AParcel_stringArrayElementAllocator elementAllocator) {
-    const Parcel* rawParcel = parcel->get();
-
     int32_t length;
-    status_t status = rawParcel->readInt32(&length);
-
-    if (status != STATUS_OK) return PruneStatusT(status);
-    if (length < -1) return STATUS_BAD_VALUE;
+    if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+        return status;
+    }
 
     if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
 
@@ -449,13 +456,10 @@
 binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData,
                                             AParcel_parcelableArrayAllocator allocator,
                                             AParcel_readParcelableElement elementReader) {
-    const Parcel* rawParcel = parcel->get();
-
     int32_t length;
-    status_t status = rawParcel->readInt32(&length);
-
-    if (status != STATUS_OK) return PruneStatusT(status);
-    if (length < -1) return STATUS_BAD_VALUE;
+    if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+        return status;
+    }
 
     if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
 
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 49d3401..7d655d8 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -26,6 +26,7 @@
     },
     apex_available: [
         "//apex_available:platform",
+        "com.android.compos",
         "com.android.virt",
     ],
 }
@@ -48,6 +49,7 @@
     },
     apex_available: [
         "//apex_available:platform",
+        "com.android.compos",
         "com.android.virt",
     ],
     lints: "none",
@@ -99,6 +101,7 @@
     },
     apex_available: [
         "//apex_available:platform",
+        "com.android.compos",
         "com.android.virt",
     ],
 }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index ec231b2..9cf433d 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,7 @@
     defaults: ["binder_test_defaults"],
     srcs: ["binderLibTest.cpp"],
     shared_libs: [
+        "libbase",
         "libbinder",
         "libutils",
     ],
@@ -101,6 +102,7 @@
 
     srcs: ["binderLibTest.cpp"],
     shared_libs: [
+        "libbase",
         "libbinder",
         "libutils",
     ],
@@ -133,6 +135,9 @@
         darwin: {
             enabled: false,
         },
+        android: {
+            test_suites: ["vts"],
+        },
     },
     defaults: [
         "binder_test_defaults",
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index ef4198d..646bcc6 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -55,4 +55,7 @@
     oneway void sleepMsAsync(int ms);
 
     void die(boolean cleanup);
+    void scheduleShutdown();
+
+    void useKernelBinderCallingId();
 }
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5612d1d..3289b5f 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -21,19 +21,28 @@
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
+
+#include <chrono>
 #include <thread>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
 #include <binder/Binder.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
 
 #include <linux/sched.h>
 #include <sys/epoll.h>
 #include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 #include "../binder_module.h"
 #include "binderAbiHelper.h"
@@ -41,7 +50,11 @@
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
 using namespace android;
+using namespace std::string_literals;
+using namespace std::chrono_literals;
+using testing::ExplainMatchResult;
 using testing::Not;
+using testing::WithParamInterface;
 
 // e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
 MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
@@ -49,6 +62,15 @@
     return expected == arg;
 }
 
+// e.g. Result<int32_t> v = 0; EXPECT_THAT(result, ResultHasValue(0));
+MATCHER_P(ResultHasValue, resultMatcher, "") {
+    if (!arg.ok()) {
+        *result_listener << "contains error " << arg.error();
+        return false;
+    }
+    return ExplainMatchResult(resultMatcher, arg.value(), result_listener);
+}
+
 static ::testing::AssertionResult IsPageAligned(void *buf) {
     if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
         return ::testing::AssertionSuccess();
@@ -72,6 +94,7 @@
     BINDER_LIB_TEST_REGISTER_SERVER,
     BINDER_LIB_TEST_ADD_SERVER,
     BINDER_LIB_TEST_ADD_POLL_SERVER,
+    BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION,
     BINDER_LIB_TEST_CALL_BACK,
     BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
     BINDER_LIB_TEST_DELAYED_CALL_BACK,
@@ -96,6 +119,8 @@
     BINDER_LIB_TEST_ECHO_VECTOR,
     BINDER_LIB_TEST_REJECT_BUF,
     BINDER_LIB_TEST_CAN_GET_SID,
+    BINDER_LIB_TEST_USLEEP,
+    BINDER_LIB_TEST_CREATE_TEST_SERVICE,
 };
 
 pid_t start_server_process(int arg2, bool usePoll = false)
@@ -156,6 +181,20 @@
     return pid;
 }
 
+android::base::Result<int32_t> GetId(sp<IBinder> service) {
+    using android::base::Error;
+    Parcel data, reply;
+    data.markForBinder(service);
+    const char *prefix = data.isForRpc() ? "On RPC server, " : "On binder server, ";
+    status_t status = service->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply);
+    if (status != OK)
+        return Error(status) << prefix << "transact(GET_ID): " << statusToString(status);
+    int32_t result = 0;
+    status = reply.readInt32(&result);
+    if (status != OK) return Error(status) << prefix << "readInt32: " << statusToString(status);
+    return result;
+}
+
 class BinderLibTestEnv : public ::testing::Environment {
     public:
         BinderLibTestEnv() {}
@@ -475,12 +514,7 @@
 }
 
 TEST_F(BinderLibTest, GetId) {
-    int32_t id;
-    Parcel data, reply;
-    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply),
-                StatusEq(NO_ERROR));
-    EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
-    EXPECT_EQ(0, id);
+    EXPECT_THAT(GetId(m_server), ResultHasValue(0));
 }
 
 TEST_F(BinderLibTest, PtrSize) {
@@ -603,6 +637,13 @@
     EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
 }
 
+TEST_F(BinderLibTest, BinderCallContextGuard) {
+    sp<IBinder> binder = addServer();
+    Parcel data, reply;
+    EXPECT_THAT(binder->transact(BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION, data, &reply),
+                StatusEq(DEAD_OBJECT));
+}
+
 TEST_F(BinderLibTest, AddServer)
 {
     sp<IBinder> server = addServer();
@@ -1134,39 +1175,187 @@
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
 }
 
-class BinderLibTestService : public BBinder
-{
-    public:
-        explicit BinderLibTestService(int32_t id)
-            : m_id(id)
-            , m_nextServerId(id + 1)
-            , m_serverStartRequested(false)
-            , m_callback(nullptr)
-        {
-            pthread_mutex_init(&m_serverWaitMutex, nullptr);
-            pthread_cond_init(&m_serverWaitCond, nullptr);
+class BinderLibRpcTestBase : public BinderLibTest {
+public:
+    void SetUp() override {
+        if (!base::GetBoolProperty("ro.debuggable", false)) {
+            GTEST_SKIP() << "Binder RPC is only enabled on debuggable builds, skipping test on "
+                            "non-debuggable builds.";
         }
-        ~BinderLibTestService()
-        {
-            exit(EXIT_SUCCESS);
-        }
+        BinderLibTest::SetUp();
+    }
 
-        void processPendingCall() {
-            if (m_callback != nullptr) {
-                Parcel data;
-                data.writeInt32(NO_ERROR);
-                m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
-                m_callback = nullptr;
-            }
+    std::tuple<android::base::unique_fd, unsigned int> CreateSocket() {
+        auto rpcServer = RpcServer::make();
+        EXPECT_NE(nullptr, rpcServer);
+        if (rpcServer == nullptr) return {};
+        rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+        unsigned int port;
+        if (!rpcServer->setupInetServer(0, &port)) {
+            ADD_FAILURE() << "setupInetServer failed";
+            return {};
         }
+        return {rpcServer->releaseServer(), port};
+    }
+};
 
-        virtual status_t onTransact(uint32_t code,
-                                    const Parcel& data, Parcel* reply,
-                                    uint32_t flags = 0) {
-            if (getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) {
-                return PERMISSION_DENIED;
-            }
-            switch (code) {
+class BinderLibRpcTest : public BinderLibRpcTestBase, public WithParamInterface<bool> {
+public:
+    sp<IBinder> GetService() {
+        return GetParam() ? sp<IBinder>(addServer()) : sp<IBinder>(sp<BBinder>::make());
+    }
+    static std::string ParamToString(const testing::TestParamInfo<ParamType> &info) {
+        return info.param ? "remote" : "local";
+    }
+};
+
+TEST_P(BinderLibRpcTest, SetRpcMaxThreads) {
+    auto binder = GetService();
+    ASSERT_TRUE(binder != nullptr);
+    auto [socket, port] = CreateSocket();
+    ASSERT_TRUE(socket.ok());
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), 1), StatusEq(OK));
+}
+
+TEST_P(BinderLibRpcTest, SetRpcClientNoFd) {
+    auto binder = GetService();
+    ASSERT_TRUE(binder != nullptr);
+    EXPECT_THAT(binder->setRpcClientDebug(android::base::unique_fd(), 1), StatusEq(BAD_VALUE));
+}
+
+TEST_P(BinderLibRpcTest, SetRpcMaxThreadsZero) {
+    auto binder = GetService();
+    ASSERT_TRUE(binder != nullptr);
+    auto [socket, port] = CreateSocket();
+    ASSERT_TRUE(socket.ok());
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), 0), StatusEq(BAD_VALUE));
+}
+
+TEST_P(BinderLibRpcTest, SetRpcMaxThreadsTwice) {
+    auto binder = GetService();
+    ASSERT_TRUE(binder != nullptr);
+
+    auto [socket1, port1] = CreateSocket();
+    ASSERT_TRUE(socket1.ok());
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket1), 1), StatusEq(OK));
+
+    auto [socket2, port2] = CreateSocket();
+    ASSERT_TRUE(socket2.ok());
+    EXPECT_THAT(binder->setRpcClientDebug(std::move(socket2), 1), StatusEq(ALREADY_EXISTS));
+}
+
+INSTANTIATE_TEST_CASE_P(BinderLibTest, BinderLibRpcTest, testing::Bool(),
+                        BinderLibRpcTest::ParamToString);
+
+class BinderLibTestService;
+class BinderLibRpcClientTest : public BinderLibRpcTestBase,
+                               public WithParamInterface<std::tuple<bool, uint32_t>> {
+public:
+    static std::string ParamToString(const testing::TestParamInfo<ParamType> &info) {
+        auto [isRemote, numThreads] = info.param;
+        return (isRemote ? "remote" : "local") + "_server_with_"s + std::to_string(numThreads) +
+                "_threads";
+    }
+    sp<IBinder> CreateRemoteService(int32_t id) {
+        Parcel data, reply;
+        status_t status = data.writeInt32(id);
+        EXPECT_THAT(status, StatusEq(OK));
+        if (status != OK) return nullptr;
+        status = m_server->transact(BINDER_LIB_TEST_CREATE_TEST_SERVICE, data, &reply);
+        EXPECT_THAT(status, StatusEq(OK));
+        if (status != OK) return nullptr;
+        sp<IBinder> ret;
+        status = reply.readStrongBinder(&ret);
+        EXPECT_THAT(status, StatusEq(OK));
+        if (status != OK) return nullptr;
+        return ret;
+    }
+};
+
+TEST_P(BinderLibRpcClientTest, Test) {
+    auto [isRemote, numThreadsParam] = GetParam();
+    uint32_t numThreads = numThreadsParam; // ... to be captured in lambda
+    int32_t id = 0xC0FFEE00 + numThreads;
+    sp<IBinder> server = isRemote ? sp<IBinder>(CreateRemoteService(id))
+                                  : sp<IBinder>(sp<BinderLibTestService>::make(id, false));
+    ASSERT_EQ(isRemote, !!server->remoteBinder());
+    ASSERT_THAT(GetId(server), ResultHasValue(id));
+
+    unsigned int port = 0;
+    // Fake servicedispatcher.
+    {
+        auto [socket, socketPort] = CreateSocket();
+        ASSERT_TRUE(socket.ok());
+        port = socketPort;
+        ASSERT_THAT(server->setRpcClientDebug(std::move(socket), numThreads), StatusEq(OK));
+    }
+
+    auto callUsleep = [](sp<IBinder> server, uint64_t us) {
+        Parcel data, reply;
+        data.markForBinder(server);
+        const char *name = data.isForRpc() ? "RPC" : "binder";
+        EXPECT_THAT(data.writeUint64(us), StatusEq(OK));
+        EXPECT_THAT(server->transact(BINDER_LIB_TEST_USLEEP, data, &reply), StatusEq(OK))
+                << "for " << name << " server";
+    };
+
+    auto threadFn = [&](size_t threadNum) {
+        usleep(threadNum * 50 * 1000); // threadNum * 50ms. Need this to avoid SYN flooding.
+        auto rpcSession = RpcSession::make();
+        ASSERT_TRUE(rpcSession->setupInetClient("127.0.0.1", port));
+        auto rpcServerBinder = rpcSession->getRootObject();
+        ASSERT_NE(nullptr, rpcServerBinder);
+
+        EXPECT_EQ(OK, rpcServerBinder->pingBinder());
+
+        // Check that |rpcServerBinder| and |server| points to the same service.
+        EXPECT_THAT(GetId(rpcServerBinder), ResultHasValue(id));
+
+        // Occupy the server thread. The server should still have enough threads to handle
+        // other connections.
+        // (numThreads - threadNum) * 100ms
+        callUsleep(rpcServerBinder, (numThreads - threadNum) * 100 * 1000);
+    };
+    std::vector<std::thread> threads;
+    for (size_t i = 0; i < numThreads; ++i) threads.emplace_back(std::bind(threadFn, i));
+    for (auto &t : threads) t.join();
+}
+
+INSTANTIATE_TEST_CASE_P(BinderLibTest, BinderLibRpcClientTest,
+                        testing::Combine(testing::Bool(), testing::Range(1u, 10u)),
+                        BinderLibRpcClientTest::ParamToString);
+
+class BinderLibTestService : public BBinder {
+public:
+    explicit BinderLibTestService(int32_t id, bool exitOnDestroy = true)
+          : m_id(id),
+            m_nextServerId(id + 1),
+            m_serverStartRequested(false),
+            m_callback(nullptr),
+            m_exitOnDestroy(exitOnDestroy) {
+        pthread_mutex_init(&m_serverWaitMutex, nullptr);
+        pthread_cond_init(&m_serverWaitCond, nullptr);
+    }
+    ~BinderLibTestService() {
+        if (m_exitOnDestroy) exit(EXIT_SUCCESS);
+    }
+
+    void processPendingCall() {
+        if (m_callback != nullptr) {
+            Parcel data;
+            data.writeInt32(NO_ERROR);
+            m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
+            m_callback = nullptr;
+        }
+    }
+
+    virtual status_t onTransact(uint32_t code, const Parcel &data, Parcel *reply,
+                                uint32_t flags = 0) {
+        // TODO(b/182914638): also checks getCallingUid() for RPC
+        if (!data.isForRpc() && getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) {
+            return PERMISSION_DENIED;
+        }
+        switch (code) {
             case BINDER_LIB_TEST_REGISTER_SERVER: {
                 int32_t id;
                 sp<IBinder> binder;
@@ -1176,8 +1365,7 @@
                     return BAD_VALUE;
                 }
 
-                if (m_id != 0)
-                    return INVALID_OPERATION;
+                if (m_id != 0) return INVALID_OPERATION;
 
                 pthread_mutex_lock(&m_serverWaitMutex);
                 if (m_serverStartRequested) {
@@ -1231,6 +1419,21 @@
                 pthread_mutex_unlock(&m_serverWaitMutex);
                 return ret;
             }
+            case BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION: {
+                IPCThreadState::SpGuard spGuard{
+                        .address = __builtin_frame_address(0),
+                        .context = "GuardInBinderTransaction",
+                };
+                const IPCThreadState::SpGuard *origGuard =
+                        IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+                // if the guard works, this should abort
+                (void)IPCThreadState::self()->getCallingPid();
+
+                IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+                return NO_ERROR;
+            }
+
             case BINDER_LIB_TEST_GETPID:
                 reply->writeInt32(getpid());
                 return NO_ERROR;
@@ -1347,8 +1550,7 @@
                     return BAD_VALUE;
                 }
                 ret = target->linkToDeath(testDeathRecipient);
-                if (ret == NO_ERROR)
-                    ret = testDeathRecipient->waitEvent(5);
+                if (ret == NO_ERROR) ret = testDeathRecipient->waitEvent(5);
                 data2.writeInt32(ret);
                 callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
                 return NO_ERROR;
@@ -1372,8 +1574,7 @@
                     return BAD_VALUE;
                 }
                 ret = write(fd, buf, size);
-                if (ret != size)
-                    return UNKNOWN_ERROR;
+                if (ret != size) return UNKNOWN_ERROR;
                 return NO_ERROR;
             }
             case BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION: {
@@ -1428,8 +1629,7 @@
             case BINDER_LIB_TEST_ECHO_VECTOR: {
                 std::vector<uint64_t> vector;
                 auto err = data.readUint64Vector(&vector);
-                if (err != NO_ERROR)
-                    return err;
+                if (err != NO_ERROR) return err;
                 reply->writeUint64Vector(vector);
                 return NO_ERROR;
             }
@@ -1439,25 +1639,47 @@
             case BINDER_LIB_TEST_CAN_GET_SID: {
                 return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR;
             }
+            case BINDER_LIB_TEST_USLEEP: {
+                uint64_t us;
+                if (status_t status = data.readUint64(&us); status != NO_ERROR) return status;
+                usleep(us);
+                return NO_ERROR;
+            }
+            case BINDER_LIB_TEST_CREATE_TEST_SERVICE: {
+                int32_t id;
+                if (status_t status = data.readInt32(&id); status != NO_ERROR) return status;
+                reply->writeStrongBinder(sp<BinderLibTestService>::make(id, false));
+                return NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
-            };
-        }
-    private:
-        int32_t m_id;
-        int32_t m_nextServerId;
-        pthread_mutex_t m_serverWaitMutex;
-        pthread_cond_t m_serverWaitCond;
-        bool m_serverStartRequested;
-        sp<IBinder> m_serverStarted;
-        sp<IBinder> m_strongRef;
-        sp<IBinder> m_callback;
+        };
+    }
+
+private:
+    int32_t m_id;
+    int32_t m_nextServerId;
+    pthread_mutex_t m_serverWaitMutex;
+    pthread_cond_t m_serverWaitCond;
+    bool m_serverStartRequested;
+    sp<IBinder> m_serverStarted;
+    sp<IBinder> m_strongRef;
+    sp<IBinder> m_callback;
+    bool m_exitOnDestroy;
 };
 
 int run_server(int index, int readypipefd, bool usePoll)
 {
     binderLibTestServiceName += String16(binderserversuffix);
 
+    // Testing to make sure that calls that we are serving can use getCallin*
+    // even though we don't here.
+    IPCThreadState::SpGuard spGuard{
+            .address = __builtin_frame_address(0),
+            .context = "main server thread",
+    };
+    (void)IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
     status_t ret;
     sp<IServiceManager> sm = defaultServiceManager();
     BinderLibTestService* testServicePtr;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index a96deb5..efc70e6 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -23,6 +23,7 @@
 #include <android/binder_libbinder.h>
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <binder/RpcServer.h>
@@ -40,6 +41,8 @@
 #include "../RpcState.h"   // for debugging
 #include "../vm_sockets.h" // for VMADDR_*
 
+using namespace std::chrono_literals;
+
 namespace android {
 
 TEST(BinderRpcParcel, EntireParcelFormatted) {
@@ -191,6 +194,25 @@
             _exit(1);
         }
     }
+
+    Status scheduleShutdown() override {
+        sp<RpcServer> strongServer = server.promote();
+        if (strongServer == nullptr) {
+            return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        }
+        std::thread([=] {
+            LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
+        }).detach();
+        return Status::ok();
+    }
+
+    Status useKernelBinderCallingId() override {
+        // this is WRONG! It does not make sense when using RPC binder, and
+        // because it is SO wrong, and so much code calls this, it should abort!
+
+        (void)IPCThreadState::self()->getCallingPid();
+        return Status::ok();
+    }
 };
 sp<IBinder> MyBinderRpcTest::mHeldBinder;
 
@@ -215,11 +237,13 @@
             prctl(PR_SET_PDEATHSIG, SIGHUP);
 
             f(&mPipe);
+
+            exit(0);
         }
     }
     ~Process() {
         if (mPid != 0) {
-            kill(mPid, SIGKILL);
+            waitpid(mPid, nullptr, 0);
         }
     }
     Pipe* getPipe() { return &mPipe; }
@@ -280,11 +304,11 @@
     sp<IBinderRpcTest> rootIface;
 
     // whether session should be invalidated by end of run
-    bool expectInvalid = false;
+    bool expectAlreadyShutdown = false;
 
     BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
     ~BinderRpcTestProcessSession() {
-        if (!expectInvalid) {
+        if (!expectAlreadyShutdown) {
             std::vector<int32_t> remoteCounts;
             // calling over any sessions counts across all sessions
             EXPECT_OK(rootIface->countBinders(&remoteCounts));
@@ -292,6 +316,8 @@
             for (auto remoteCount : remoteCounts) {
                 EXPECT_EQ(remoteCount, 1);
             }
+
+            EXPECT_OK(rootIface->scheduleShutdown());
         }
 
         rootIface = nullptr;
@@ -363,6 +389,9 @@
                     configure(server);
 
                     server->join();
+
+                    // Another thread calls shutdown. Wait for it to complete.
+                    (void)server->shutdown();
                 }),
         };
 
@@ -414,15 +443,6 @@
     }
 };
 
-TEST_P(BinderRpc, RootObjectIsNull) {
-    auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) {
-        // this is the default, but to be explicit
-        server->setRootObject(nullptr);
-    });
-
-    EXPECT_EQ(nullptr, proc.sessions.at(0).root);
-}
-
 TEST_P(BinderRpc, Ping) {
     auto proc = createRpcTestSocketServerProcess(1);
     ASSERT_NE(proc.rootBinder, nullptr);
@@ -810,7 +830,7 @@
 TEST_P(BinderRpc, OnewayStressTest) {
     constexpr size_t kNumClientThreads = 10;
     constexpr size_t kNumServerThreads = 10;
-    constexpr size_t kNumCalls = 100;
+    constexpr size_t kNumCalls = 50;
 
     auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
 
@@ -833,8 +853,7 @@
     constexpr size_t kReallyLongTimeMs = 100;
     constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
 
-    // more than one thread, just so this doesn't deadlock
-    auto proc = createRpcTestSocketServerProcess(2);
+    auto proc = createRpcTestSocketServerProcess(1);
 
     size_t epochMsBefore = epochMillis();
 
@@ -866,6 +885,14 @@
     size_t epochMsAfter = epochMillis();
 
     EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+
+    // pending oneway transactions hold ref, make sure we read data on all
+    // sockets
+    std::vector<std::thread> threads;
+    for (size_t i = 0; i < 1 + kNumExtraServerThreads; i++) {
+        threads.push_back(std::thread([&] { EXPECT_OK(proc.rootIface->sleepMs(250)); }));
+    }
+    for (auto& t : threads) t.join();
 }
 
 TEST_P(BinderRpc, Die) {
@@ -883,10 +910,23 @@
         EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
                 << "Do death cleanup: " << doDeathCleanup;
 
-        proc.expectInvalid = true;
+        proc.expectAlreadyShutdown = true;
     }
 }
 
+TEST_P(BinderRpc, UseKernelBinderCallingId) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    // we can't allocate IPCThreadState so actually the first time should
+    // succeed :(
+    EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+
+    // second time! we catch the error :)
+    EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
+
+    proc.expectAlreadyShutdown = true;
+}
+
 TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
     auto proc = createRpcTestSocketServerProcess(1);
 
@@ -970,6 +1010,54 @@
 INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
                         ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
+class OneOffSignal {
+public:
+    // If notify() was previously called, or is called within |duration|, return true; else false.
+    template <typename R, typename P>
+    bool wait(std::chrono::duration<R, P> duration) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        return mCv.wait_for(lock, duration, [this] { return mValue; });
+    }
+    void notify() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mValue = true;
+        lock.unlock();
+        mCv.notify_all();
+    }
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    bool mValue = false;
+};
+
+TEST(BinderRpc, Shutdown) {
+    auto addr = allocateSocketAddress();
+    unlink(addr.c_str());
+    auto server = RpcServer::make();
+    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    ASSERT_TRUE(server->setupUnixDomainServer(addr.c_str()));
+    auto joinEnds = std::make_shared<OneOffSignal>();
+
+    // If things are broken and the thread never stops, don't block other tests. Because the thread
+    // may run after the test finishes, it must not access the stack memory of the test. Hence,
+    // shared pointers are passed.
+    std::thread([server, joinEnds] {
+        server->join();
+        joinEnds->notify();
+    }).detach();
+
+    bool shutdown = false;
+    for (int i = 0; i < 10 && !shutdown; i++) {
+        usleep(300 * 1000); // 300ms; total 3s
+        if (server->shutdown()) shutdown = true;
+    }
+    ASSERT_TRUE(shutdown) << "server->shutdown() never returns true";
+
+    ASSERT_TRUE(joinEnds->wait(2s))
+            << "After server->shutdown() returns true, join() did not stop after 2s";
+}
+
 } // namespace android
 
 int main(int argc, char** argv) {
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 2ce13df..6c3b3d9 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -102,7 +102,7 @@
         return Status::ok();
     }
     Status sendAndCallBinder(const sp<IBinder>& binder) override {
-        Stability::debugLogStability("sendAndCallBinder got binder", binder);
+        ALOGI("Debug log stability: %s", Stability::debugToString(binder).c_str());
         return Status::fromExceptionCode(BadStableBinder::doUserTransaction(binder));
     }
     Status returnNoStabilityBinder(sp<IBinder>* _aidl_return) override {
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 624def1..a717ce9 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -66,6 +66,10 @@
     int32_t mValue = 0;
 };
 
+struct BigStruct {
+    uint8_t data[1337];
+};
+
 #define PARCEL_READ_WITH_STATUS(T, FUN) \
     [] (const ::android::Parcel& p, uint8_t /*data*/) {\
         FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
@@ -165,22 +169,20 @@
     PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
     PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
 
-    // TODO(b/131868573): can force read of arbitrarily sized vector
-    // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
 
     // only reading one parcelable type for now
-    // TODO(b/131868573): can force read of arbitrarily sized vector
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
+    PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
     PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
     PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
     PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable),
@@ -189,45 +191,43 @@
     PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
     PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
 
-    // TODO(b/131868573): can force read of arbitrarily sized vector
-    // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
-    // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
+    PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+    PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+    PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
 
-    // TODO(b/131868573): can force read of arbitrarily sized vector
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
-    // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
-    // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
-    // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
-    // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
-    // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
+    PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
+    PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
+    PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
+    PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
+    PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
+    PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
+    PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
+    PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
+    PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
+    PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
+    PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
 
     [] (const android::Parcel& p, uint8_t /*len*/) {
         FUZZ_LOG() << "about to read flattenable";
@@ -242,8 +242,12 @@
         FUZZ_LOG() << "read lite flattenable: " << status;
     },
 
-    // TODO(b/131868573): can force read of arbitrarily sized vector
-    // TODO: resizeOutVector
+    PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, resizeOutVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, resizeOutVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, resizeOutVector),
+    PARCEL_READ_WITH_STATUS(std::vector<BigStruct>, resizeOutVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<BigStruct>>, resizeOutVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<BigStruct>>, resizeOutVector),
 
     PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
     [] (const android::Parcel& p, uint8_t /*len*/) {
@@ -261,10 +265,9 @@
     PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
     PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor),
 
-    // TODO(b/131868573): can force read of arbitrarily sized vector
-    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
-    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
-    // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+    PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+    PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
 
     [] (const android::Parcel& p, uint8_t len) {
         FUZZ_LOG() << "about to readBlob";
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 008780c..6b783a4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -91,28 +91,27 @@
         PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readRequiredParcelFileDescriptor),
         PARCEL_READ(std::string, ndk::AParcel_readString),
         PARCEL_READ(std::optional<std::string>, ndk::AParcel_readString),
-        // TODO(b/131868573): can force process to allocate arbitrary amount of
-        // memory
-        // PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>,
-        // ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>,
-        // ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
-        // PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
-        // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+
+        PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<SomeParcelable>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
+        PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
+        PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
+        PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
 };
 // clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index a47b753..2a79e85 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -23,7 +23,8 @@
 #include <iostream>
 
 #include <android-base/logging.h>
-#include <binder/RpcSession.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.h>
 #include <fuzzbinder/random_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
@@ -33,7 +34,6 @@
 #include <sys/time.h>
 
 using android::fillRandomParcel;
-using android::RpcSession;
 using android::sp;
 
 void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
@@ -46,9 +46,22 @@
     fillRandomParcel(p->parcel(), std::move(provider));
 }
 
+template <typename P, typename B>
+void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider&& provider) {
+    uint32_t code = provider.ConsumeIntegral<uint32_t>();
+    uint32_t flag = provider.ConsumeIntegral<uint32_t>();
+
+    FUZZ_LOG() << "backend: " << backend;
+
+    P reply;
+    P data;
+    fillRandomParcel(&data, std::move(provider));
+    (void)binder->transact(code, data, &reply, flag);
+}
+
 template <typename P>
-void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
-            FuzzedDataProvider&& provider) {
+void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+                FuzzedDataProvider&& provider) {
     // Allow some majority of the bytes to be dedicated to telling us what to
     // do. The fixed value added here represents that we want to test doing a
     // lot of 'instructions' even on really short parcels.
@@ -59,18 +72,7 @@
             provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
 
     P p;
-    if constexpr (std::is_same_v<P, android::Parcel>) {
-        if (provider.ConsumeBool()) {
-            auto session = sp<RpcSession>::make();
-            CHECK(session->addNullDebuggingClient());
-            p.markForRpc(session);
-            fillRandomParcelData(&p, std::move(provider));
-        } else {
-            fillRandomParcel(&p, std::move(provider));
-        }
-    } else {
-        fillRandomParcel(&p, std::move(provider));
-    }
+    fillRandomParcel(&p, std::move(provider));
 
     // since we are only using a byte to index
     CHECK(reads.size() <= 255) << reads.size();
@@ -95,25 +97,18 @@
     }
 }
 
-size_t getHardMemoryLimit() {
-    struct rlimit limit;
-    CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
-    return limit.rlim_max;
+void* NothingClass_onCreate(void* args) {
+    return args;
 }
-
-void setMemoryLimit(size_t cur, size_t max) {
-    const struct rlimit kLimit = {
-       .rlim_cur = cur,
-       .rlim_max = max,
-    };
-    CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+void NothingClass_onDestroy(void* /*userData*/) {}
+binder_status_t NothingClass_onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
+    return STATUS_UNKNOWN_ERROR;
 }
+static AIBinder_Class* kNothingClass =
+        AIBinder_Class_define("nothing", NothingClass_onCreate, NothingClass_onDestroy,
+                              NothingClass_onTransact);
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static constexpr size_t kMemLimit = 1 * 1024 * 1024;
-    size_t hardLimit = getHardMemoryLimit();
-    setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
-
     if (size <= 1) return 0;  // no use
 
     // avoid timeouts, see b/142617274, b/142473153
@@ -121,24 +116,39 @@
 
     FuzzedDataProvider provider = FuzzedDataProvider(data, size);
 
-    const std::function<void(FuzzedDataProvider &&)> fuzzBackend[3] = {
+    const std::function<void(FuzzedDataProvider &&)> fuzzBackend[] = {
             [](FuzzedDataProvider&& provider) {
-                doFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
-                                                    std::move(provider));
+                doTransactFuzz<
+                        ::android::hardware::Parcel>("hwbinder",
+                                                     sp<::android::hardware::BHwBinder>::make(),
+                                                     std::move(provider));
             },
             [](FuzzedDataProvider&& provider) {
-                doFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
-                                          std::move(provider));
+                doTransactFuzz<::android::Parcel>("binder", sp<::android::BBinder>::make(),
+                                                  std::move(provider));
             },
             [](FuzzedDataProvider&& provider) {
-                doFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
-                                         std::move(provider));
+                // fuzz from the libbinder layer since it's a superset of the
+                // interface you get at the libbinder_ndk layer
+                auto ndkBinder = ndk::SpAIBinder(AIBinder_new(kNothingClass, nullptr));
+                auto binder = AIBinder_toPlatformBinder(ndkBinder.get());
+                doTransactFuzz<::android::Parcel>("binder_ndk", binder, std::move(provider));
+            },
+            [](FuzzedDataProvider&& provider) {
+                doReadFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
+                                                        std::move(provider));
+            },
+            [](FuzzedDataProvider&& provider) {
+                doReadFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+                                              std::move(provider));
+            },
+            [](FuzzedDataProvider&& provider) {
+                doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+                                             std::move(provider));
             },
     };
 
     provider.PickValueInArray(fuzzBackend)(std::move(provider));
 
-    setMemoryLimit(hardLimit, hardLimit);
-
     return 0;
 }
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index b045a22..92fdc72 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/logging.h>
 #include <binder/IServiceManager.h>
+#include <binder/RpcSession.h>
 #include <fuzzbinder/random_fd.h>
 #include <utils/String16.h>
 
@@ -33,6 +34,14 @@
 };
 
 void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+    if (provider.ConsumeBool()) {
+        auto session = sp<RpcSession>::make();
+        CHECK(session->addNullDebuggingClient());
+        p->markForRpc(session);
+        fillRandomParcelData(p, std::move(provider));
+        return;
+    }
+
     while (provider.remaining_bytes() > 0) {
         auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
                 // write data
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index 3603ebe..072f8dd 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -20,6 +20,7 @@
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <fuzzer/FuzzedDataProvider.h>
 
 #include <sys/resource.h>
 #include <sys/un.h>
@@ -29,20 +30,6 @@
 static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
         "/binderRpcFuzzerSocket_" + std::to_string(getpid());
 
-size_t getHardMemoryLimit() {
-    struct rlimit limit;
-    CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
-    return limit.rlim_max;
-}
-
-void setMemoryLimit(size_t cur, size_t max) {
-    const struct rlimit kLimit = {
-            .rlim_cur = cur,
-            .rlim_max = max,
-    };
-    CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
-}
-
 class SomeBinder : public BBinder {
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
         (void)flags;
@@ -67,6 +54,7 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     if (size > 50000) return 0;
+    FuzzedDataProvider provider(data, size);
 
     unlink(kSock.c_str());
 
@@ -75,11 +63,7 @@
     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     CHECK(server->setupUnixDomainServer(kSock.c_str()));
 
-    static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024;
-    size_t hardLimit = getHardMemoryLimit();
-    setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
-
-    std::thread serverThread([=] { (void)server->acceptOne(); });
+    std::thread serverThread([=] { (void)server->join(); });
 
     sockaddr_un addr{
             .sun_family = AF_UNIX,
@@ -87,33 +71,43 @@
     CHECK_LT(kSock.size(), sizeof(addr.sun_path));
     memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
 
-    base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    CHECK_NE(clientFd.get(), -1);
-    CHECK_EQ(0,
-             TEMP_FAILURE_RETRY(
-                     connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
-            << strerror(errno);
+    std::vector<base::unique_fd> connections;
 
-    serverThread.join();
+    bool hangupBeforeShutdown = provider.ConsumeBool();
 
-    // TODO(b/182938024): fuzz multiple sessions, instead of just one
+    while (provider.remaining_bytes() > 0) {
+        if (connections.empty() || provider.ConsumeBool()) {
+            base::unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+            CHECK_NE(fd.get(), -1);
+            CHECK_EQ(0,
+                     TEMP_FAILURE_RETRY(
+                             connect(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
+                    << strerror(errno);
+            connections.push_back(std::move(fd));
+        } else {
+            size_t idx = provider.ConsumeIntegralInRange<size_t>(0, connections.size() - 1);
 
-#if 0
-    // make fuzzer more productive locally by forcing it to create a new session
-    int32_t id = -1;
-    CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
-#endif
-
-    CHECK(base::WriteFully(clientFd, data, size));
-
-    clientFd.reset();
-
-    // TODO(b/185167543): better way to force a server to shutdown
-    while (!server->listSessions().empty() && server->numUninitializedSessions()) {
-        usleep(1);
+            if (provider.ConsumeBool()) {
+                std::vector<uint8_t> writeData = provider.ConsumeBytes<uint8_t>(
+                        provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+                CHECK(base::WriteFully(connections.at(idx).get(), writeData.data(),
+                                       writeData.size()));
+            } else {
+                connections.erase(connections.begin() + idx); // hang up
+            }
+        }
     }
 
-    setMemoryLimit(hardLimit, hardLimit);
+    if (hangupBeforeShutdown) {
+        connections.clear();
+        while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+            // wait for all threads to finish processing existing information
+            usleep(1);
+        }
+    }
+
+    while (!server->shutdown()) usleep(1);
+    serverThread.join();
 
     return 0;
 }
diff --git a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
index 69f1b9d..72c5bc4 100644
--- a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
@@ -72,6 +72,11 @@
                               },
                               [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
                                   bbinder->getDebugPid();
+                              },
+                              [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+                                  auto rpcMaxThreads = fdp->ConsumeIntegralInRange<uint32_t>(0, 20);
+                                  (void)bbinder->setRpcClientDebug(android::base::unique_fd(),
+                                                                   rpcMaxThreads);
                               }};
 
 } // namespace android
diff --git a/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
index 8b4ed70..371dcbd 100644
--- a/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
@@ -31,37 +31,27 @@
 static const std::vector<
         std::function<void(FuzzedDataProvider*, android::sp<android::IBinder> const&)>>
         gStabilityOperations = {
-                // markCompilationUnit(IBinder* binder)
                 [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
                     if (!marked) {
                         android::internal::Stability::markCompilationUnit(bbinder.get());
                         marked = true;
                     }
                 },
-
-                // markVintf(IBinder* binder)
                 [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
                     if (!marked) {
                         android::internal::Stability::markVintf(bbinder.get());
                         marked = true;
                     }
                 },
-
-                // debugLogStability(const std::string& tag, const sp<IBinder>& binder)
-                [](FuzzedDataProvider* fdp, android::sp<android::IBinder> const& bbinder) -> void {
-                    std::string tag = fdp->ConsumeRandomLengthString(STABILITY_MAX_TAG_LENGTH);
-                    android::internal::Stability::debugLogStability(tag, bbinder);
+                [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+                    (void)android::internal::Stability::debugToString(bbinder);
                 },
-
-                // markVndk(IBinder* binder)
                 [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
                     if (!marked) {
                         android::internal::Stability::markVndk(bbinder.get());
                         marked = true;
                     }
                 },
-
-                // requiresVintfDeclaration(const sp<IBinder>& binder)
                 [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
                     android::internal::Stability::requiresVintfDeclaration(bbinder);
                 }};