Merge "Fix warnings in preparation for Rust 1.54.0"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 37fc9a9..e3c4ede 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -266,7 +266,10 @@
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace
 
-on post-fs-data
+# Only create the tracing instance if persist.mm_events.enabled
+# Attempting to remove the tracing instance after it has been created
+# will likely fail with EBUSY as it would be in use by traced_probes.
+on post-fs-data && property:persist.mm_events.enabled=true
 # Create MM Events Tracing Instance for Kmem Activity Trigger
     mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system
     mkdir /sys/kernel/tracing/instances/mm_events 0755 system system
@@ -275,10 +278,18 @@
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb
     chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb
 
+# Set the default buffer size to the minimum
+    write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1
+    write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1
+
 # Read and enable tracing
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on
     chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on
 
+# Tracing disabled by default
+    write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0
+    write /sys/kernel/tracing/instances/mm_events/tracing_on 0
+
 # Read and truncate kernel trace
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace
     chmod 0666 /sys/kernel/tracing/instances/mm_events/trace
diff --git a/cmds/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp
new file mode 100644
index 0000000..0c78c5a
--- /dev/null
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_fuzz {
+    name: "cmd_fuzzer",
+    srcs: [
+        "cmd_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libcmd",
+        "libutils",
+        "liblog",
+        "libselinux",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/cmds/cmd/fuzzer/README.md b/cmds/cmd/fuzzer/README.md
new file mode 100644
index 0000000..db37ece
--- /dev/null
+++ b/cmds/cmd/fuzzer/README.md
@@ -0,0 +1,51 @@
+# Fuzzer for libcmd_fuzzer
+
+## Plugin Design Considerations
+The fuzzer plugin for libcmd is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libcmd supports the following parameters:
+1. In (parameter name: `in`)
+2. Out (parameter name: `out`)
+3. Err (parameter name: `err`)
+4. Run Mode (parameter name: `runMode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `in` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `out` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `err` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `runMode` | 1.`RunMode::kStandalone` 2. `RunMode::kLibrary` | Value chosen from valid values using FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the cmd module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build cmd_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) cmd_fuzzer
+```
+#### Steps to run
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/cmd_fuzzer/cmd_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/cmds/cmd/fuzzer/cmd_fuzzer.cpp b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
new file mode 100644
index 0000000..ab514a1
--- /dev/null
+++ b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/TextOutput.h>
+#include <cmd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace std;
+using namespace android;
+
+class TestTextOutput : public TextOutput {
+public:
+    TestTextOutput() {}
+    virtual ~TestTextOutput() {}
+
+    virtual status_t print(const char* /*txt*/, size_t /*len*/) { return NO_ERROR; }
+    virtual void moveIndent(int /*delta*/) { return; }
+    virtual void pushBundle() { return; }
+    virtual void popBundle() { return; }
+};
+
+class CmdFuzzer {
+public:
+    void process(const uint8_t* data, size_t size);
+
+private:
+    FuzzedDataProvider* mFDP = nullptr;
+};
+
+void CmdFuzzer::process(const uint8_t* data, size_t size) {
+    mFDP = new FuzzedDataProvider(data, size);
+    vector<string> arguments;
+    if (mFDP->ConsumeBool()) {
+        if (mFDP->ConsumeBool()) {
+            arguments = {"-w", "media.aaudio"};
+        } else {
+            arguments = {"-l"};
+        }
+    } else {
+        while (mFDP->remaining_bytes() > 0) {
+            size_t sizestr = mFDP->ConsumeIntegralInRange<size_t>(1, mFDP->remaining_bytes());
+            string argument = mFDP->ConsumeBytesAsString(sizestr);
+            arguments.emplace_back(argument);
+        }
+    }
+    vector<string_view> argSV;
+    for (auto& argument : arguments) {
+        argSV.emplace_back(argument.c_str());
+    }
+    int32_t in = open("/dev/null", O_RDWR | O_CREAT);
+    int32_t out = open("/dev/null", O_RDWR | O_CREAT);
+    int32_t err = open("/dev/null", O_RDWR | O_CREAT);
+    TestTextOutput output;
+    TestTextOutput error;
+    RunMode runMode = mFDP->ConsumeBool() ? RunMode::kStandalone : RunMode::kLibrary;
+    cmdMain(argSV, output, error, in, out, err, runMode);
+    delete mFDP;
+    close(in);
+    close(out);
+    close(err);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    CmdFuzzer cmdFuzzer;
+    cmdFuzzer.process(data, size);
+    return 0;
+}
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 2722e21..ff73c94 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -28,6 +28,7 @@
 #include <sstream>
 
 #include <android-base/file.h>
+#include <android-base/hex.h>
 #include <android-base/logging.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl-hash/Hash.h>
@@ -691,8 +692,7 @@
             }
 
             auto&& hashArray = hashChain[hashIndex];
-            std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
-            entry->hash = Hash::hexString(hashVec);
+            entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
         });
         if (!hashRet.isOk()) {
             handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp
index 8e21975..1753343 100644
--- a/cmds/lshal/TableEntry.cpp
+++ b/cmds/lshal/TableEntry.cpp
@@ -18,6 +18,7 @@
 
 #include <map>
 
+#include <android-base/hex.h>
 #include <android-base/strings.h>
 #include <hidl-hash/Hash.h>
 #include <vintf/parse_string.h>
@@ -104,7 +105,8 @@
 }
 
 std::string TableEntry::isReleased() const {
-    static const std::string unreleased = Hash::hexString(Hash::kEmptyHash);
+    static const std::string unreleased = android::base::HexString(Hash::kEmptyHash.data(),
+                                                                   Hash::kEmptyHash.size());
 
     if (hash.empty()) {
         return "?";
diff --git a/docs/Doxyfile b/docs/Doxyfile
index a1bd960..ea22337 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1638,6 +1638,7 @@
     "__attribute__(x)=" \
     __ANDROID__ \
     __BIONIC__ \
+    "U_IN_DOXYGEN=1" \ # Required by the ICU4C module only
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 2175fd4..6e318ea 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -154,8 +154,7 @@
     return checkPermission(permission, pid, uid);
 }
 
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid)
-{
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure) {
     static Mutex gPermissionControllerLock;
     static sp<IPermissionController> gPermissionController;
 
@@ -180,8 +179,10 @@
 
             // Is this a permission failure, or did the controller go away?
             if (IInterface::asBinder(pc)->isBinderAlive()) {
-                ALOGW("Permission failure: %s from uid=%d pid=%d",
-                        String8(permission).string(), uid, pid);
+                if (logPermissionFailure) {
+                    ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).string(),
+                          uid, pid);
+                }
                 return false;
             }
 
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 200d923..62ea187 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -110,6 +110,10 @@
     return mMaxThreads;
 }
 
+void RpcServer::setProtocolVersion(uint32_t version) {
+    mProtocolVersion = version;
+}
+
 void RpcServer::setRootObject(const sp<IBinder>& binder) {
     std::lock_guard<std::mutex> _l(mLock);
     mRootObjectWeak = mRootObject = binder;
@@ -245,13 +249,37 @@
     RpcConnectionHeader header;
     status_t status = server->mShutdownTrigger->interruptableReadFully(clientFd.get(), &header,
                                                                        sizeof(header));
-    bool idValid = status == OK;
-    if (!idValid) {
+    if (status != OK) {
         ALOGE("Failed to read ID for client connecting to RPC server: %s",
               statusToString(status).c_str());
         // still need to cleanup before we can return
     }
-    bool incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+
+    bool incoming = false;
+    uint32_t protocolVersion = 0;
+    RpcAddress sessionId = RpcAddress::zero();
+    bool requestingNewSession = false;
+
+    if (status == OK) {
+        incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+        protocolVersion = std::min(header.version,
+                                   server->mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION));
+        sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
+        requestingNewSession = sessionId.isZero();
+
+        if (requestingNewSession) {
+            RpcNewSessionResponse response{
+                    .version = protocolVersion,
+            };
+
+            status = server->mShutdownTrigger->interruptableWriteFully(clientFd.get(), &response,
+                                                                       sizeof(response));
+            if (status != OK) {
+                ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
+                // still need to cleanup before we can return
+            }
+        }
+    }
 
     std::thread thisThread;
     sp<RpcSession> session;
@@ -269,19 +297,16 @@
         };
         server->mConnectingThreads.erase(threadId);
 
-        if (!idValid || server->mShutdownTrigger->isTriggered()) {
+        if (status != OK || server->mShutdownTrigger->isTriggered()) {
             return;
         }
 
-        RpcAddress sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
-
-        if (sessionId.isZero()) {
+        if (requestingNewSession) {
             if (incoming) {
                 ALOGE("Cannot create a new session with an incoming connection, would leak");
                 return;
             }
 
-            sessionId = RpcAddress::zero();
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
@@ -295,6 +320,7 @@
 
             session = RpcSession::make();
             session->setMaxThreads(server->mMaxThreads);
+            if (!session->setProtocolVersion(protocolVersion)) return;
             if (!session->setForServer(server,
                                        sp<RpcServer::EventListener>::fromExisting(
                                                static_cast<RpcServer::EventListener*>(
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 1c37651..254b99c 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -77,6 +77,31 @@
     return mMaxThreads;
 }
 
+bool RpcSession::setProtocolVersion(uint32_t version) {
+    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
+        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
+              "is %u).",
+              version, RPC_WIRE_PROTOCOL_VERSION);
+        return false;
+    }
+
+    std::lock_guard<std::mutex> _l(mMutex);
+    if (mProtocolVersion && version > *mProtocolVersion) {
+        ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u",
+              *mProtocolVersion, version);
+        return false;
+    }
+
+    mProtocolVersion = version;
+    return true;
+}
+
+std::optional<uint32_t> RpcSession::getProtocolVersion() {
+    std::lock_guard<std::mutex> _l(mMutex);
+    return mProtocolVersion;
+}
+
 bool RpcSession::setupUnixDomainClient(const char* path) {
     return setupSocketClient(UnixSocketAddress(path));
 }
@@ -424,6 +449,18 @@
 
     if (!setupOneSocketConnection(addr, RpcAddress::zero(), false /*incoming*/)) return false;
 
+    {
+        ExclusiveConnection connection;
+        status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
+                                                    ConnectionUse::CLIENT, &connection);
+        if (status != OK) return false;
+
+        uint32_t version;
+        status = state()->readNewSessionResponse(connection.get(),
+                                                 sp<RpcSession>::fromExisting(this), &version);
+        if (!setProtocolVersion(version)) return false;
+    }
+
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once.
     // TODO(b/186470974): first risk of blocking
@@ -484,7 +521,10 @@
             return false;
         }
 
-        RpcConnectionHeader header{.options = 0};
+        RpcConnectionHeader header{
+                .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
+                .options = 0,
+        };
         memcpy(&header.sessionId, &id.viewRawEmbedded(), sizeof(RpcWireAddress));
 
         if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 332c75f..f3406bb 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -315,6 +315,18 @@
     return OK;
 }
 
+status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+                                          const sp<RpcSession>& session, uint32_t* version) {
+    RpcNewSessionResponse response;
+    if (status_t status =
+                rpcRec(connection, session, "new session response", &response, sizeof(response));
+        status != OK) {
+        return status;
+    }
+    *version = response.version;
+    return OK;
+}
+
 status_t RpcState::sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
                                       const sp<RpcSession>& session) {
     RpcOutgoingConnectionInit init{
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 5ac0b97..1446eec 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -60,6 +60,8 @@
     RpcState();
     ~RpcState();
 
+    status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+                                    const sp<RpcSession>& session, uint32_t* version);
     status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
                                 const sp<RpcSession>& session);
     status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 2a44c7a..0f8efd2 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -37,9 +37,20 @@
  * either as part of a new session or an existing session
  */
 struct RpcConnectionHeader {
+    uint32_t version; // maximum supported by caller
+    uint8_t reserver0[4];
     RpcWireAddress sessionId;
     uint8_t options;
-    uint8_t reserved[7];
+    uint8_t reserved1[7];
+};
+
+/**
+ * In response to an RpcConnectionHeader which corresponds to a new session,
+ * this returns information to the server.
+ */
+struct RpcNewSessionResponse {
+    uint32_t version; // maximum supported by callee <= maximum supported by caller
+    uint8_t reserved[4];
 };
 
 #define RPC_CONNECTION_INIT_OKAY "cci"
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 8e46147..d152005 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -167,7 +167,8 @@
 bool checkCallingPermission(const String16& permission);
 bool checkCallingPermission(const String16& permission,
                             int32_t* outPid, int32_t* outUid);
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid,
+                     bool logPermissionFailure = true);
 
 #ifndef __ANDROID__
 // Create an IServiceManager that delegates the service manager on the device via adb.
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index a8094dd..40ff78c 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -105,6 +105,13 @@
     size_t getMaxThreads();
 
     /**
+     * By default, the latest protocol version which is supported by a client is
+     * used. However, this can be used in order to prevent newer protocol
+     * versions from ever being used. This is expected to be useful for testing.
+     */
+    void setProtocolVersion(uint32_t version);
+
+    /**
      * The root object can be retrieved by any client, without any
      * authentication. TODO(b/183988761)
      *
@@ -164,6 +171,7 @@
 
     bool mAgreedExperimental = false;
     size_t mMaxThreads = 1;
+    std::optional<uint32_t> mProtocolVersion;
     base::unique_fd mServer; // socket we are accepting sessions on
 
     std::mutex mLock; // for below
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 2101df8..1f7c029 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -37,6 +37,10 @@
 class RpcSocketAddress;
 class RpcState;
 
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 0;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+
 /**
  * This represents a session (group of connections) between a client
  * and a server. Multiple connections are needed for multiple parallel "binder"
@@ -60,6 +64,13 @@
     size_t getMaxThreads();
 
     /**
+     * By default, the minimum of the supported versions of the client and the
+     * server will be used. Usually, this API should only be used for debugging.
+     */
+    [[nodiscard]] bool setProtocolVersion(uint32_t version);
+    std::optional<uint32_t> getProtocolVersion();
+
+    /**
      * This should be called once per thread, matching 'join' in the remote
      * process.
      */
@@ -291,6 +302,7 @@
     std::mutex mMutex; // for all below
 
     size_t mMaxThreads = 0;
+    std::optional<uint32_t> mProtocolVersion;
 
     std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
     size_t mWaitingThreads = 0;
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index a457e67..5f4a7b5 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,21 +18,31 @@
 #include <android-base/logging.h>
 #include <benchmark/benchmark.h>
 #include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
 
 #include <thread>
 
+#include <signal.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 using android::BBinder;
+using android::defaultServiceManager;
 using android::IBinder;
 using android::interface_cast;
+using android::IPCThreadState;
+using android::IServiceManager;
 using android::OK;
+using android::ProcessState;
 using android::RpcServer;
 using android::RpcSession;
 using android::sp;
+using android::String16;
 using android::binder::Status;
 
 class MyBinderRpcBenchmark : public BnBinderRpcBenchmark {
@@ -46,28 +56,51 @@
     }
 };
 
-static sp<RpcSession> gSession = RpcSession::make();
+enum Transport {
+    KERNEL,
+    RPC,
+};
 
-void BM_getRootObject(benchmark::State& state) {
-    while (state.KeepRunning()) {
-        CHECK(gSession->getRootObject() != nullptr);
+static void EachTransport(benchmark::internal::Benchmark* b) {
+#ifdef __BIONIC__
+    b->Args({Transport::KERNEL});
+#endif
+    b->Args({Transport::RPC});
+}
+
+static sp<RpcSession> gSession = RpcSession::make();
+#ifdef __BIONIC__
+static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
+static sp<IBinder> gKernelBinder;
+#endif
+
+static sp<IBinder> getBinderForOptions(benchmark::State& state) {
+    Transport transport = static_cast<Transport>(state.range(0));
+    switch (transport) {
+#ifdef __BIONIC__
+        case KERNEL:
+            return gKernelBinder;
+#endif
+        case RPC:
+            return gSession->getRootObject();
+        default:
+            LOG(FATAL) << "Unknown transport value: " << transport;
+            return nullptr;
     }
 }
-BENCHMARK(BM_getRootObject);
 
 void BM_pingTransaction(benchmark::State& state) {
-    sp<IBinder> binder = gSession->getRootObject();
-    CHECK(binder != nullptr);
+    sp<IBinder> binder = getBinderForOptions(state);
 
     while (state.KeepRunning()) {
         CHECK_EQ(OK, binder->pingBinder());
     }
 }
-BENCHMARK(BM_pingTransaction);
+BENCHMARK(BM_pingTransaction)->Apply(EachTransport);
 
 void BM_repeatString(benchmark::State& state) {
-    sp<IBinder> binder = gSession->getRootObject();
-    CHECK(binder != nullptr);
+    sp<IBinder> binder = getBinderForOptions(state);
+
     sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
     CHECK(iface != nullptr);
 
@@ -92,7 +125,7 @@
         CHECK(ret.isOk()) << ret;
     }
 }
-BENCHMARK(BM_repeatString);
+BENCHMARK(BM_repeatString)->Apply(EachTransport);
 
 void BM_repeatBinder(benchmark::State& state) {
     sp<IBinder> binder = gSession->getRootObject();
@@ -109,7 +142,7 @@
         CHECK(ret.isOk()) << ret;
     }
 }
-BENCHMARK(BM_repeatBinder);
+BENCHMARK(BM_repeatBinder)->Apply(EachTransport);
 
 int main(int argc, char** argv) {
     ::benchmark::Initialize(&argc, argv);
@@ -118,13 +151,36 @@
     std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
     (void)unlink(addr.c_str());
 
-    std::thread([addr]() {
+    std::cerr << "Tests suffixes:" << std::endl;
+    std::cerr << "\t\\" << Transport::KERNEL << " is KERNEL" << std::endl;
+    std::cerr << "\t\\" << Transport::RPC << " is RPC" << std::endl;
+
+    if (0 == fork()) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
         sp<RpcServer> server = RpcServer::make();
         server->setRootObject(sp<MyBinderRpcBenchmark>::make());
         server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
         CHECK(server->setupUnixDomainServer(addr.c_str()));
         server->join();
-    }).detach();
+        exit(1);
+    }
+
+#ifdef __BIONIC__
+    if (0 == fork()) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
+        CHECK_EQ(OK,
+                 defaultServiceManager()->addService(kKernelBinderInstance,
+                                                     sp<MyBinderRpcBenchmark>::make()));
+        IPCThreadState::self()->joinThreadPool();
+        exit(1);
+    }
+
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+
+    gKernelBinder = defaultServiceManager()->waitForService(kKernelBinderInstance);
+    CHECK_NE(nullptr, gKernelBinder.get());
+#endif
 
     for (size_t tries = 0; tries < 5; tries++) {
         usleep(10000);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 40ebd9c..d5786bc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -47,6 +47,9 @@
 
 namespace android {
 
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+              RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+
 TEST(BinderRpcParcel, EntireParcelFormatted) {
     Parcel p;
     p.writeInt32(3);
@@ -67,6 +70,19 @@
     ASSERT_EQ(sinkFd, retrieved.get());
 }
 
+TEST(BinderRpc, CannotUseNextWireVersion) {
+    auto session = RpcSession::make();
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+    auto session = RpcSession::make();
+    EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \