libbinder: add RpcTransportTipcAndroid
Adds a new Binder RPC transport in Android for Trusty IPC.
To enable code sharing between the transports, this moves
interruptableReadOrWrite into a new RpcTransportUtils.h header
that is used by RpcTransportRaw and RpcTransportTrusty.
Bug: 224644083
Test: trusty_binder_test
Change-Id: I47843560374049821cbfc36875a738083ffd5d75
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 441a4a8..fabf3eb 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -339,6 +339,34 @@
defaults: ["libbinder_tls_defaults"],
}
+cc_library_shared {
+ name: "libbinder_trusty",
+ vendor: true,
+ srcs: [
+ "RpcTransportTipcAndroid.cpp",
+ "RpcTrusty.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libtrusty",
+ "libutils",
+ ],
+ static_libs: [
+ "libbase",
+ ],
+ export_include_dirs: ["include_trusty"],
+
+ // Most of Android doesn't need this library and shouldn't use it,
+ // so we restrict its visibility to the Trusty-specific packages.
+ visibility: [
+ ":__subpackages__",
+ "//system/core/trusty:__subpackages__",
+ "//vendor:__subpackages__",
+ ],
+}
+
// For testing
cc_library_static {
name: "libbinder_tls_static",
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
new file mode 100644
index 0000000..79983f4
--- /dev/null
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcTransportTipcAndroid"
+
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcAndroid.h>
+#include <log/log.h>
+#include <poll.h>
+#include <trusty/tipc.h>
+
+#include "FdTrigger.h"
+#include "RpcState.h"
+#include "RpcTransportUtils.h"
+
+using android::base::Error;
+using android::base::Result;
+
+namespace android {
+
+namespace {
+
+// RpcTransport for writing Trusty IPC clients in Android.
+class RpcTransportTipcAndroid : public RpcTransport {
+public:
+ explicit RpcTransportTipcAndroid(android::base::unique_fd socket)
+ : mSocket(std::move(socket)) {}
+
+ status_t pollRead() override {
+ if (mReadBufferPos < mReadBufferSize) {
+ // We have more data in the read buffer
+ return OK;
+ }
+
+ // Trusty IPC device is not a socket, so MSG_PEEK is not available
+ pollfd pfd{.fd = mSocket.get(), .events = static_cast<int16_t>(POLLIN), .revents = 0};
+ ssize_t ret = TEMP_FAILURE_RETRY(::poll(&pfd, 1, 0));
+ if (ret < 0) {
+ int savedErrno = errno;
+ if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
+ return WOULD_BLOCK;
+ }
+
+ LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+
+ if (pfd.revents & POLLNVAL) {
+ return BAD_VALUE;
+ }
+ if (pfd.revents & POLLERR) {
+ return DEAD_OBJECT;
+ }
+ if (pfd.revents & POLLHUP) {
+ return DEAD_OBJECT;
+ }
+ if (pfd.revents & POLLIN) {
+ return OK;
+ }
+
+ return WOULD_BLOCK;
+ }
+
+ status_t interruptableWriteFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+ override {
+ auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
+ // TODO: send ancillaryFds. For now, we just abort if anyone tries
+ // to send any.
+ LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(),
+ "File descriptors are not supported on Trusty yet");
+ return TEMP_FAILURE_RETRY(tipc_send(mSocket.get(), iovs, niovs, nullptr, 0));
+ };
+ return interruptableReadOrWrite(mSocket.get(), fdTrigger, iovs, niovs, writeFn, "tipc_send",
+ POLLOUT, altPoll);
+ }
+
+ status_t interruptableReadFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/)
+ override {
+ auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
+ // Fill the read buffer at most once per readFn call, then try to
+ // return as much of it as possible. If the input iovecs are spread
+ // across multiple messages that require multiple fillReadBuffer
+ // calls, we expect the caller to advance the iovecs past the first
+ // read and call readFn as many times as needed to get all the data
+ status_t ret = fillReadBuffer();
+ if (ret != OK) {
+ return ret;
+ }
+
+ ssize_t processSize = 0;
+ for (size_t i = 0; i < niovs && mReadBufferPos < mReadBufferSize; i++) {
+ auto& iov = iovs[i];
+ size_t numBytes = std::min(iov.iov_len, mReadBufferSize - mReadBufferPos);
+ memcpy(iov.iov_base, mReadBuffer.get() + mReadBufferPos, numBytes);
+ mReadBufferPos += numBytes;
+ processSize += numBytes;
+ }
+
+ return processSize;
+ };
+ return interruptableReadOrWrite(mSocket.get(), fdTrigger, iovs, niovs, readFn, "read",
+ POLLIN, altPoll);
+ }
+
+private:
+ status_t fillReadBuffer() {
+ if (mReadBufferPos < mReadBufferSize) {
+ return OK;
+ }
+
+ if (!mReadBuffer) {
+ // Guarantee at least kDefaultBufferSize bytes
+ mReadBufferCapacity = std::max(mReadBufferCapacity, kDefaultBufferSize);
+ mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]);
+ if (!mReadBuffer) {
+ return NO_MEMORY;
+ }
+ }
+
+ // Reset the size and position in case we have to exit with an error.
+ // After we read a message into the buffer, we update the size
+ // with the actual value.
+ mReadBufferPos = 0;
+ mReadBufferSize = 0;
+
+ while (true) {
+ ssize_t processSize =
+ TEMP_FAILURE_RETRY(read(mSocket.get(), mReadBuffer.get(), mReadBufferCapacity));
+ if (processSize == 0) {
+ return DEAD_OBJECT;
+ } else if (processSize < 0) {
+ int savedErrno = errno;
+ if (savedErrno == EMSGSIZE) {
+ // Buffer was too small, double it and retry
+ if (__builtin_mul_overflow(mReadBufferCapacity, 2, &mReadBufferCapacity)) {
+ return NO_MEMORY;
+ }
+ mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]);
+ if (!mReadBuffer) {
+ return NO_MEMORY;
+ }
+ continue;
+ } else {
+ LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+ } else {
+ mReadBufferSize = static_cast<size_t>(processSize);
+ return OK;
+ }
+ }
+ }
+
+ base::unique_fd mSocket;
+
+ // For now, we copy all the input data into a temporary buffer because
+ // we might get multiple interruptableReadFully calls per message, but
+ // the tipc device only allows one read call. We read every message into
+ // this temporary buffer, then return pieces of it from our method.
+ //
+ // The special transaction GET_MAX_THREADS takes 40 bytes, so the default
+ // size should start pretty high.
+ static constexpr size_t kDefaultBufferSize = 64;
+ std::unique_ptr<uint8_t[]> mReadBuffer;
+ size_t mReadBufferPos = 0;
+ size_t mReadBufferSize = 0;
+ size_t mReadBufferCapacity = 0;
+};
+
+// RpcTransportCtx for Trusty.
+class RpcTransportCtxTipcAndroid : public RpcTransportCtx {
+public:
+ std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
+ FdTrigger*) const override {
+ return std::make_unique<RpcTransportTipcAndroid>(std::move(fd));
+ }
+ std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
+};
+
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const {
+ return std::make_unique<RpcTransportCtxTipcAndroid>();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newClientCtx() const {
+ return std::make_unique<RpcTransportCtxTipcAndroid>();
+}
+
+const char* RpcTransportCtxFactoryTipcAndroid::toCString() const {
+ return "trusty";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcAndroid::make() {
+ return std::unique_ptr<RpcTransportCtxFactoryTipcAndroid>(
+ new RpcTransportCtxFactoryTipcAndroid());
+}
+
+} // namespace android
diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp
new file mode 100644
index 0000000..ea49eef
--- /dev/null
+++ b/libs/binder/RpcTrusty.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcTrusty"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcAndroid.h>
+#include <trusty/tipc.h>
+
+namespace android {
+
+using android::base::unique_fd;
+
+sp<IBinder> RpcTrustyConnect(const char* device, const char* port) {
+ auto session = RpcSession::make(RpcTransportCtxFactoryTipcAndroid::make());
+ auto request = [=] {
+ int tipcFd = tipc_connect(device, port);
+ if (tipcFd < 0) {
+ LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd;
+ return unique_fd();
+ }
+ return unique_fd(tipcFd);
+ };
+ if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
+ LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ return session->getRootObject();
+}
+
+} // namespace android
diff --git a/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h
new file mode 100644
index 0000000..4a4172a
--- /dev/null
+++ b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses Trusty IPC.
+
+#pragma once
+
+#include <memory>
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory for writing Trusty IPC clients in Android.
+class RpcTransportCtxFactoryTipcAndroid : public RpcTransportCtxFactory {
+public:
+ static std::unique_ptr<RpcTransportCtxFactory> make();
+
+ std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
+ std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
+ const char* toCString() const override;
+
+private:
+ RpcTransportCtxFactoryTipcAndroid() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/include_trusty/binder/RpcTrusty.h b/libs/binder/include_trusty/binder/RpcTrusty.h
new file mode 100644
index 0000000..f124e0c
--- /dev/null
+++ b/libs/binder/include_trusty/binder/RpcTrusty.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IBinder.h>
+
+namespace android {
+
+sp<IBinder> RpcTrustyConnect(const char* device, const char* port);
+
+} // namespace android