Merge "Tilt scale correction for certain resolutions"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6f5f70d..287dd48 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -124,7 +124,6 @@
{ "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } },
{ "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } },
{ "rro", "Runtime Resource Overlay", ATRACE_TAG_RRO, { } },
- { "sysprop", "System Property", ATRACE_TAG_SYSPROP, { } },
{ k_coreServiceCategory, "Core services", 0, { } },
{ k_pdxServiceCategory, "PDX services", 0, { } },
{ "sched", "CPU Scheduling", 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..8262bc2
--- /dev/null
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_cmds_cmd_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_cmds_cmd_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/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index c9a7c7b..1db2867 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1236,12 +1236,19 @@
std::string path(title);
path.append(" - ").append(String8(service).c_str());
size_t bytes_written = 0;
- status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args);
+ status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
if (status == OK) {
dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
std::chrono::duration<double> elapsed_seconds;
- status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
- /* as_proto = */ false, elapsed_seconds, bytes_written);
+ if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
+ service == String16("meminfo")) {
+ // Use a longer timeout for meminfo, since 30s is not always enough.
+ status = dumpsys.writeDump(STDOUT_FILENO, service, 60s,
+ /* as_proto = */ false, elapsed_seconds, bytes_written);
+ } else {
+ status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
+ /* as_proto = */ false, elapsed_seconds, bytes_written);
+ }
dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
bool dump_complete = (status == OK);
dumpsys.stopDumpThread(dump_complete);
@@ -1308,7 +1315,7 @@
path.append("_HIGH");
}
path.append(kProtoExt);
- status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args);
+ status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
if (status == OK) {
status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout);
bool dumpTerminated = (status == OK);
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 83a52b8..3d2bdf1 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -21,7 +21,9 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <binder/BpBinder.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/Stability.h>
@@ -58,26 +60,30 @@
}
static void usage() {
- fprintf(stderr,
- "usage: dumpsys\n"
- " To dump all services.\n"
- "or:\n"
- " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | "
- "--skip SERVICES "
- "| SERVICE [ARGS]]\n"
- " --help: shows this help\n"
- " -l: only list services, do not dump them\n"
- " -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"
- " --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");
+ fprintf(
+ stderr,
+ "usage: dumpsys\n"
+ " To dump all services.\n"
+ "or:\n"
+ " dumpsys [-t TIMEOUT] [--priority LEVEL] [--clients] [--dump] [--pid] [--thread] "
+ "[--help | "
+ "-l | --skip SERVICES "
+ "| SERVICE [ARGS]]\n"
+ " --help: shows this help\n"
+ " -l: only list services, do not dump them\n"
+ " -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"
+ " --clients: dump client PIDs instead of usual dump\n"
+ " --dump: ask the service to dump itself (this is the default)\n"
+ " --pid: dump PID 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");
}
static bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
@@ -127,17 +133,15 @@
bool showListOnly = false;
bool skipServices = false;
bool asProto = false;
- Type type = Type::DUMP;
+ int dumpTypeFlags = 0;
int timeoutArgMs = 10000;
int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
- 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},
- {"stability", no_argument, 0, 0},
- {"thread", no_argument, 0, 0},
- {0, 0, 0, 0}};
+ static struct option longOptions[] = {
+ {"help", no_argument, 0, 0}, {"clients", no_argument, 0, 0},
+ {"dump", no_argument, 0, 0}, {"pid", no_argument, 0, 0},
+ {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0},
+ {"skip", 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
// happens on test cases).
@@ -168,12 +172,16 @@
usage();
return -1;
}
+ } else if (!strcmp(longOptions[optionIndex].name, "dump")) {
+ dumpTypeFlags |= TYPE_DUMP;
} else if (!strcmp(longOptions[optionIndex].name, "pid")) {
- type = Type::PID;
+ dumpTypeFlags |= TYPE_PID;
} else if (!strcmp(longOptions[optionIndex].name, "stability")) {
- type = Type::STABILITY;
+ dumpTypeFlags |= TYPE_STABILITY;
} else if (!strcmp(longOptions[optionIndex].name, "thread")) {
- type = Type::THREAD;
+ dumpTypeFlags |= TYPE_THREAD;
+ } else if (!strcmp(longOptions[optionIndex].name, "clients")) {
+ dumpTypeFlags |= TYPE_CLIENTS;
}
break;
@@ -211,6 +219,10 @@
}
}
+ if (dumpTypeFlags == 0) {
+ dumpTypeFlags = TYPE_DUMP;
+ }
+
for (int i = optind; i < argc; i++) {
if (skipServices) {
skippedServices.add(String16(argv[i]));
@@ -263,7 +275,7 @@
const String16& serviceName = services[i];
if (IsSkipped(skippedServices, serviceName)) continue;
- if (startDumpThread(type, serviceName, args) == OK) {
+ if (startDumpThread(dumpTypeFlags, serviceName, args) == OK) {
bool addSeparator = (N > 1);
if (addSeparator) {
writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
@@ -330,18 +342,21 @@
}
}
-static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd) {
+static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd, bool exclusive) {
pid_t pid;
status_t status = service->getDebugPid(&pid);
if (status != OK) {
return status;
}
+ if (!exclusive) {
+ WriteStringToFd("Service host process PID: ", fd.get());
+ }
WriteStringToFd(std::to_string(pid) + "\n", fd.get());
return OK;
}
static status_t dumpStabilityToFd(const sp<IBinder>& service, const unique_fd& fd) {
- WriteStringToFd(internal::Stability::debugToString(service) + "\n", fd);
+ WriteStringToFd("Stability: " + internal::Stability::debugToString(service) + "\n", fd);
return OK;
}
@@ -362,7 +377,43 @@
return OK;
}
-status_t Dumpsys::startDumpThread(Type type, const String16& serviceName,
+static status_t dumpClientsToFd(const sp<IBinder>& service, const unique_fd& fd) {
+ std::string clientPids;
+ const auto remoteBinder = service->remoteBinder();
+ if (remoteBinder == nullptr) {
+ WriteStringToFd("Client PIDs are not available for local binders.\n", fd.get());
+ return OK;
+ }
+ const auto handle = remoteBinder->getDebugBinderHandle();
+ if (handle == std::nullopt) {
+ return OK;
+ }
+ std::vector<pid_t> pids;
+ pid_t myPid = getpid();
+ pid_t servicePid;
+ status_t status = service->getDebugPid(&servicePid);
+ if (status != OK) {
+ return status;
+ }
+ status =
+ getBinderClientPids(BinderDebugContext::BINDER, myPid, servicePid, handle.value(), &pids);
+ if (status != OK) {
+ return status;
+ }
+ pids.erase(std::remove_if(pids.begin(), pids.end(), [&](pid_t pid) { return pid == myPid; }),
+ pids.end());
+ WriteStringToFd("Client PIDs: " + ::android::base::Join(pids, ", ") + "\n", fd.get());
+ return OK;
+}
+
+static void reportDumpError(const String16& serviceName, status_t error, const char* context) {
+ if (error == OK) return;
+
+ std::cerr << "Error with service '" << serviceName << "' while " << context << ": "
+ << statusToString(error) << std::endl;
+}
+
+status_t Dumpsys::startDumpThread(int dumpTypeFlags, const String16& serviceName,
const Vector<String16>& args) {
sp<IBinder> service = sm_->checkService(serviceName);
if (service == nullptr) {
@@ -383,29 +434,27 @@
// dump blocks until completion, so spawn a thread..
activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
- status_t err = 0;
-
- switch (type) {
- case Type::DUMP:
- err = service->dump(remote_end.get(), args);
- break;
- 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;
- default:
- std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl;
- return;
+ if (dumpTypeFlags & TYPE_PID) {
+ status_t err = dumpPidToFd(service, remote_end, dumpTypeFlags == TYPE_PID);
+ reportDumpError(serviceName, err, "dumping PID");
+ }
+ if (dumpTypeFlags & TYPE_STABILITY) {
+ status_t err = dumpStabilityToFd(service, remote_end);
+ reportDumpError(serviceName, err, "dumping stability");
+ }
+ if (dumpTypeFlags & TYPE_THREAD) {
+ status_t err = dumpThreadsToFd(service, remote_end);
+ reportDumpError(serviceName, err, "dumping thread info");
+ }
+ if (dumpTypeFlags & TYPE_CLIENTS) {
+ status_t err = dumpClientsToFd(service, remote_end);
+ reportDumpError(serviceName, err, "dumping clients info");
}
- if (err != OK) {
- std::cerr << "Error dumping service info status_t: " << statusToString(err) << " "
- << serviceName << std::endl;
+ // other types always act as a header, this is usually longer
+ if (dumpTypeFlags & TYPE_DUMP) {
+ status_t err = service->dump(remote_end.get(), args);
+ reportDumpError(serviceName, err, "dumping");
}
});
return OK;
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 1b3ae6a..6ab1a7d 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -51,24 +51,26 @@
*/
static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags);
- enum class Type {
- DUMP, // dump using `dump` function
- PID, // dump pid of server only
- STABILITY, // dump stability information of server
- THREAD, // dump thread usage of server only
+ enum Type {
+ TYPE_DUMP = 0x1, // dump using `dump` function
+ TYPE_PID = 0x2, // dump pid of server only
+ TYPE_STABILITY = 0x4, // dump stability information of server
+ TYPE_THREAD = 0x8, // dump thread usage of server only
+ TYPE_CLIENTS = 0x10, // dump pid of clients
};
/**
* Starts a thread to connect to a service and get its dump output. The thread redirects
* the output to a pipe. Thread must be stopped by a subsequent call to {@code
* stopDumpThread}.
+ * @param dumpTypeFlags operations to perform
* @param serviceName
* @param args list of arguments to pass to service dump method.
* @return {@code OK} thread is started successfully.
* {@code NAME_NOT_FOUND} service could not be found.
* {@code != OK} error
*/
- status_t startDumpThread(Type type, const String16& serviceName,
+ status_t startDumpThread(int dumpTypeFlags, const String16& serviceName,
const Vector<String16>& args);
/**
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 277f445..fa63db5 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -200,7 +200,7 @@
CaptureStdout();
CaptureStderr();
dump_.setServiceArgs(args, supportsProto, priorityFlags);
- status_t status = dump_.startDumpThread(Dumpsys::Type::DUMP, serviceName, args);
+ status_t status = dump_.startDumpThread(Dumpsys::TYPE_DUMP, serviceName, args);
EXPECT_THAT(status, Eq(0));
status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false,
elapsedDuration, bytesWritten);
@@ -627,6 +627,51 @@
AssertOutputFormat(format);
}
+// Tests 'dumpsys --clients'
+TEST_F(DumpsysTest, ListAllServicesWithClients) {
+ ExpectListServices({"Locksmith", "Valet"});
+ ExpectCheckService("Locksmith");
+ ExpectCheckService("Valet");
+
+ CallMain({"--clients"});
+
+ AssertRunningServices({"Locksmith", "Valet"});
+
+ const std::string format("(.|\n)*((Client PIDs are not available for local binders.)(.|\n)*){2}");
+ AssertOutputFormat(format);
+}
+
+// Tests 'dumpsys --clients service_name'
+TEST_F(DumpsysTest, ListServiceWithClients) {
+ ExpectCheckService("Locksmith");
+
+ CallMain({"--clients", "Locksmith"});
+
+ const std::string format("Client PIDs are not available for local binders.\n");
+ AssertOutputFormat(format);
+}
+// Tests 'dumpsys --thread --stability'
+TEST_F(DumpsysTest, ListAllServicesWithMultipleOptions) {
+ ExpectListServices({"Locksmith", "Valet"});
+ ExpectCheckService("Locksmith");
+ ExpectCheckService("Valet");
+
+ CallMain({"--pid", "--stability"});
+ AssertRunningServices({"Locksmith", "Valet"});
+
+ AssertOutputContains(std::to_string(getpid()));
+ AssertOutputContains("stability");
+}
+
+// Tests 'dumpsys --pid --stability service_name'
+TEST_F(DumpsysTest, ListServiceWithMultipleOptions) {
+ ExpectCheckService("Locksmith");
+ CallMain({"--pid", "--stability", "Locksmith"});
+
+ AssertOutputContains(std::to_string(getpid()));
+ AssertOutputContains("stability");
+}
+
TEST_F(DumpsysTest, GetBytesWritten) {
const char* serviceName = "service2";
const char* dumpContents = "dump1";
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 15f0c5b..210f977 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1231,6 +1231,14 @@
}
}
+ // On-device signing related. odsign sets the system property odsign.verification.success if
+ // AOT artifacts have the expected signatures.
+ const bool trust_art_apex_data_files =
+ ::android::base::GetBoolProperty("odsign.verification.success", false);
+ if (!trust_art_apex_data_files) {
+ AddRuntimeArg("-Xdeny-art-apex-data-files");
+ }
+
PrepareArgs(dexoptanalyzer_bin);
}
@@ -1363,10 +1371,12 @@
return kSecondaryDexAccessReadOk;
} else {
if (errno == ENOENT) {
- LOG(INFO) << "Secondary dex does not exist: " << dex_path;
+ async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+ "Secondary dex does not exist: %s", dex_path.c_str());
return kSecondaryDexAccessDoesNotExist;
} else {
- PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not access secondary dex: %s (%d)", dex_path.c_str(), errno);
return errno == EACCES
? kSecondaryDexAccessPermissionError
: kSecondaryDexAccessIOError;
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index e847626..b661684 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -283,6 +283,13 @@
}
}
+ // On-device signing related. odsign sets the system property odsign.verification.success if
+ // AOT artifacts have the expected signatures.
+ const bool trust_art_apex_data_files = GetBoolProperty("odsign.verification.success", false);
+ if (!trust_art_apex_data_files) {
+ AddRuntimeArg("-Xdeny-art-apex-data-files");
+ }
+
if (target_sdk_version != 0) {
AddRuntimeArg(StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version));
}
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/Android.bp b/libs/binder/Android.bp
index da570f3..72d8afd 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -104,6 +104,7 @@
"BpBinder.cpp",
"BufferedTextOutput.cpp",
"Debug.cpp",
+ "FdTrigger.cpp",
"IInterface.cpp",
"IMemory.cpp",
"IPCThreadState.cpp",
@@ -123,6 +124,7 @@
"RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
+ "RpcTransportRaw.cpp",
"Static.cpp",
"Stability.cpp",
"Status.cpp",
@@ -147,9 +149,6 @@
enabled: false,
},
host: {
- static_libs: [
- "libbase",
- ],
srcs: [
"ServiceManagerHost.cpp",
"UtilsHost.cpp",
@@ -190,8 +189,13 @@
"libutils",
],
+ static_libs: [
+ "libbase",
+ ],
+
header_libs: [
"libbinder_headers",
+ "libandroid_runtime_vm_headers",
],
export_header_lib_headers: [
@@ -231,6 +235,42 @@
},
}
+cc_defaults {
+ name: "libbinder_tls_shared_deps",
+ shared_libs: [
+ "libbinder",
+ "libcrypto",
+ "liblog",
+ "libssl",
+ "libutils",
+ ],
+}
+
+cc_defaults {
+ name: "libbinder_tls_defaults",
+ defaults: ["libbinder_tls_shared_deps"],
+ host_supported: true,
+
+ header_libs: [
+ "libbinder_headers",
+ ],
+ export_header_lib_headers: [
+ "libbinder_headers",
+ ],
+ export_include_dirs: ["include_tls"],
+ static_libs: [
+ "libbase",
+ ],
+ srcs: [
+ "RpcTransportTls.cpp",
+ ],
+}
+
+cc_library_shared {
+ name: "libbinder_tls",
+ defaults: ["libbinder_tls_defaults"],
+}
+
// AIDL interface between libbinder and framework.jar
filegroup {
name: "libbinder_aidl",
@@ -287,7 +327,7 @@
// Do not expand the visibility.
visibility: [
"//packages/modules/Virtualization/authfs:__subpackages__",
- "//packages/modules/Virtualization/compos",
+ "//packages/modules/Virtualization/compos:__subpackages__",
"//packages/modules/Virtualization/microdroid",
],
}
@@ -356,5 +396,6 @@
"libbinder",
"liblog",
"libutils",
+ "android.debug_aidl-cpp",
],
}
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 628381c..d3eef4e 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -555,7 +555,9 @@
return status;
}
rpcServer->setRootObjectWeak(weakThis);
- rpcServer->setupExternalServer(std::move(socketFd));
+ if (auto status = rpcServer->setupExternalServer(std::move(socketFd)); status != OK) {
+ return status;
+ }
rpcServer->setMaxThreads(binderThreadPoolMaxCount);
rpcServer->start();
e->mRpcServerLinks.emplace(link);
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3099296..55566e2 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -188,6 +188,14 @@
return std::get<BinderHandle>(mHandle).handle;
}
+std::optional<int32_t> BpBinder::getDebugBinderHandle() const {
+ if (!isRpcBinder()) {
+ return binderHandle();
+ } else {
+ return std::nullopt;
+ }
+}
+
bool BpBinder::isDescriptorCached() const {
Mutex::Autolock _l(mLock);
return mDescriptorCache.size() ? true : false;
@@ -261,15 +269,15 @@
if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
using android::internal::Stability;
- auto category = Stability::getCategory(this);
+ int16_t stability = Stability::getRepr(this);
Stability::Level required = privateVendor ? Stability::VENDOR
: Stability::getLocalLevel();
- if (CC_UNLIKELY(!Stability::check(category, required))) {
+ if (CC_UNLIKELY(!Stability::check(stability, required))) {
ALOGE("Cannot do a user transaction on a %s binder (%s) in a %s context.",
- category.debugString().c_str(),
- String8(getInterfaceDescriptor()).c_str(),
- Stability::levelString(required).c_str());
+ Stability::levelString(stability).c_str(),
+ String8(getInterfaceDescriptor()).c_str(),
+ Stability::levelString(required).c_str());
return BAD_TYPE;
}
}
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index 8676955..e4ac4b4 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -26,22 +26,6 @@
namespace android {
-std::string hexString(const void* bytes, size_t len) {
- if (bytes == nullptr) return "<null>";
-
- const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
- const char chars[] = "0123456789abcdef";
- std::string result;
- result.resize(len * 2);
-
- for (size_t i = 0; i < len; i++) {
- result[2 * i] = chars[bytes8[i] >> 4];
- result[2 * i + 1] = chars[bytes8[i] & 0xf];
- }
-
- return result;
-}
-
// ---------------------------------------------------------------------
static const char indentStr[] =
diff --git a/libs/binder/Debug.h b/libs/binder/Debug.h
index 7ca087e..262dfba 100644
--- a/libs/binder/Debug.h
+++ b/libs/binder/Debug.h
@@ -23,8 +23,6 @@
namespace android {
// ---------------------------------------------------------------------------
-std::string hexString(const void* data, size_t size);
-
const char* stringForIndent(int32_t indentLevel);
typedef void (*debugPrintFunc)(void* cookie, const char* txt);
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
new file mode 100644
index 0000000..b197a6a
--- /dev/null
+++ b/libs/binder/FdTrigger.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "FdTrigger"
+#include <log/log.h>
+
+#include <poll.h>
+
+#include <android-base/macros.h>
+
+#include "FdTrigger.h"
+namespace android {
+
+std::unique_ptr<FdTrigger> FdTrigger::make() {
+ auto ret = std::make_unique<FdTrigger>();
+ if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) {
+ ALOGE("Could not create pipe %s", strerror(errno));
+ return nullptr;
+ }
+ return ret;
+}
+
+void FdTrigger::trigger() {
+ mWrite.reset();
+}
+
+bool FdTrigger::isTriggered() {
+ return mWrite == -1;
+}
+
+status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
+ while (true) {
+ pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
+ {.fd = mRead.get(), .events = 0, .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 & event ? OK : DEAD_OBJECT;
+ }
+}
+
+android::base::Result<bool> FdTrigger::isTriggeredPolled() {
+ pollfd pfd{.fd = mRead.get(), .events = 0, .revents = 0};
+ int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, 0));
+ if (ret < 0) {
+ return android::base::ErrnoError() << "FdTrigger::isTriggeredPolled: Error in poll()";
+ }
+ if (ret == 0) {
+ return false;
+ }
+ if (pfd.revents & POLLHUP) {
+ return true;
+ }
+ return android::base::Error() << "FdTrigger::isTriggeredPolled: poll() returns " << pfd.revents;
+}
+
+} // namespace android
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
new file mode 100644
index 0000000..a428417
--- /dev/null
+++ b/libs/binder/FdTrigger.h
@@ -0,0 +1,67 @@
+/*
+ * 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 <memory>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+/** This is not a pipe. */
+class FdTrigger {
+public:
+ /** Returns nullptr for error case */
+ static std::unique_ptr<FdTrigger> make();
+
+ /**
+ * Close the write end of the pipe so that the read end receives POLLHUP.
+ * Not threadsafe.
+ */
+ void trigger();
+
+ /**
+ * Check whether this has been triggered by checking the write end.
+ */
+ bool isTriggered();
+
+ /**
+ * Poll for a read event.
+ *
+ * event - for pollfd
+ *
+ * Return:
+ * true - time to read!
+ * false - trigger happened
+ */
+ status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
+
+ /**
+ * Check whether this has been triggered by poll()ing the read end.
+ *
+ * Return:
+ * true - triggered
+ * false - not triggered
+ * error - error when polling
+ */
+ android::base::Result<bool> isTriggeredPolled();
+
+private:
+ base::unique_fd mWrite;
+ base::unique_fd mRead;
+};
+} // namespace android
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/Parcel.cpp b/libs/binder/Parcel.cpp
index ebba375..956524a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -173,7 +173,7 @@
status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder)
{
internal::Stability::tryMarkCompilationUnit(binder.get());
- int16_t rep = internal::Stability::getCategory(binder.get()).repr();
+ int16_t rep = internal::Stability::getRepr(binder.get());
return writeInt32(rep);
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ef4341..caa00a5 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -409,7 +409,7 @@
uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
if (result == -1) {
- ALOGD("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+ ALOGV("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
}
} else {
ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp
index 98dee9a..ffc94b9 100644
--- a/libs/binder/RpcAddress.cpp
+++ b/libs/binder/RpcAddress.cpp
@@ -16,6 +16,7 @@
#include <binder/RpcAddress.h>
+#include <android-base/hex.h>
#include <binder/Parcel.h>
#include "Debug.h"
@@ -94,7 +95,7 @@
}
std::string RpcAddress::toString() const {
- return hexString(mRawAddr.get(), sizeof(RpcWireAddress));
+ return base::HexString(mRawAddr.get(), sizeof(RpcWireAddress));
}
status_t RpcAddress::writeToParcel(Parcel* parcel) const {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 2a87ae4..a20445b 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "RpcServer"
+#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -25,8 +26,10 @@
#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
+#include <binder/RpcTransportRaw.h>
#include <log/log.h>
+#include "FdTrigger.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
#include "RpcWireFormat.h"
@@ -36,39 +39,42 @@
using base::ScopeGuard;
using base::unique_fd;
-RpcServer::RpcServer() {}
+RpcServer::RpcServer(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory)
+ : mRpcTransportCtxFactory(std::move(rpcTransportCtxFactory)) {}
RpcServer::~RpcServer() {
(void)shutdown();
}
-sp<RpcServer> RpcServer::make() {
- return sp<RpcServer>::make();
+sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+ // Default is without TLS.
+ if (rpcTransportCtxFactory == nullptr)
+ rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make();
+ return sp<RpcServer>::make(std::move(rpcTransportCtxFactory));
}
void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
mAgreedExperimental = true;
}
-bool RpcServer::setupUnixDomainServer(const char* path) {
+status_t RpcServer::setupUnixDomainServer(const char* path) {
return setupSocketServer(UnixSocketAddress(path));
}
-bool RpcServer::setupVsockServer(unsigned int port) {
+status_t RpcServer::setupVsockServer(unsigned int port) {
// realizing value w/ this type at compile time to avoid ubsan abort
constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
return setupSocketServer(VsockSocketAddress(kAnyCid, port));
}
-bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
- const char* kAddr = "127.0.0.1";
-
+status_t RpcServer::setupInetServer(const char* address, unsigned int port,
+ unsigned int* assignedPort) {
if (assignedPort != nullptr) *assignedPort = 0;
- auto aiStart = InetSocketAddress::getAddrInfo(kAddr, port);
- if (aiStart == nullptr) return false;
+ auto aiStart = InetSocketAddress::getAddrInfo(address, port);
+ if (aiStart == nullptr) return UNKNOWN_ERROR;
for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
- InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, kAddr, port);
- if (!setupSocketServer(socketAddress)) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, address, port);
+ if (status_t status = setupSocketServer(socketAddress); status != OK) {
continue;
}
@@ -79,7 +85,7 @@
int savedErrno = errno;
ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(),
strerror(savedErrno));
- return false;
+ return -savedErrno;
}
LOG_ALWAYS_FATAL_IF(len != sizeof(addr), "Wrong socket type: len %zu vs len %zu",
static_cast<size_t>(len), sizeof(addr));
@@ -92,11 +98,11 @@
*assignedPort = realPort;
}
- return true;
+ return OK;
}
- ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr,
+ ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", address,
port);
- return false;
+ return UNKNOWN_ERROR;
}
void RpcServer::setMaxThreads(size_t threads) {
@@ -109,6 +115,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;
@@ -147,14 +157,18 @@
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();
+ mShutdownTrigger = FdTrigger::make();
LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Cannot create join signaler");
+
+ mCtx = mRpcTransportCtxFactory->newServerCtx();
+ LOG_ALWAYS_FATAL_IF(mCtx == nullptr, "Unable to create RpcTransportCtx with %s sockets",
+ mRpcTransportCtxFactory->toCString());
}
status_t status;
- while ((status = mShutdownTrigger->triggerablePollRead(mServer)) == OK) {
+ while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
unique_fd clientFd(TEMP_FAILURE_RETRY(
- accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
+ accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC | SOCK_NONBLOCK)));
if (clientFd < 0) {
ALOGE("Could not accept4 socket: %s", strerror(errno));
@@ -182,7 +196,7 @@
bool RpcServer::shutdown() {
std::unique_lock<std::mutex> _l(mLock);
if (mShutdownTrigger == nullptr) {
- LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed.");
+ LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)");
return false;
}
@@ -212,7 +226,10 @@
mJoinThread.reset();
}
+ LOG_RPC_DETAIL("Finished waiting on shutdown.");
+
mShutdownTrigger = nullptr;
+ mCtx = nullptr;
return true;
}
@@ -238,17 +255,56 @@
// 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);
+ LOG_ALWAYS_FATAL_IF(server->mCtx == nullptr);
+
+ status_t status = OK;
+
+ int clientFdForLog = clientFd.get();
+ auto client = server->mCtx->newTransport(std::move(clientFd), server->mShutdownTrigger.get());
+ if (client == nullptr) {
+ ALOGE("Dropping accept4()-ed socket because sslAccept fails");
+ status = DEAD_OBJECT;
+ // still need to cleanup before we can return
+ } else {
+ LOG_RPC_DETAIL("Created RpcTransport %p for client fd %d", client.get(), clientFdForLog);
+ }
RpcConnectionHeader header;
- status_t status = server->mShutdownTrigger->interruptableReadFully(clientFd.get(), &header,
- sizeof(header));
- 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
+ if (status == OK) {
+ status = client->interruptableReadFully(server->mShutdownTrigger.get(), &header,
+ sizeof(header));
+ 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 reverse = header.options & RPC_CONNECTION_OPTION_REVERSE;
+
+ 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 = client->interruptableWriteFully(server->mShutdownTrigger.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;
@@ -266,19 +322,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 (reverse) {
- ALOGE("Cannot create a new session with a reverse connection, would leak");
+ 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
@@ -292,6 +345,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*>(
@@ -312,8 +366,8 @@
session = it->second;
}
- if (reverse) {
- LOG_ALWAYS_FATAL_IF(!session->addOutgoingConnection(std::move(clientFd), true),
+ if (incoming) {
+ LOG_ALWAYS_FATAL_IF(OK != session->addOutgoingConnection(std::move(client), true),
"server state must already be initialized");
return;
}
@@ -322,7 +376,7 @@
session->preJoinThreadOwnership(std::move(thisThread));
}
- auto setupResult = session->preJoinSetup(std::move(clientFd));
+ auto setupResult = session->preJoinSetup(std::move(client));
// avoid strong cycle
server = nullptr;
@@ -330,39 +384,44 @@
RpcSession::join(std::move(session), std::move(setupResult));
}
-bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
+status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ unique_fd serverFd(TEMP_FAILURE_RETRY(
+ socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
if (serverFd == -1) {
- ALOGE("Could not create socket: %s", strerror(errno));
- return false;
+ int savedErrno = errno;
+ ALOGE("Could not create socket: %s", strerror(savedErrno));
+ return -savedErrno;
}
if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
int savedErrno = errno;
ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
+ return -savedErrno;
}
- if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client,
+ // the backlog is increased to a large number.
+ // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced
+ // to 1.
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 50 /*backlog*/))) {
int savedErrno = errno;
ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
+ return -savedErrno;
}
LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
- if (!setupExternalServer(std::move(serverFd))) {
+ if (status_t status = setupExternalServer(std::move(serverFd)); status != OK) {
ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
- return false;
+ return status;
}
- return true;
+ return OK;
}
-void RpcServer::onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) {
+void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) {
auto id = session->mId;
LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
LOG_RPC_DETAIL("Dropping session with address %s", id->toString().c_str());
@@ -392,15 +451,15 @@
return std::move(mServer);
}
-bool RpcServer::setupExternalServer(base::unique_fd serverFd) {
+status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
std::lock_guard<std::mutex> _l(mLock);
if (mServer.ok()) {
ALOGE("Each RpcServer can only have one server.");
- return false;
+ return INVALID_OPERATION;
}
mServer = std::move(serverFd);
- return true;
+ return OK;
}
} // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index ee5e8bb..4c47005 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -18,21 +18,28 @@
#include <binder/RpcSession.h>
+#include <dlfcn.h>
#include <inttypes.h>
#include <poll.h>
+#include <pthread.h>
#include <unistd.h>
#include <string_view>
#include <android-base/macros.h>
+#include <android_runtime/vm.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
+#include <binder/RpcTransportRaw.h>
#include <binder/Stability.h>
+#include <jni.h>
#include <utils/String8.h>
+#include "FdTrigger.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
#include "RpcWireFormat.h"
+#include "Utils.h"
#ifdef __GLIBC__
extern "C" pid_t gettid();
@@ -42,7 +49,8 @@
using base::unique_fd;
-RpcSession::RpcSession() {
+RpcSession::RpcSession(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory)
+ : mRpcTransportCtxFactory(std::move(rpcTransportCtxFactory)) {
LOG_RPC_DETAIL("RpcSession created %p", this);
mState = std::make_unique<RpcState>();
@@ -55,8 +63,11 @@
"Should not be able to destroy a session with servers in use.");
}
-sp<RpcSession> RpcSession::make() {
- return sp<RpcSession>::make();
+sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+ // Default is without TLS.
+ if (rpcTransportCtxFactory == nullptr)
+ rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make();
+ return sp<RpcSession>::make(std::move(rpcTransportCtxFactory));
}
void RpcSession::setMaxThreads(size_t threads) {
@@ -73,34 +84,88 @@
return mMaxThreads;
}
-bool RpcSession::setupUnixDomainClient(const char* path) {
- return setupSocketClient(UnixSocketAddress(path));
-}
-
-bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
- return setupSocketClient(VsockSocketAddress(cid, port));
-}
-
-bool RpcSession::setupInetClient(const char* addr, unsigned int port) {
- auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
- if (aiStart == nullptr) return false;
- for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
- InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
- if (setupSocketClient(socketAddress)) return true;
- }
- ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
- return false;
-}
-
-bool RpcSession::addNullDebuggingClient() {
- unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
-
- if (serverFd == -1) {
- ALOGE("Could not connect to /dev/null: %s", strerror(errno));
+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;
}
- return addOutgoingConnection(std::move(serverFd), 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;
+}
+
+status_t RpcSession::setupUnixDomainClient(const char* path) {
+ return setupSocketClient(UnixSocketAddress(path));
+}
+
+status_t RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
+ return setupSocketClient(VsockSocketAddress(cid, port));
+}
+
+status_t RpcSession::setupInetClient(const char* addr, unsigned int port) {
+ auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+ if (aiStart == nullptr) return UNKNOWN_ERROR;
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
+ if (status_t status = setupSocketClient(socketAddress); status == OK) return OK;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
+ return NAME_NOT_FOUND;
+}
+
+status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
+ return setupClient([&](const RpcAddress& sessionId, bool incoming) -> status_t {
+ // std::move'd from fd becomes -1 (!ok())
+ if (!fd.ok()) {
+ fd = request();
+ if (!fd.ok()) return BAD_VALUE;
+ }
+ if (auto res = setNonBlocking(fd); !res.ok()) {
+ ALOGE("setupPreconnectedClient: %s", res.error().message().c_str());
+ return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
+ }
+ return initAndAddConnection(std::move(fd), sessionId, incoming);
+ });
+}
+
+status_t RpcSession::addNullDebuggingClient() {
+ // Note: only works on raw sockets.
+ if (auto status = initShutdownTrigger(); status != OK) return status;
+
+ unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
+
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not connect to /dev/null: %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+
+ auto ctx = mRpcTransportCtxFactory->newClientCtx();
+ if (ctx == nullptr) {
+ ALOGE("Unable to create RpcTransportCtx for null debugging client");
+ return NO_MEMORY;
+ }
+ auto server = ctx->newTransport(std::move(serverFd), mShutdownTrigger.get());
+ if (server == nullptr) {
+ ALOGE("Unable to set up RpcTransport");
+ return UNKNOWN_ERROR;
+ }
+ return addOutgoingConnection(std::move(server), false);
}
sp<IBinder> RpcSession::getRootObject() {
@@ -128,6 +193,7 @@
if (wait) {
LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed");
mShutdownListener->waitForShutdown(_l);
+
LOG_ALWAYS_FATAL_IF(!mThreads.empty(), "Shutdown failed");
}
@@ -158,62 +224,6 @@
return state()->sendDecStrong(connection.get(), sp<RpcSession>::fromExisting(this), address);
}
-std::unique_ptr<RpcSession::FdTrigger> RpcSession::FdTrigger::make() {
- auto ret = std::make_unique<RpcSession::FdTrigger>();
- if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) {
- ALOGE("Could not create pipe %s", strerror(errno));
- return nullptr;
- }
- return ret;
-}
-
-void RpcSession::FdTrigger::trigger() {
- mWrite.reset();
-}
-
-bool RpcSession::FdTrigger::isTriggered() {
- return mWrite == -1;
-}
-
-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;
-
- MAYBE_WAIT_IN_FLAKE_MODE;
-
- 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);
@@ -234,7 +244,7 @@
return OK;
}
-void RpcSession::WaitForShutdownListener::onSessionLockedAllIncomingThreadsEnded(
+void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded(
const sp<RpcSession>& session) {
(void)session;
mShutdown = true;
@@ -261,12 +271,19 @@
}
}
-RpcSession::PreJoinSetupResult RpcSession::preJoinSetup(base::unique_fd fd) {
+RpcSession::PreJoinSetupResult RpcSession::preJoinSetup(
+ std::unique_ptr<RpcTransport> rpcTransport) {
// must be registered to allow arbitrary client code executing commands to
// be able to do nested calls (we can't only read from it)
- sp<RpcConnection> connection = assignIncomingConnectionToThisThread(std::move(fd));
+ sp<RpcConnection> connection = assignIncomingConnectionToThisThread(std::move(rpcTransport));
- status_t status = mState->readConnectionInit(connection, sp<RpcSession>::fromExisting(this));
+ status_t status;
+
+ if (connection == nullptr) {
+ status = DEAD_OBJECT;
+ } else {
+ status = mState->readConnectionInit(connection, sp<RpcSession>::fromExisting(this));
+ }
return PreJoinSetupResult{
.connection = std::move(connection),
@@ -274,10 +291,67 @@
};
}
+namespace {
+// RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If
+// Android Runtime doesn't exist, no-op.
+class JavaThreadAttacher {
+public:
+ JavaThreadAttacher() {
+ // Use dlsym to find androidJavaAttachThread because libandroid_runtime is loaded after
+ // libbinder.
+ auto vm = getJavaVM();
+ if (vm == nullptr) return;
+
+ char threadName[16];
+ if (0 != pthread_getname_np(pthread_self(), threadName, sizeof(threadName))) {
+ constexpr const char* defaultThreadName = "UnknownRpcSessionThread";
+ memcpy(threadName, defaultThreadName,
+ std::min<size_t>(sizeof(threadName), strlen(defaultThreadName) + 1));
+ }
+ LOG_RPC_DETAIL("Attaching current thread %s to JVM", threadName);
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_2;
+ args.name = threadName;
+ args.group = nullptr;
+ JNIEnv* env;
+
+ LOG_ALWAYS_FATAL_IF(vm->AttachCurrentThread(&env, &args) != JNI_OK,
+ "Cannot attach thread %s to JVM", threadName);
+ mAttached = true;
+ }
+ ~JavaThreadAttacher() {
+ if (!mAttached) return;
+ auto vm = getJavaVM();
+ LOG_ALWAYS_FATAL_IF(vm == nullptr,
+ "Unable to detach thread. No JavaVM, but it was present before!");
+
+ LOG_RPC_DETAIL("Detaching current thread from JVM");
+ if (vm->DetachCurrentThread() != JNI_OK) {
+ mAttached = false;
+ } else {
+ ALOGW("Unable to detach current thread from JVM");
+ }
+ }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher);
+ bool mAttached = false;
+
+ static JavaVM* getJavaVM() {
+ static auto fn = reinterpret_cast<decltype(&AndroidRuntimeGetJavaVM)>(
+ dlsym(RTLD_DEFAULT, "AndroidRuntimeGetJavaVM"));
+ if (fn == nullptr) return nullptr;
+ return fn();
+ }
+};
+} // namespace
+
void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult) {
sp<RpcConnection>& connection = setupResult.connection;
if (setupResult.status == OK) {
+ LOG_ALWAYS_FATAL_IF(!connection, "must have connection if setup succeeded");
+ JavaThreadAttacher javaThreadAttacher;
while (true) {
status_t status = session->state()->getAndExecuteCommand(connection, session,
RpcState::CommandType::ANY);
@@ -292,9 +366,6 @@
statusToString(setupResult.status).c_str());
}
- LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
- "bad state: connection object guaranteed to be in list");
-
sp<RpcSession::EventListener> listener;
{
std::lock_guard<std::mutex> _l(session->mMutex);
@@ -306,6 +377,12 @@
listener = session->mEventListener.promote();
}
+ // done after all cleanup, since session shutdown progresses via callbacks here
+ if (connection != nullptr) {
+ LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
+ "bad state: connection object guaranteed to be in list");
+ }
+
session = nullptr;
if (listener != nullptr) {
@@ -322,36 +399,48 @@
return server;
}
-bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+status_t RpcSession::setupClient(
+ const std::function<status_t(const RpcAddress& sessionId, bool incoming)>& connectAndInit) {
{
std::lock_guard<std::mutex> _l(mMutex);
LOG_ALWAYS_FATAL_IF(mOutgoingConnections.size() != 0,
"Must only setup session once, but already has %zu clients",
mOutgoingConnections.size());
}
+ if (auto status = initShutdownTrigger(); status != OK) return status;
- if (!setupOneSocketConnection(addr, RpcAddress::zero(), false /*reverse*/)) return false;
+ if (status_t status = connectAndInit(RpcAddress::zero(), false /*incoming*/); status != OK)
+ return status;
+
+ {
+ ExclusiveConnection connection;
+ if (status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
+ ConnectionUse::CLIENT, &connection);
+ status != OK)
+ return status;
+
+ uint32_t version;
+ if (status_t status =
+ state()->readNewSessionResponse(connection.get(),
+ sp<RpcSession>::fromExisting(this), &version);
+ status != OK)
+ return status;
+ if (!setProtocolVersion(version)) return BAD_VALUE;
+ }
// TODO(b/189955605): we should add additional sessions dynamically
// instead of all at once.
- // TODO(b/186470974): first risk of blocking
size_t numThreadsAvailable;
if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) {
- ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
+ ALOGE("Could not get max threads after initial session setup: %s",
statusToString(status).c_str());
- return false;
+ return status;
}
if (status_t status = readId(); status != OK) {
- ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(),
+ ALOGE("Could not get session id after initial session setup: %s",
statusToString(status).c_str());
- return false;
- }
-
- // we've already setup one client
- for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
- // TODO(b/189955605): shutdown existing connections?
- if (!setupOneSocketConnection(addr, mId.value(), false /*reverse*/)) return false;
+ return status;
}
// TODO(b/189955605): we should add additional sessions dynamically
@@ -360,25 +449,38 @@
// requested to be set) in order to allow the other side to reliably make
// any requests at all.
- for (size_t i = 0; i < mMaxThreads; i++) {
- if (!setupOneSocketConnection(addr, mId.value(), true /*reverse*/)) return false;
+ // we've already setup one client
+ for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+ if (status_t status = connectAndInit(mId.value(), false /*incoming*/); status != OK)
+ return status;
}
- return true;
+ for (size_t i = 0; i < mMaxThreads; i++) {
+ if (status_t status = connectAndInit(mId.value(), true /*incoming*/); status != OK)
+ return status;
+ }
+
+ return OK;
}
-bool RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, const RpcAddress& id,
- bool reverse) {
+status_t RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+ return setupClient([&](const RpcAddress& sessionId, bool incoming) {
+ return setupOneSocketConnection(addr, sessionId, incoming);
+ });
+}
+
+status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
+ const RpcAddress& sessionId, bool incoming) {
for (size_t tries = 0; tries < 5; tries++) {
if (tries > 0) usleep(10000);
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ unique_fd serverFd(TEMP_FAILURE_RETRY(
+ socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
if (serverFd == -1) {
int savedErrno = errno;
ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
strerror(savedErrno));
- return false;
+ return -savedErrno;
}
if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
@@ -386,76 +488,131 @@
ALOGW("Connection reset on %s", addr.toString().c_str());
continue;
}
- int savedErrno = errno;
- ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
- strerror(savedErrno));
- return false;
+ if (errno != EAGAIN && errno != EINPROGRESS) {
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return -savedErrno;
+ }
+ // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or
+ // EINPROGRESS (for others). Call poll() and getsockopt() to get the error.
+ status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT);
+ if (pollStatus != OK) {
+ ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s",
+ statusToString(pollStatus).c_str());
+ return pollStatus;
+ }
+ int soError;
+ socklen_t soErrorLen = sizeof(soError);
+ int ret = getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &soError, &soErrorLen);
+ if (ret == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s",
+ strerror(savedErrno));
+ return -savedErrno;
+ }
+ if (soError != 0) {
+ ALOGE("After connect(), getsockopt() returns error for socket at %s: %s",
+ addr.toString().c_str(), strerror(soError));
+ return -soError;
+ }
}
-
- RpcConnectionHeader header{.options = 0};
- memcpy(&header.sessionId, &id.viewRawEmbedded(), sizeof(RpcWireAddress));
-
- if (reverse) header.options |= RPC_CONNECTION_OPTION_REVERSE;
-
- if (sizeof(header) != TEMP_FAILURE_RETRY(write(serverFd.get(), &header, sizeof(header)))) {
- int savedErrno = errno;
- ALOGE("Could not write connection header to socket at %s: %s", addr.toString().c_str(),
- strerror(savedErrno));
- return false;
- }
-
LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
- if (reverse) {
- std::mutex mutex;
- std::condition_variable joinCv;
- std::unique_lock<std::mutex> lock(mutex);
- std::thread thread;
- sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this);
- bool ownershipTransferred = false;
- thread = std::thread([&]() {
- std::unique_lock<std::mutex> threadLock(mutex);
- unique_fd fd = std::move(serverFd);
- // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
- sp<RpcSession> session = thiz;
- session->preJoinThreadOwnership(std::move(thread));
-
- // only continue once we have a response or the connection fails
- auto setupResult = session->preJoinSetup(std::move(fd));
-
- ownershipTransferred = true;
- threadLock.unlock();
- joinCv.notify_one();
- // do not use & vars below
-
- RpcSession::join(std::move(session), std::move(setupResult));
- });
- joinCv.wait(lock, [&] { return ownershipTransferred; });
- LOG_ALWAYS_FATAL_IF(!ownershipTransferred);
- return true;
- } else {
- return addOutgoingConnection(std::move(serverFd), true);
- }
+ return initAndAddConnection(std::move(serverFd), sessionId, incoming);
}
ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
- return false;
+ return UNKNOWN_ERROR;
}
-bool RpcSession::addOutgoingConnection(unique_fd fd, bool init) {
+status_t RpcSession::initAndAddConnection(unique_fd fd, const RpcAddress& sessionId,
+ bool incoming) {
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr);
+ auto ctx = mRpcTransportCtxFactory->newClientCtx();
+ if (ctx == nullptr) {
+ ALOGE("Unable to create client RpcTransportCtx with %s sockets",
+ mRpcTransportCtxFactory->toCString());
+ return NO_MEMORY;
+ }
+ auto server = ctx->newTransport(std::move(fd), mShutdownTrigger.get());
+ if (server == nullptr) {
+ ALOGE("Unable to set up RpcTransport in %s context", mRpcTransportCtxFactory->toCString());
+ return UNKNOWN_ERROR;
+ }
+
+ LOG_RPC_DETAIL("Socket at client with RpcTransport %p", server.get());
+
+ RpcConnectionHeader header{
+ .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
+ .options = 0,
+ };
+ memcpy(&header.sessionId, &sessionId.viewRawEmbedded(), sizeof(RpcWireAddress));
+
+ if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
+
+ auto sendHeaderStatus =
+ server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header));
+ if (sendHeaderStatus != OK) {
+ ALOGE("Could not write connection header to socket: %s",
+ statusToString(sendHeaderStatus).c_str());
+ return sendHeaderStatus;
+ }
+
+ LOG_RPC_DETAIL("Socket at client: header sent");
+
+ if (incoming) {
+ return addIncomingConnection(std::move(server));
+ } else {
+ return addOutgoingConnection(std::move(server), true /*init*/);
+ }
+}
+
+status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) {
+ std::mutex mutex;
+ std::condition_variable joinCv;
+ std::unique_lock<std::mutex> lock(mutex);
+ std::thread thread;
+ sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this);
+ bool ownershipTransferred = false;
+ thread = std::thread([&]() {
+ std::unique_lock<std::mutex> threadLock(mutex);
+ std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport);
+ // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
+ sp<RpcSession> session = thiz;
+ session->preJoinThreadOwnership(std::move(thread));
+
+ // only continue once we have a response or the connection fails
+ auto setupResult = session->preJoinSetup(std::move(movedRpcTransport));
+
+ ownershipTransferred = true;
+ threadLock.unlock();
+ joinCv.notify_one();
+ // do not use & vars below
+
+ RpcSession::join(std::move(session), std::move(setupResult));
+ });
+ joinCv.wait(lock, [&] { return ownershipTransferred; });
+ LOG_ALWAYS_FATAL_IF(!ownershipTransferred);
+ return OK;
+}
+
+status_t RpcSession::initShutdownTrigger() {
+ // first client connection added, but setForServer not called, so
+ // initializaing for a client.
+ if (mShutdownTrigger == nullptr) {
+ mShutdownTrigger = FdTrigger::make();
+ mEventListener = mShutdownListener = sp<WaitForShutdownListener>::make();
+ if (mShutdownTrigger == nullptr) return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) {
sp<RpcConnection> connection = sp<RpcConnection>::make();
{
std::lock_guard<std::mutex> _l(mMutex);
-
- // first client connection added, but setForServer not called, so
- // initializaing for a client.
- if (mShutdownTrigger == nullptr) {
- mShutdownTrigger = FdTrigger::make();
- mEventListener = mShutdownListener = sp<WaitForShutdownListener>::make();
- if (mShutdownTrigger == nullptr) return false;
- }
-
- connection->fd = std::move(fd);
+ connection->rpcTransport = std::move(rpcTransport);
connection->exclusiveTid = gettid();
mOutgoingConnections.push_back(connection);
}
@@ -470,7 +627,7 @@
connection->exclusiveTid = std::nullopt;
}
- return status == OK;
+ return status;
}
bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListener>& eventListener,
@@ -490,26 +647,44 @@
return true;
}
-sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(unique_fd fd) {
+sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(
+ std::unique_ptr<RpcTransport> rpcTransport) {
std::lock_guard<std::mutex> _l(mMutex);
+
+ if (mIncomingConnections.size() >= mMaxThreads) {
+ ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)",
+ mIncomingConnections.size(), mMaxThreads);
+ return nullptr;
+ }
+
+ // Don't accept any more connections, some have shutdown. Usually this
+ // happens when new connections are still being established as part of a
+ // very short-lived session which shuts down after it already started
+ // accepting new connections.
+ if (mIncomingConnections.size() < mMaxIncomingConnections) {
+ return nullptr;
+ }
+
sp<RpcConnection> session = sp<RpcConnection>::make();
- session->fd = std::move(fd);
+ session->rpcTransport = std::move(rpcTransport);
session->exclusiveTid = gettid();
+
mIncomingConnections.push_back(session);
+ mMaxIncomingConnections = mIncomingConnections.size();
return session;
}
bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) {
- std::lock_guard<std::mutex> _l(mMutex);
+ std::unique_lock<std::mutex> _l(mMutex);
if (auto it = std::find(mIncomingConnections.begin(), mIncomingConnections.end(), connection);
it != mIncomingConnections.end()) {
mIncomingConnections.erase(it);
if (mIncomingConnections.size() == 0) {
sp<EventListener> listener = mEventListener.promote();
if (listener) {
- listener->onSessionLockedAllIncomingThreadsEnded(
- sp<RpcSession>::fromExisting(this));
+ _l.unlock();
+ listener->onSessionAllIncomingThreadsEnded(sp<RpcSession>::fromExisting(this));
}
}
return true;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b5eaaa3..b58f1b3 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,6 +18,7 @@
#include "RpcState.h"
+#include <android-base/hex.h>
#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
@@ -34,11 +35,10 @@
using base::ScopeGuard;
-#ifdef RPC_FLAKE_PRONE
+#if RPC_FLAKE_PRONE
void rpcMaybeWaitToFlake() {
- static std::random_device r;
- static std::mutex m;
-
+ [[clang::no_destroy]] static std::random_device r;
+ [[clang::no_destroy]] static std::mutex m;
unsigned num;
{
std::lock_guard<std::mutex> lock(m);
@@ -273,10 +273,8 @@
status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what, const void* data,
size_t size) {
- LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, connection->fd.get(),
- hexString(data, size).c_str());
-
- MAYBE_WAIT_IN_FLAKE_MODE;
+ LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ android::base::HexString(data, size).c_str());
if (size > std::numeric_limits<ssize_t>::max()) {
ALOGE("Cannot send %s at size %zu (too big)", what, size);
@@ -284,15 +282,14 @@
return BAD_VALUE;
}
- ssize_t sent = TEMP_FAILURE_RETRY(send(connection->fd.get(), data, size, MSG_NOSIGNAL));
-
- if (sent < 0 || sent != static_cast<ssize_t>(size)) {
- int savedErrno = errno;
- LOG_RPC_DETAIL("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent,
- size, connection->fd.get(), strerror(savedErrno));
-
+ if (status_t status =
+ connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
+ data, size);
+ status != OK) {
+ LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+ connection->rpcTransport.get(), statusToString(status).c_str());
(void)session->shutdownAndWait(false);
- return -savedErrno;
+ return status;
}
return OK;
@@ -308,15 +305,28 @@
}
if (status_t status =
- session->mShutdownTrigger->interruptableReadFully(connection->fd.get(), data, size);
+ connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
+ data, size);
status != OK) {
- LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on fd %d, error: %s", what, size,
- connection->fd.get(), statusToString(status).c_str());
+ LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+ connection->rpcTransport.get(), statusToString(status).c_str());
return status;
}
- LOG_RPC_DETAIL("Received %s on fd %d: %s", what, connection->fd.get(),
- hexString(data, size).c_str());
+ LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ android::base::HexString(data, size).c_str());
+ 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;
}
@@ -482,7 +492,8 @@
return status;
if (flags & IBinder::FLAG_ONEWAY) {
- LOG_RPC_DETAIL("Oneway command, so no longer waiting on %d", connection->fd.get());
+ LOG_RPC_DETAIL("Oneway command, so no longer waiting on RpcTransport %p",
+ connection->rpcTransport.get());
// Do not wait on result.
// However, too many oneway calls may cause refcounts to build up and fill up the socket,
@@ -577,7 +588,7 @@
status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
- LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", connection->fd.get());
+ LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
RpcWireHeader command;
if (status_t status = rpcRec(connection, session, "command header", &command, sizeof(command));
@@ -590,8 +601,7 @@
status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
uint8_t buf;
- while (0 < TEMP_FAILURE_RETRY(
- recv(connection->fd.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT))) {
+ while (connection->rpcTransport->peek(&buf, sizeof(buf)).value_or(0) > 0) {
status_t status = getAndExecuteCommand(connection, session, type);
if (status != OK) return status;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 8201eba..1446eec 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -44,7 +44,7 @@
#define RPC_FLAKE_PRONE false
-#ifdef RPC_FLAKE_PRONE
+#if RPC_FLAKE_PRONE
void rpcMaybeWaitToFlake();
#define MAYBE_WAIT_IN_FLAKE_MODE rpcMaybeWaitToFlake()
#else
@@ -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/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
new file mode 100644
index 0000000..d77fc52
--- /dev/null
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RpcRawTransport"
+#include <log/log.h>
+
+#include <poll.h>
+
+#include <binder/RpcTransportRaw.h>
+
+#include "FdTrigger.h"
+#include "RpcState.h"
+
+using android::base::ErrnoError;
+using android::base::Result;
+
+namespace android {
+
+namespace {
+
+// RpcTransport with TLS disabled.
+class RpcTransportRaw : public RpcTransport {
+public:
+ explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
+ Result<size_t> send(const void* buf, size_t size) {
+ ssize_t ret = TEMP_FAILURE_RETRY(::send(mSocket.get(), buf, size, MSG_NOSIGNAL));
+ if (ret < 0) {
+ return ErrnoError() << "send()";
+ }
+ return ret;
+ }
+ Result<size_t> recv(void* buf, size_t size) {
+ ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_NOSIGNAL));
+ if (ret < 0) {
+ return ErrnoError() << "recv()";
+ }
+ return ret;
+ }
+ Result<size_t> peek(void *buf, size_t size) override {
+ ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
+ if (ret < 0) {
+ return ErrnoError() << "recv(MSG_PEEK)";
+ }
+ return ret;
+ }
+
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size) override {
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data);
+ const uint8_t* end = buffer + size;
+
+ MAYBE_WAIT_IN_FLAKE_MODE;
+
+ status_t status;
+ while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLOUT)) == OK) {
+ auto writeSize = this->send(buffer, end - buffer);
+ if (!writeSize.ok()) {
+ LOG_RPC_DETAIL("RpcTransport::send(): %s", writeSize.error().message().c_str());
+ return writeSize.error().code() == 0 ? UNKNOWN_ERROR : -writeSize.error().code();
+ }
+
+ if (*writeSize == 0) return DEAD_OBJECT;
+
+ buffer += *writeSize;
+ if (buffer == end) return OK;
+ }
+ return status;
+ }
+
+ status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) override {
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
+ uint8_t* end = buffer + size;
+
+ MAYBE_WAIT_IN_FLAKE_MODE;
+
+ status_t status;
+ while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLIN)) == OK) {
+ auto readSize = this->recv(buffer, end - buffer);
+ if (!readSize.ok()) {
+ LOG_RPC_DETAIL("RpcTransport::recv(): %s", readSize.error().message().c_str());
+ return readSize.error().code() == 0 ? UNKNOWN_ERROR : -readSize.error().code();
+ }
+
+ if (*readSize == 0) return DEAD_OBJECT; // EOF
+
+ buffer += *readSize;
+ if (buffer == end) return OK;
+ }
+ return status;
+ }
+
+private:
+ android::base::unique_fd mSocket;
+};
+
+// RpcTransportCtx with TLS disabled.
+class RpcTransportCtxRaw : public RpcTransportCtx {
+public:
+ std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const {
+ return std::make_unique<RpcTransportRaw>(std::move(fd));
+ }
+};
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newServerCtx() const {
+ return std::make_unique<RpcTransportCtxRaw>();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newClientCtx() const {
+ return std::make_unique<RpcTransportCtxRaw>();
+}
+
+const char *RpcTransportCtxFactoryRaw::toCString() const {
+ return "raw";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryRaw::make() {
+ return std::unique_ptr<RpcTransportCtxFactoryRaw>(new RpcTransportCtxFactoryRaw());
+}
+
+} // namespace android
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
new file mode 100644
index 0000000..a102913
--- /dev/null
+++ b/libs/binder/RpcTransportTls.cpp
@@ -0,0 +1,535 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RpcTransportTls"
+#include <log/log.h>
+
+#include <poll.h>
+
+#include <openssl/bn.h>
+#include <openssl/ssl.h>
+
+#include <binder/RpcTransportTls.h>
+
+#include "FdTrigger.h"
+#include "RpcState.h"
+
+#define SHOULD_LOG_TLS_DETAIL false
+
+#if SHOULD_LOG_TLS_DETAIL
+#define LOG_TLS_DETAIL(...) ALOGI(__VA_ARGS__)
+#else
+#define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
+#endif
+
+#define TEST_AND_RETURN(value, expr) \
+ do { \
+ if (!(expr)) { \
+ ALOGE("Failed to call: %s", #expr); \
+ return value; \
+ } \
+ } while (0)
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+
+namespace android {
+namespace {
+
+constexpr const int kCertValidDays = 30;
+
+// Implement BIO for socket that ignores SIGPIPE.
+int socketNew(BIO* bio) {
+ BIO_set_data(bio, reinterpret_cast<void*>(-1));
+ BIO_set_init(bio, 0);
+ return 1;
+}
+int socketFree(BIO* bio) {
+ LOG_ALWAYS_FATAL_IF(bio == nullptr);
+ return 1;
+}
+int socketRead(BIO* bio, char* buf, int size) {
+ android::base::borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
+ int ret = TEMP_FAILURE_RETRY(::recv(fd.get(), buf, size, MSG_NOSIGNAL));
+ BIO_clear_retry_flags(bio);
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ BIO_set_retry_read(bio);
+ }
+ return ret;
+}
+
+int socketWrite(BIO* bio, const char* buf, int size) {
+ android::base::borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
+ int ret = TEMP_FAILURE_RETRY(::send(fd.get(), buf, size, MSG_NOSIGNAL));
+ BIO_clear_retry_flags(bio);
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ BIO_set_retry_write(bio);
+ }
+ return ret;
+}
+
+long socketCtrl(BIO* bio, int cmd, long num, void*) { // NOLINT
+ android::base::borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
+ if (cmd == BIO_CTRL_FLUSH) return 1;
+ LOG_ALWAYS_FATAL("sockCtrl(fd=%d, %d, %ld)", fd.get(), cmd, num);
+ return 0;
+}
+
+bssl::UniquePtr<BIO> newSocketBio(android::base::borrowed_fd fd) {
+ static const BIO_METHOD* gMethods = ([] {
+ auto methods = BIO_meth_new(BIO_get_new_index(), "socket_no_signal");
+ LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_write(methods, socketWrite), "BIO_meth_set_write");
+ LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_read(methods, socketRead), "BIO_meth_set_read");
+ LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_ctrl(methods, socketCtrl), "BIO_meth_set_ctrl");
+ LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_create(methods, socketNew), "BIO_meth_set_create");
+ LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_destroy(methods, socketFree), "BIO_meth_set_destroy");
+ return methods;
+ })();
+ bssl::UniquePtr<BIO> ret(BIO_new(gMethods));
+ if (ret == nullptr) return nullptr;
+ BIO_set_data(ret.get(), reinterpret_cast<void*>(fd.get()));
+ BIO_set_init(ret.get(), 1);
+ return ret;
+}
+
+bssl::UniquePtr<EVP_PKEY> makeKeyPairForSelfSignedCert() {
+ bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (ec_key == nullptr || !EC_KEY_generate_key(ec_key.get())) {
+ ALOGE("Failed to generate key pair.");
+ return nullptr;
+ }
+ bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
+ // Use set1 instead of assign to avoid leaking ec_key when assign fails. set1 increments
+ // the refcount of the ec_key, so it is okay to release it at the end of this function.
+ if (evp_pkey == nullptr || !EVP_PKEY_set1_EC_KEY(evp_pkey.get(), ec_key.get())) {
+ ALOGE("Failed to assign key pair.");
+ return nullptr;
+ }
+ return evp_pkey;
+}
+
+bssl::UniquePtr<X509> makeSelfSignedCert(EVP_PKEY* evp_pkey, const int valid_days) {
+ bssl::UniquePtr<X509> x509(X509_new());
+ bssl::UniquePtr<BIGNUM> serial(BN_new());
+ bssl::UniquePtr<BIGNUM> serialLimit(BN_new());
+ TEST_AND_RETURN(nullptr, BN_lshift(serialLimit.get(), BN_value_one(), 128));
+ TEST_AND_RETURN(nullptr, BN_rand_range(serial.get(), serialLimit.get()));
+ TEST_AND_RETURN(nullptr, BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get())));
+ TEST_AND_RETURN(nullptr, X509_gmtime_adj(X509_getm_notBefore(x509.get()), 0));
+ TEST_AND_RETURN(nullptr,
+ X509_gmtime_adj(X509_getm_notAfter(x509.get()), 60 * 60 * 24 * valid_days));
+
+ X509_NAME* subject = X509_get_subject_name(x509.get());
+ TEST_AND_RETURN(nullptr,
+ X509_NAME_add_entry_by_txt(subject, "O", MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>("Android"), -1, -1,
+ 0));
+ TEST_AND_RETURN(nullptr,
+ X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>("BinderRPC"), -1,
+ -1, 0));
+ TEST_AND_RETURN(nullptr, X509_set_issuer_name(x509.get(), subject));
+
+ TEST_AND_RETURN(nullptr, X509_set_pubkey(x509.get(), evp_pkey));
+ TEST_AND_RETURN(nullptr, X509_sign(x509.get(), evp_pkey, EVP_sha256()));
+ return x509;
+}
+
+[[maybe_unused]] void sslDebugLog(const SSL* ssl, int type, int value) {
+ switch (type) {
+ case SSL_CB_HANDSHAKE_START:
+ LOG_TLS_DETAIL("Handshake started.");
+ break;
+ case SSL_CB_HANDSHAKE_DONE:
+ LOG_TLS_DETAIL("Handshake done.");
+ break;
+ case SSL_CB_ACCEPT_LOOP:
+ LOG_TLS_DETAIL("Handshake progress: %s", SSL_state_string_long(ssl));
+ break;
+ default:
+ LOG_TLS_DETAIL("SSL Debug Log: type = %d, value = %d", type, value);
+ break;
+ }
+}
+
+// Handles libssl's error queue.
+//
+// Call into any of its member functions to ensure the error queue is properly handled or cleared.
+// If the error queue is not handled or cleared, the destructor will abort.
+class ErrorQueue {
+public:
+ ~ErrorQueue() { LOG_ALWAYS_FATAL_IF(!mHandled); }
+
+ // Clear the error queue.
+ void clear() {
+ ERR_clear_error();
+ mHandled = true;
+ }
+
+ // Stores the error queue in |ssl| into a string, then clears the error queue.
+ std::string toString() {
+ std::stringstream ss;
+ ERR_print_errors_cb(
+ [](const char* str, size_t len, void* ctx) {
+ auto ss = (std::stringstream*)ctx;
+ (*ss) << std::string_view(str, len) << "\n";
+ return 1; // continue
+ },
+ &ss);
+ // Though ERR_print_errors_cb should have cleared it, it is okay to clear again.
+ clear();
+ return ss.str();
+ }
+
+ // |sslError| should be from Ssl::getError().
+ // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
+ // return error. Also return error if |fdTrigger| is triggered before or during poll().
+ status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
+ const char* fnString, int additionalEvent = 0) {
+ switch (sslError) {
+ case SSL_ERROR_WANT_READ:
+ return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString);
+ case SSL_ERROR_WANT_WRITE:
+ return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString);
+ case SSL_ERROR_SYSCALL: {
+ auto queue = toString();
+ LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString,
+ SSL_error_description(sslError), queue.c_str());
+ return DEAD_OBJECT;
+ }
+ default: {
+ auto queue = toString();
+ ALOGE("%s(): %s. Error queue: %s", fnString, SSL_error_description(sslError),
+ queue.c_str());
+ return UNKNOWN_ERROR;
+ }
+ }
+ }
+
+private:
+ bool mHandled = false;
+
+ status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger,
+ const char* fnString) {
+ status_t ret = fdTrigger->triggerablePoll(fd, event);
+ if (ret != OK && ret != DEAD_OBJECT && ret != -ECANCELED) {
+ ALOGE("triggerablePoll error while poll()-ing after %s(): %s", fnString,
+ statusToString(ret).c_str());
+ }
+ clear();
+ return ret;
+ }
+};
+
+// Helper to call a function, with its return value instantiable.
+template <typename Fn, typename... Args>
+struct FuncCaller {
+ struct Monostate {};
+ static constexpr bool sIsVoid = std::is_void_v<std::invoke_result_t<Fn, Args...>>;
+ using Result = std::conditional_t<sIsVoid, Monostate, std::invoke_result_t<Fn, Args...>>;
+ static inline Result call(Fn fn, Args&&... args) {
+ if constexpr (std::is_void_v<std::invoke_result_t<Fn, Args...>>) {
+ std::invoke(fn, std::forward<Args>(args)...);
+ return {};
+ } else {
+ return std::invoke(fn, std::forward<Args>(args)...);
+ }
+ }
+};
+
+// Helper to Ssl::call(). Returns the result to the SSL_* function as well as an ErrorQueue object.
+template <typename Fn, typename... Args>
+struct SslCaller {
+ using RawCaller = FuncCaller<Fn, SSL*, Args...>;
+ struct ResultAndErrorQueue {
+ typename RawCaller::Result result;
+ ErrorQueue errorQueue;
+ };
+ static inline ResultAndErrorQueue call(Fn fn, SSL* ssl, Args&&... args) {
+ LOG_ALWAYS_FATAL_IF(ssl == nullptr);
+ auto result = RawCaller::call(fn, std::forward<SSL*>(ssl), std::forward<Args>(args)...);
+ return ResultAndErrorQueue{std::move(result), ErrorQueue()};
+ }
+};
+
+// A wrapper over bssl::UniquePtr<SSL>. This class ensures that all SSL_* functions are called
+// through call(), which returns an ErrorQueue object that requires the caller to either handle
+// or clear it.
+// Example:
+// auto [ret, errorQueue] = ssl.call(SSL_read, buf, size);
+// if (ret >= 0) errorQueue.clear();
+// else ALOGE("%s", errorQueue.toString().c_str());
+class Ssl {
+public:
+ explicit Ssl(bssl::UniquePtr<SSL> ssl) : mSsl(std::move(ssl)) {
+ LOG_ALWAYS_FATAL_IF(mSsl == nullptr);
+ }
+
+ template <typename Fn, typename... Args>
+ inline typename SslCaller<Fn, Args...>::ResultAndErrorQueue call(Fn fn, Args&&... args) {
+ return SslCaller<Fn, Args...>::call(fn, mSsl.get(), std::forward<Args>(args)...);
+ }
+
+ int getError(int ret) {
+ LOG_ALWAYS_FATAL_IF(mSsl == nullptr);
+ return SSL_get_error(mSsl.get(), ret);
+ }
+
+private:
+ bssl::UniquePtr<SSL> mSsl;
+};
+
+class RpcTransportTls : public RpcTransport {
+public:
+ RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
+ : mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
+ Result<size_t> peek(void* buf, size_t size) override;
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size) override;
+ status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) override;
+
+private:
+ android::base::unique_fd mSocket;
+ Ssl mSsl;
+
+ static status_t isTriggered(FdTrigger* fdTrigger);
+};
+
+// Error code is errno.
+Result<size_t> RpcTransportTls::peek(void* buf, size_t size) {
+ size_t todo = std::min<size_t>(size, std::numeric_limits<int>::max());
+ auto [ret, errorQueue] = mSsl.call(SSL_peek, buf, static_cast<int>(todo));
+ if (ret < 0) {
+ int err = mSsl.getError(ret);
+ if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+ // Seen EAGAIN / EWOULDBLOCK on recv(2) / send(2).
+ // Like RpcTransportRaw::peek(), don't handle it here.
+ return Error(EWOULDBLOCK) << "SSL_peek(): " << errorQueue.toString();
+ }
+ return Error() << "SSL_peek(): " << errorQueue.toString();
+ }
+ errorQueue.clear();
+ LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret);
+ return ret;
+}
+
+status_t RpcTransportTls::isTriggered(FdTrigger* fdTrigger) {
+ auto ret = fdTrigger->isTriggeredPolled();
+ if (!ret.ok()) {
+ ALOGE("%s: %s", __PRETTY_FUNCTION__, ret.error().message().c_str());
+ return ret.error().code() == 0 ? UNKNOWN_ERROR : -ret.error().code();
+ }
+ return OK;
+}
+
+status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
+ size_t size) {
+ auto buffer = reinterpret_cast<const uint8_t*>(data);
+ const uint8_t* end = buffer + size;
+
+ MAYBE_WAIT_IN_FLAKE_MODE;
+
+ // Before doing any I/O, check trigger once. This ensures the trigger is checked at least
+ // once. The trigger is also checked via triggerablePoll() after every SSL_write().
+ if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+
+ while (buffer < end) {
+ size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
+ auto [writeSize, errorQueue] = mSsl.call(SSL_write, buffer, todo);
+ if (writeSize > 0) {
+ buffer += writeSize;
+ errorQueue.clear();
+ continue;
+ }
+ // SSL_write() should never return 0 unless BIO_write were to return 0.
+ int sslError = mSsl.getError(writeSize);
+ // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
+ // triggerablePoll()-ed. Then additionalEvent is no longer necessary.
+ status_t pollStatus =
+ errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, "SSL_write", POLLIN);
+ if (pollStatus != OK) return pollStatus;
+ // Do not advance buffer. Try SSL_write() again.
+ }
+ LOG_TLS_DETAIL("TLS: Sent %zu bytes!", size);
+ return OK;
+}
+
+status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) {
+ auto buffer = reinterpret_cast<uint8_t*>(data);
+ uint8_t* end = buffer + size;
+
+ MAYBE_WAIT_IN_FLAKE_MODE;
+
+ // Before doing any I/O, check trigger once. This ensures the trigger is checked at least
+ // once. The trigger is also checked via triggerablePoll() after every SSL_write().
+ if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+
+ while (buffer < end) {
+ size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
+ auto [readSize, errorQueue] = mSsl.call(SSL_read, buffer, todo);
+ if (readSize > 0) {
+ buffer += readSize;
+ errorQueue.clear();
+ continue;
+ }
+ if (readSize == 0) {
+ // SSL_read() only returns 0 on EOF.
+ errorQueue.clear();
+ return DEAD_OBJECT;
+ }
+ int sslError = mSsl.getError(readSize);
+ status_t pollStatus =
+ errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, "SSL_read");
+ if (pollStatus != OK) return pollStatus;
+ // Do not advance buffer. Try SSL_read() again.
+ }
+ LOG_TLS_DETAIL("TLS: Received %zu bytes!", size);
+ return OK;
+}
+
+// For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|.
+bool setFdAndDoHandshake(Ssl* ssl, android::base::borrowed_fd fd, FdTrigger* fdTrigger) {
+ bssl::UniquePtr<BIO> bio = newSocketBio(fd);
+ TEST_AND_RETURN(false, bio != nullptr);
+ auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get());
+ (void)bio.release(); // SSL_set_bio takes ownership.
+ errorQueue.clear();
+
+ MAYBE_WAIT_IN_FLAKE_MODE;
+
+ while (true) {
+ auto [ret, errorQueue] = ssl->call(SSL_do_handshake);
+ if (ret > 0) {
+ errorQueue.clear();
+ return true;
+ }
+ if (ret == 0) {
+ // SSL_do_handshake() only returns 0 on EOF.
+ ALOGE("SSL_do_handshake(): EOF: %s", errorQueue.toString().c_str());
+ return false;
+ }
+ int sslError = ssl->getError(ret);
+ status_t pollStatus =
+ errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake");
+ if (pollStatus != OK) return false;
+ }
+}
+
+class RpcTransportCtxTlsServer : public RpcTransportCtx {
+public:
+ static std::unique_ptr<RpcTransportCtxTlsServer> create();
+ std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd acceptedFd,
+ FdTrigger* fdTrigger) const override;
+
+private:
+ bssl::UniquePtr<SSL_CTX> mCtx;
+};
+
+std::unique_ptr<RpcTransportCtxTlsServer> RpcTransportCtxTlsServer::create() {
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+ TEST_AND_RETURN(nullptr, ctx != nullptr);
+
+ // Server use self-signing cert
+ auto evp_pkey = makeKeyPairForSelfSignedCert();
+ TEST_AND_RETURN(nullptr, evp_pkey != nullptr);
+ auto cert = makeSelfSignedCert(evp_pkey.get(), kCertValidDays);
+ TEST_AND_RETURN(nullptr, cert != nullptr);
+ TEST_AND_RETURN(nullptr, SSL_CTX_use_PrivateKey(ctx.get(), evp_pkey.get()));
+ TEST_AND_RETURN(nullptr, SSL_CTX_use_certificate(ctx.get(), cert.get()));
+ // Require at least TLS 1.3
+ TEST_AND_RETURN(nullptr, SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION));
+
+ if constexpr (SHOULD_LOG_TLS_DETAIL) { // NOLINT
+ SSL_CTX_set_info_callback(ctx.get(), sslDebugLog);
+ }
+
+ auto rpcTransportTlsServerCtx = std::make_unique<RpcTransportCtxTlsServer>();
+ rpcTransportTlsServerCtx->mCtx = std::move(ctx);
+ return rpcTransportTlsServerCtx;
+}
+
+std::unique_ptr<RpcTransport> RpcTransportCtxTlsServer::newTransport(
+ android::base::unique_fd acceptedFd, FdTrigger* fdTrigger) const {
+ bssl::UniquePtr<SSL> ssl(SSL_new(mCtx.get()));
+ TEST_AND_RETURN(nullptr, ssl != nullptr);
+ Ssl wrapped(std::move(ssl));
+
+ wrapped.call(SSL_set_accept_state).errorQueue.clear();
+ TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, acceptedFd, fdTrigger));
+ return std::make_unique<RpcTransportTls>(std::move(acceptedFd), std::move(wrapped));
+}
+
+class RpcTransportCtxTlsClient : public RpcTransportCtx {
+public:
+ static std::unique_ptr<RpcTransportCtxTlsClient> create();
+ std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd connectedFd,
+ FdTrigger* fdTrigger) const override;
+
+private:
+ bssl::UniquePtr<SSL_CTX> mCtx;
+};
+
+std::unique_ptr<RpcTransportCtxTlsClient> RpcTransportCtxTlsClient::create() {
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+ TEST_AND_RETURN(nullptr, ctx != nullptr);
+
+ // TODO(b/195166979): server should send certificate in a different channel, and client
+ // should verify it here.
+ SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER,
+ [](SSL*, uint8_t*) -> ssl_verify_result_t { return ssl_verify_ok; });
+
+ // Require at least TLS 1.3
+ TEST_AND_RETURN(nullptr, SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION));
+
+ if constexpr (SHOULD_LOG_TLS_DETAIL) { // NOLINT
+ SSL_CTX_set_info_callback(ctx.get(), sslDebugLog);
+ }
+
+ auto rpcTransportTlsClientCtx = std::make_unique<RpcTransportCtxTlsClient>();
+ rpcTransportTlsClientCtx->mCtx = std::move(ctx);
+ return rpcTransportTlsClientCtx;
+}
+
+std::unique_ptr<RpcTransport> RpcTransportCtxTlsClient::newTransport(
+ android::base::unique_fd connectedFd, FdTrigger* fdTrigger) const {
+ bssl::UniquePtr<SSL> ssl(SSL_new(mCtx.get()));
+ TEST_AND_RETURN(nullptr, ssl != nullptr);
+ Ssl wrapped(std::move(ssl));
+
+ wrapped.call(SSL_set_connect_state).errorQueue.clear();
+ TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, connectedFd, fdTrigger));
+ return std::make_unique<RpcTransportTls>(std::move(connectedFd), std::move(wrapped));
+}
+
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const {
+ return android::RpcTransportCtxTlsServer::create();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newClientCtx() const {
+ return android::RpcTransportCtxTlsClient::create();
+}
+
+const char* RpcTransportCtxFactoryTls::toCString() const {
+ return "tls";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTls::make() {
+ return std::unique_ptr<RpcTransportCtxFactoryTls>(new RpcTransportCtxFactoryTls());
+}
+
+} // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 2016483..0f8efd2 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -21,7 +21,7 @@
#pragma clang diagnostic error "-Wpadded"
enum : uint8_t {
- RPC_CONNECTION_OPTION_REVERSE = 0x1,
+ RPC_CONNECTION_OPTION_INCOMING = 0x1, // default is outgoing
};
constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_CREATED = 1 << 0; // distinguish from '0' address
@@ -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"
@@ -47,7 +58,7 @@
/**
* Whenever a client connection is setup, this is sent as the initial
* transaction. The main use of this is in order to control the timing for when
- * a reverse connection is setup.
+ * an incoming connection is setup.
*/
struct RpcOutgoingConnectionInit {
char msg[4];
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 07f5778..59334b7 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -158,8 +158,10 @@
LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
auto rpcSession = RpcSession::make();
- if (!rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort())) {
- ALOGE("Unable to set up inet client on host port %u", *forwardResult->hostPort());
+ if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
+ status != OK) {
+ ALOGE("Unable to set up inet client on host port %u: %s", *forwardResult->hostPort(),
+ statusToString(status).c_str());
return nullptr;
}
auto binder = rpcSession->getRootObject();
@@ -167,9 +169,12 @@
ALOGE("RpcSession::getRootObject returns nullptr");
return nullptr;
}
- binder->attachObject(kDeviceServiceExtraId,
- static_cast<void*>(new CommandResult(std::move(*result))), nullptr,
- &cleanupCommandResult);
+
+ LOG_ALWAYS_FATAL_IF(
+ nullptr !=
+ binder->attachObject(kDeviceServiceExtraId,
+ static_cast<void*>(new CommandResult(std::move(*result))), nullptr,
+ &cleanupCommandResult));
return binder;
}
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index dac3819..d25c038 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -23,20 +23,6 @@
namespace android {
namespace internal {
-// the libbinder parcel format is currently unstable
-
-// oldest version which is supported
-constexpr uint8_t kBinderWireFormatOldest = 1;
-// current version
-constexpr uint8_t kBinderWireFormatVersion = 1;
-
-Stability::Category Stability::Category::currentFromLevel(Level level) {
- return {
- .version = kBinderWireFormatVersion,
- .level = level,
- };
-}
-
void Stability::forceDowngradeToStability(const sp<IBinder>& binder, Level level) {
// Downgrading a remote binder would require also copying the version from
// the binder sent here. In practice though, we don't need to downgrade the
@@ -44,8 +30,7 @@
// what we can do to it.
LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder");
- auto stability = Category::currentFromLevel(level);
- status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
+ status_t result = setRepr(binder.get(), level, REPR_LOG | REPR_ALLOW_DOWNGRADE);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
@@ -61,41 +46,31 @@
forceDowngradeToStability(binder, Level::VENDOR);
}
-std::string Stability::Category::debugString() {
- return levelString(level) + " wire protocol version "
- + std::to_string(version);
-}
-
void Stability::markCompilationUnit(IBinder* binder) {
- auto stability = Category::currentFromLevel(getLocalLevel());
- status_t result = setRepr(binder, stability.repr(), REPR_LOG);
+ status_t result = setRepr(binder, getLocalLevel(), REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
void Stability::markVintf(IBinder* binder) {
- auto stability = Category::currentFromLevel(Level::VINTF);
- status_t result = setRepr(binder, stability.repr(), REPR_LOG);
+ status_t result = setRepr(binder, Level::VINTF, REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
std::string Stability::debugToString(const sp<IBinder>& binder) {
- auto stability = getCategory(binder.get());
- return stability.debugString();
+ return levelString(getRepr(binder.get()));
}
void Stability::markVndk(IBinder* binder) {
- auto stability = Category::currentFromLevel(Level::VENDOR);
- status_t result = setRepr(binder, stability.repr(), REPR_LOG);
+ status_t result = setRepr(binder, Level::VENDOR, REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) {
- return check(getCategory(binder.get()), Level::VINTF);
+ return check(getRepr(binder.get()), Level::VINTF);
}
void Stability::tryMarkCompilationUnit(IBinder* binder) {
- auto stability = Category::currentFromLevel(getLocalLevel());
- (void) setRepr(binder, stability.repr(), REPR_NONE);
+ (void)setRepr(binder, getLocalLevel(), REPR_NONE);
}
Stability::Level Stability::getLocalLevel() {
@@ -117,92 +92,77 @@
#endif
}
-status_t Stability::setRepr(IBinder* binder, int32_t representation, uint32_t flags) {
+status_t Stability::setRepr(IBinder* binder, int32_t setting, uint32_t flags) {
bool log = flags & REPR_LOG;
bool allowDowngrade = flags & REPR_ALLOW_DOWNGRADE;
- auto current = getCategory(binder);
- auto setting = Category::fromRepr(representation);
-
- // If we have ahold of a binder with a newer declared version, then it
- // should support older versions, and we will simply write our parcels with
- // the current wire parcel format.
- if (setting.version < kBinderWireFormatOldest) {
- // always log, because this shouldn't happen
- ALOGE("Cannot accept binder with older binder wire protocol version "
- "%u. Versions less than %u are unsupported.", setting.version,
- kBinderWireFormatOldest);
- return BAD_TYPE;
- }
+ int16_t current = getRepr(binder);
// null binder is always written w/ 'UNDECLARED' stability
if (binder == nullptr) {
- if (setting.level == UNDECLARED) {
+ if (setting == UNDECLARED) {
return OK;
} else {
if (log) {
- ALOGE("Null binder written with stability %s.",
- levelString(setting.level).c_str());
+ ALOGE("Null binder written with stability %s.", levelString(setting).c_str());
}
return BAD_TYPE;
}
}
- if (!isDeclaredLevel(setting.level)) {
+ if (!isDeclaredLevel(setting)) {
if (log) {
- ALOGE("Can only set known stability, not %u.", setting.level);
+ ALOGE("Can only set known stability, not %d.", setting);
}
return BAD_TYPE;
}
+ Level levelSetting = static_cast<Level>(setting);
if (current == setting) return OK;
- bool hasAlreadyBeenSet = current.repr() != 0;
- bool isAllowedDowngrade = allowDowngrade && check(current, setting.level);
+ bool hasAlreadyBeenSet = current != Level::UNDECLARED;
+ bool isAllowedDowngrade = allowDowngrade && check(current, levelSetting);
if (hasAlreadyBeenSet && !isAllowedDowngrade) {
if (log) {
ALOGE("Interface being set with %s but it is already marked as %s",
- setting.debugString().c_str(),
- current.debugString().c_str());
+ levelString(setting).c_str(), levelString(current).c_str());
}
return BAD_TYPE;
}
if (isAllowedDowngrade) {
- ALOGI("Interface set with %s downgraded to %s stability",
- current.debugString().c_str(),
- setting.debugString().c_str());
+ ALOGI("Interface set with %s downgraded to %s stability", levelString(current).c_str(),
+ levelString(setting).c_str());
}
BBinder* local = binder->localBinder();
if (local != nullptr) {
- local->mStability = setting.repr();
+ local->mStability = setting;
} else {
- binder->remoteBinder()->mStability = setting.repr();
+ binder->remoteBinder()->mStability = setting;
}
return OK;
}
-Stability::Category Stability::getCategory(IBinder* binder) {
+int16_t Stability::getRepr(IBinder* binder) {
if (binder == nullptr) {
- return Category::currentFromLevel(Level::UNDECLARED);
+ return Level::UNDECLARED;
}
BBinder* local = binder->localBinder();
if (local != nullptr) {
- return Category::fromRepr(local->mStability);
+ return local->mStability;
}
- return Category::fromRepr(binder->remoteBinder()->mStability);
+ return binder->remoteBinder()->mStability;
}
-bool Stability::check(Category provided, Level required) {
- bool stable = (provided.level & required) == required;
+bool Stability::check(int16_t provided, Level required) {
+ bool stable = (provided & required) == required;
- if (provided.level != UNDECLARED && !isDeclaredLevel(provided.level)) {
- ALOGE("Unknown stability when checking interface stability %d.",
- provided.level);
+ if (provided != UNDECLARED && !isDeclaredLevel(provided)) {
+ ALOGE("Unknown stability when checking interface stability %d.", provided);
stable = false;
}
@@ -210,11 +170,11 @@
return stable;
}
-bool Stability::isDeclaredLevel(Level stability) {
+bool Stability::isDeclaredLevel(int32_t stability) {
return stability == VENDOR || stability == SYSTEM || stability == VINTF;
}
-std::string Stability::levelString(Level level) {
+std::string Stability::levelString(int32_t level) {
switch (level) {
case Level::UNDECLARED: return "undeclared stability";
case Level::VENDOR: return "vendor stability";
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index b5a078c..a44c578 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -130,13 +130,13 @@
}
// The remote threw an exception. Get the message back.
- String16 message;
+ std::optional<String16> message;
status = parcel.readString16(&message);
if (status != OK) {
setFromStatusT(status);
return status;
}
- mMessage = String8(message);
+ mMessage = String8(message.value_or(String16()));
// Skip over the remote stack trace data
int32_t remote_stack_trace_header_size;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 748fa72..9c5ce67 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,6 +16,9 @@
"name": "binderDriverInterfaceTest"
},
{
+ "name": "binderHostDeviceTest"
+ },
+ {
"name": "binderTextOutputTest"
},
{
@@ -55,6 +58,9 @@
"name": "CtsOsTestCases",
"options": [
{
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ },
+ {
"exclude-filter": "android.os.cts.BuildTest#testSdkInt"
},
{
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index 90a4502..d2a5be1 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -18,10 +18,24 @@
#include <string.h>
+using android::base::ErrnoError;
+using android::base::Result;
+
namespace android {
void zeroMemory(uint8_t* data, size_t size) {
memset(data, 0, size);
}
-} // namespace android
+Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+ int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
+ if (flags == -1) {
+ return ErrnoError() << "Could not get flags for fd";
+ }
+ if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) {
+ return ErrnoError() << "Could not set non-blocking flag for fd";
+ }
+ return {};
+}
+
+} // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index f94b158..1e383da 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -17,9 +17,14 @@
#include <cstdint>
#include <stddef.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
namespace android {
// avoid optimizations
void zeroMemory(uint8_t* data, size_t size);
+android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+
} // namespace android
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index e524dab..d121ce2 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -111,15 +111,15 @@
errWrite.reset();
ret.pid = pid;
- auto handlePoll = [](android::base::unique_fd* fd, const pollfd& pfd, std::string* s) {
+ auto handlePoll = [](android::base::unique_fd* fd, const pollfd* pfd, std::string* s) {
if (!fd->ok()) return true;
- if (pfd.revents & POLLIN) {
+ if (pfd->revents & POLLIN) {
char buf[1024];
ssize_t n = TEMP_FAILURE_RETRY(read(fd->get(), buf, sizeof(buf)));
if (n < 0) return false;
if (n > 0) *s += std::string_view(buf, n);
}
- if (pfd.revents & POLLHUP) {
+ if (pfd->revents & POLLHUP) {
fd->reset();
}
return true;
@@ -142,9 +142,9 @@
int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
if (pollRet == -1) return android::base::ErrnoError() << "poll()";
- if (!handlePoll(&ret.outPipe, *outPollFd, &ret.stdout))
+ if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdout))
return android::base::ErrnoError() << "read(stdout)";
- if (!handlePoll(&ret.errPipe, *errPollFd, &ret.stderr))
+ if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderr))
return android::base::ErrnoError() << "read(stderr)";
if (end && end(ret)) return ret;
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index c69bb9e..a6d35c7 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -90,6 +90,8 @@
static void setLimitCallback(binder_proxy_limit_callback cb);
static void setBinderProxyCountWatermarks(int high, int low);
+ std::optional<int32_t> getDebugBinderHandle() const;
+
class ObjectManager {
public:
ObjectManager();
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index c484d83..43fc5ff 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -259,21 +259,20 @@
* but calls from different threads are allowed to be interleaved.
*
* This returns the object which is already attached. If this returns a
- * non-null value, it means that attachObject failed. TODO(b/192023359):
- * remove logs and add [[nodiscard]]
+ * non-null value, it means that attachObject failed (a given objectID can
+ * only be used once).
*/
- virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
- object_cleanup_func func) = 0;
+ [[nodiscard]] virtual void* attachObject(const void* objectID, void* object,
+ void* cleanupCookie, object_cleanup_func func) = 0;
/**
* Returns object attached with attachObject.
*/
- virtual void* findObject(const void* objectID) const = 0;
+ [[nodiscard]] virtual void* findObject(const void* objectID) const = 0;
/**
* Returns object attached with attachObject, and detaches it. This does not
- * delete the object. This is equivalent to using attachObject to attach a null
- * object.
+ * delete the object.
*/
- virtual void* detachObject(const void* objectID) = 0;
+ [[nodiscard]] virtual void* detachObject(const void* objectID) = 0;
/**
* Use the lock that this binder contains internally. For instance, this can
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/Parcel.h b/libs/binder/include/binder/Parcel.h
index 02052ad..fd8ac62 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -245,9 +245,10 @@
template<typename T>
status_t writeNullableParcelable(const std::optional<T>& parcelable)
{ return writeData(parcelable); }
- template<typename T>
- status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead")))
- { return writeData(parcelable); }
+ template <typename T>
+ status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
+ return writeData(parcelable);
+ }
status_t writeParcelable(const Parcelable& parcelable);
@@ -401,9 +402,10 @@
template<typename T>
status_t readParcelable(std::optional<T>* parcelable) const
{ return readData(parcelable); }
- template<typename T>
- status_t readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead")))
- { return readData(parcelable); }
+ template <typename T>
+ status_t readParcelable(std::unique_ptr<T>* parcelable) const {
+ return readData(parcelable);
+ }
// If strong binder would be nullptr, readStrongBinder() returns an error.
// TODO: T must be derived from IInterface, fix for clarity.
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 9e4475c..42c85f9 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -42,6 +42,7 @@
}
mStability = other.mStability;
}
+ ParcelableHolder(ParcelableHolder&& other) = default;
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index c8d2857..bf3e7e0 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -19,6 +19,7 @@
#include <binder/IBinder.h>
#include <binder/RpcAddress.h>
#include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -31,6 +32,7 @@
namespace android {
+class FdTrigger;
class RpcSocketAddress;
/**
@@ -47,7 +49,8 @@
*/
class RpcServer final : public virtual RefBase, private RpcSession::EventListener {
public:
- static sp<RpcServer> make();
+ static sp<RpcServer> make(
+ std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
/**
* This represents a session for responses, e.g.:
@@ -57,12 +60,12 @@
* process B makes binder b and sends it to A
* A uses this 'back session' to send things back to B
*/
- [[nodiscard]] bool setupUnixDomainServer(const char* path);
+ [[nodiscard]] status_t setupUnixDomainServer(const char* path);
/**
* Creates an RPC server at the current port.
*/
- [[nodiscard]] bool setupVsockServer(unsigned int port);
+ [[nodiscard]] status_t setupVsockServer(unsigned int port);
/**
* Creates an RPC server at the current port using IPv4.
@@ -72,8 +75,14 @@
* Set |port| to 0 to pick an ephemeral port; see discussion of
* /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort|
* will be set to the picked port number, if it is not null.
+ *
+ * Set the IPv4 address for the socket to be listening on.
+ * "127.0.0.1" allows for local connections from the same device.
+ * "0.0.0.0" allows for connections on any IP address that the device may
+ * have
*/
- [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+ [[nodiscard]] status_t setupInetServer(const char* address, unsigned int port,
+ unsigned int* assignedPort);
/**
* If setup*Server has been successful, return true. Otherwise return false.
@@ -89,7 +98,7 @@
* Set up server using an external FD previously set up by releaseServer().
* Return false if there's already a server.
*/
- bool setupExternalServer(base::unique_fd serverFd);
+ [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
@@ -105,6 +114,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)
*
@@ -154,16 +170,18 @@
private:
friend sp<RpcServer>;
- RpcServer();
+ explicit RpcServer(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
- void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
+ void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd);
- bool setupSocketServer(const RpcSocketAddress& address);
+ status_t setupSocketServer(const RpcSocketAddress& address);
+ const std::unique_ptr<RpcTransportCtxFactory> mRpcTransportCtxFactory;
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
@@ -173,8 +191,9 @@
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
std::map<RpcAddress, sp<RpcSession>> mSessions;
- std::unique_ptr<RpcSession::FdTrigger> mShutdownTrigger;
+ std::unique_ptr<FdTrigger> mShutdownTrigger;
std::condition_variable mShutdownCv;
+ std::unique_ptr<RpcTransportCtx> mCtx;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 69c2a1a..6e6eb74 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -18,6 +18,7 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcAddress.h>
+#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -36,6 +37,12 @@
class RpcServer;
class RpcSocketAddress;
class RpcState;
+class RpcTransport;
+class FdTrigger;
+
+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
@@ -44,7 +51,8 @@
*/
class RpcSession final : public virtual RefBase {
public:
- static sp<RpcSession> make();
+ static sp<RpcSession> make(
+ std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
/**
* Set the maximum number of threads allowed to be made (for things like callbacks).
@@ -60,20 +68,40 @@
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.
*/
- [[nodiscard]] bool setupUnixDomainClient(const char* path);
+ [[nodiscard]] status_t setupUnixDomainClient(const char* path);
/**
* Connects to an RPC server at the CVD & port.
*/
- [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
+ [[nodiscard]] status_t setupVsockClient(unsigned int cvd, unsigned int port);
/**
* Connects to an RPC server at the given address and port.
*/
- [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
+ [[nodiscard]] status_t setupInetClient(const char* addr, unsigned int port);
+
+ /**
+ * Starts talking to an RPC server which has already been connected to. This
+ * is expected to be used when another process has permission to connect to
+ * a binder RPC service, but this process only has permission to talk to
+ * that service.
+ *
+ * For convenience, if 'fd' is -1, 'request' will be called.
+ *
+ * For future compatibility, 'request' should not reference any stack data.
+ */
+ [[nodiscard]] status_t setupPreconnectedClient(base::unique_fd fd,
+ std::function<base::unique_fd()>&& request);
/**
* For debugging!
@@ -82,7 +110,7 @@
* response will never be satisfied. All data sent here will be
* unceremoniously cast down the bottomless pit, /dev/null.
*/
- [[nodiscard]] bool addNullDebuggingClient();
+ [[nodiscard]] status_t addNullDebuggingClient();
/**
* Query the other side of the session for the root object hosted by that
@@ -131,66 +159,27 @@
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();
-
- /**
- * Close the write end of the pipe so that the read end receives POLLHUP.
- * Not threadsafe.
- */
- void trigger();
-
- /**
- * Whether this has been triggered.
- */
- bool isTriggered();
-
- /**
- * 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;
- };
+ explicit RpcSession(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
class EventListener : public virtual RefBase {
public:
- virtual void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0;
+ virtual void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0;
virtual void onSessionIncomingThreadEnded() = 0;
};
class WaitForShutdownListener : public EventListener {
public:
- void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
+ void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
void waitForShutdown(std::unique_lock<std::mutex>& lock);
private:
std::condition_variable mCv;
- bool mShutdown = false;
+ volatile bool mShutdown = false;
};
struct RpcConnection : public RefBase {
- base::unique_fd fd;
+ std::unique_ptr<RpcTransport> rpcTransport;
// whether this or another thread is currently using this fd to make
// or receive transactions.
@@ -216,20 +205,30 @@
// Status of setup
status_t status;
};
- PreJoinSetupResult preJoinSetup(base::unique_fd fd);
+ PreJoinSetupResult preJoinSetup(std::unique_ptr<RpcTransport> rpcTransport);
// join on thread passed to preJoinThreadOwnership
static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
- [[nodiscard]] bool setupSocketClient(const RpcSocketAddress& address);
- [[nodiscard]] bool setupOneSocketConnection(const RpcSocketAddress& address,
- const RpcAddress& sessionId, bool server);
- [[nodiscard]] bool addOutgoingConnection(base::unique_fd fd, bool init);
+ [[nodiscard]] status_t setupClient(
+ const std::function<status_t(const RpcAddress& sessionId, bool incoming)>&
+ connectAndInit);
+ [[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
+ [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
+ const RpcAddress& sessionId, bool incoming);
+ [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd, const RpcAddress& sessionId,
+ bool incoming);
+ [[nodiscard]] status_t addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport);
+ [[nodiscard]] status_t addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport,
+ bool init);
[[nodiscard]] bool setForServer(const wp<RpcServer>& server,
const wp<RpcSession::EventListener>& eventListener,
const RpcAddress& sessionId);
- sp<RpcConnection> assignIncomingConnectionToThisThread(base::unique_fd fd);
+ sp<RpcConnection> assignIncomingConnectionToThisThread(
+ std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
+ status_t initShutdownTrigger();
+
enum class ConnectionUse {
CLIENT,
CLIENT_ASYNC,
@@ -260,6 +259,8 @@
bool mReentrant = false;
};
+ const std::unique_ptr<RpcTransportCtxFactory> mRpcTransportCtxFactory;
+
// On the other side of a session, for each of mOutgoingConnections here, there should
// be one of mIncomingConnections on the other side (and vice versa).
//
@@ -287,12 +288,14 @@
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;
// hint index into clients, ++ when sending an async transaction
size_t mOutgoingConnectionsOffset = 0;
std::vector<sp<RpcConnection>> mOutgoingConnections;
+ size_t mMaxIncomingConnections = 0;
std::vector<sp<RpcConnection>> mIncomingConnections;
std::map<std::thread::id, std::thread> mThreads;
};
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
new file mode 100644
index 0000000..1b69519
--- /dev/null
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+// Wraps the transport layer of RPC. Implementation may use plain sockets or TLS.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class FdTrigger;
+
+// Represents a socket connection.
+class RpcTransport {
+public:
+ virtual ~RpcTransport() = default;
+
+ // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
+ virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+
+ /**
+ * Read (or write), but allow to be interrupted by a trigger.
+ *
+ * Return:
+ * OK - succeeded in completely processing 'size'
+ * error - interrupted (failure or trigger)
+ */
+ virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
+ size_t size) = 0;
+ virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf, size_t size) = 0;
+
+protected:
+ RpcTransport() = default;
+};
+
+// Represents the context that generates the socket connection.
+class RpcTransportCtx {
+public:
+ virtual ~RpcTransportCtx() = default;
+
+ // Create a new RpcTransport object.
+ //
+ // Implemenion details: for TLS, this function may incur I/O. |fdTrigger| may be used
+ // to interrupt I/O. This function blocks until handshake is finished.
+ [[nodiscard]] virtual std::unique_ptr<RpcTransport> newTransport(
+ android::base::unique_fd fd, FdTrigger *fdTrigger) const = 0;
+
+protected:
+ RpcTransportCtx() = default;
+};
+
+// A factory class that generates RpcTransportCtx.
+class RpcTransportCtxFactory {
+public:
+ virtual ~RpcTransportCtxFactory() = default;
+ // Creates server context.
+ [[nodiscard]] virtual std::unique_ptr<RpcTransportCtx> newServerCtx() const = 0;
+
+ // Creates client context.
+ [[nodiscard]] virtual std::unique_ptr<RpcTransportCtx> newClientCtx() const = 0;
+
+ // Return a short description of this transport (e.g. "raw"). For logging / debugging / testing
+ // only.
+ [[nodiscard]] virtual const char *toCString() const = 0;
+
+protected:
+ RpcTransportCtxFactory() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcTransportRaw.h b/libs/binder/include/binder/RpcTransportRaw.h
new file mode 100644
index 0000000..6fb1f92
--- /dev/null
+++ b/libs/binder/include/binder/RpcTransportRaw.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses plain sockets.
+// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx.
+
+#pragma once
+
+#include <memory>
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory with TLS disabled.
+class RpcTransportCtxFactoryRaw : 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:
+ RpcTransportCtxFactoryRaw() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 629b565..ce4362f 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -44,10 +44,9 @@
// to old servers, and new servers know how to interpret the 8-byte result,
// they can still communicate.
//
-// Every binder object has a stability level associated with it, and when
-// communicating with a binder, we make sure that the command we sent is one
-// that it knows how to process. The summary of stability of a binder is
-// represented by a Stability::Category object.
+// This class is specifically about (1). (2) is not currently tracked by
+// libbinder for regular binder calls, and everything on the system uses the
+// same copy of libbinder.
class Stability final {
public:
@@ -128,7 +127,10 @@
static void tryMarkCompilationUnit(IBinder* binder);
- enum Level : uint8_t {
+ // Currently, we use int16_t for Level so that it can fit in BBinder.
+ // However, on the wire, we have 4 bytes reserved for stability, so whenever
+ // we ingest a Level, we always accept an int32_t.
+ enum Level : int16_t {
UNDECLARED = 0,
VENDOR = 0b000011,
@@ -136,37 +138,6 @@
VINTF = 0b111111,
};
- // This is the format of stability passed on the wire. It is only 2 bytes
- // long, but 2 bytes in addition to this are reserved here. The difference
- // in size is in order to free up space in BBinder, which is fixed by
- // prebuilts inheriting from it.
- struct Category {
- static inline Category fromRepr(int16_t representation) {
- return *reinterpret_cast<Category*>(&representation);
- }
- int16_t repr() const { return *reinterpret_cast<const int16_t*>(this); }
- static inline Category currentFromLevel(Level level);
-
- bool operator== (const Category& o) const {
- return repr() == o.repr();
- }
- bool operator!= (const Category& o) const {
- return !(*this == o);
- }
-
- std::string debugString();
-
- // This is the version of the wire protocol associated with the host
- // process of a particular binder. As the wire protocol changes, if
- // sending a transaction to a binder with an old version, the Parcel
- // class must write parcels according to the version documented here.
- uint8_t version;
-
- // bitmask of Stability::Level
- Level level;
- };
- static_assert(sizeof(Category) == sizeof(int16_t));
-
// returns the stability according to how this was built
static Level getLocalLevel();
@@ -179,18 +150,18 @@
REPR_ALLOW_DOWNGRADE = 2,
};
// applies stability to binder if stability level is known
- __attribute__((warn_unused_result))
- static status_t setRepr(IBinder* binder, int32_t representation, uint32_t flags);
+ __attribute__((warn_unused_result)) static status_t setRepr(IBinder* binder, int32_t setting,
+ uint32_t flags);
// get stability information as encoded on the wire
- static Category getCategory(IBinder* binder);
+ static int16_t getRepr(IBinder* binder);
// whether a transaction on binder is allowed, if the transaction
// is done from a context with a specific stability level
- static bool check(Category provided, Level required);
+ static bool check(int16_t provided, Level required);
- static bool isDeclaredLevel(Level level);
- static std::string levelString(Level level);
+ static bool isDeclaredLevel(int32_t level);
+ static std::string levelString(int32_t level);
Stability();
};
diff --git a/libs/binder/include_tls/binder/RpcTransportTls.h b/libs/binder/include_tls/binder/RpcTransportTls.h
new file mode 100644
index 0000000..531aaa9
--- /dev/null
+++ b/libs/binder/include_tls/binder/RpcTransportTls.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses TLS.
+
+#pragma once
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory with TLS enabled with self-signed certificate.
+class RpcTransportCtxFactoryTls : 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:
+ RpcTransportCtxFactoryTls() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 68ec669..bcb13ae 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -19,16 +19,20 @@
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+using android::OK;
using android::RpcServer;
using android::RpcSession;
+using android::status_t;
+using android::statusToString;
extern "C" {
bool RunRpcServer(AIBinder* service, unsigned int port) {
auto server = RpcServer::make();
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
- if (!server->setupVsockServer(port)) {
- LOG(ERROR) << "Failed to set up vsock server with port " << port;
+ if (status_t status = server->setupVsockServer(port); status != OK) {
+ LOG(ERROR) << "Failed to set up vsock server with port " << port
+ << " error: " << statusToString(status).c_str();
return false;
}
server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -41,8 +45,9 @@
AIBinder* RpcClient(unsigned int cid, unsigned int port) {
auto session = RpcSession::make();
- if (!session->setupVsockClient(cid, port)) {
- LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port;
+ if (status_t status = session->setupVsockClient(cid, port); status != OK) {
+ LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
+ << " error: " << statusToString(status).c_str();
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 266ef37..11e9fc5 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -47,7 +47,8 @@
void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */};
static void attach(const sp<IBinder>& binder) {
- binder->attachObject(kId, kValue, nullptr /*cookie*/, clean);
+ // can only attach once
+ CHECK_EQ(nullptr, binder->attachObject(kId, kValue, nullptr /*cookie*/, clean));
}
static bool has(const sp<IBinder>& binder) {
return binder != nullptr && binder->findObject(kId) == kValue;
@@ -81,8 +82,8 @@
const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
if (newDescriptor == currentDescriptor) {
LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
- << "' match during associateClass, but they are different class objects. "
- "Class descriptor collision?";
+ << "' match during associateClass, but they are different class objects ("
+ << clazz << " vs " << mClazz << "). Class descriptor collision?";
} else {
LOG(ERROR) << __func__
<< ": Class cannot be associated on object which already has a class. "
@@ -232,13 +233,15 @@
void ABpBinder::onLastStrongRef(const void* id) {
// Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for
- // the ABpBinder to be deleted. Since a strong reference to this ABpBinder object should no
- // longer be able to exist at the time of this method call, there is no longer a need to
- // recover it.
+ // the ABpBinder to be deleted. Even though we have no more references on the ABpBinder
+ // (BpRefBase), the remote object may still exist (for instance, if we
+ // receive it from another process, before the ABpBinder is attached).
ABpBinderTag::Value* value =
- static_cast<ABpBinderTag::Value*>(remote()->detachObject(ABpBinderTag::kId));
- if (value) ABpBinderTag::clean(ABpBinderTag::kId, value, nullptr /*cookie*/);
+ static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId));
+ CHECK_NE(nullptr, value) << "ABpBinder must always be attached";
+
+ remote()->withLock([&]() { value->binder = nullptr; });
BpRefBase::onLastStrongRef(id);
}
@@ -251,6 +254,9 @@
return static_cast<ABBinder*>(binder.get());
}
+ // The following code ensures that for a given binder object (remote or local), if it is not an
+ // ABBinder then at most one ABpBinder object exists in a given process representing it.
+
auto* value = static_cast<ABpBinderTag::Value*>(binder->findObject(ABpBinderTag::kId));
if (value == nullptr) {
value = new ABpBinderTag::Value;
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 6c44726..5de64f8 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -55,6 +55,12 @@
std::call_once(mFlagThis, [&]() {
__assert(__FILE__, __LINE__, "SharedRefBase: no ref created during lifetime");
});
+
+ if (ref() != nullptr) {
+ __assert(__FILE__, __LINE__,
+ "SharedRefBase: destructed but still able to lock weak_ptr. Is this object "
+ "double-owned?");
+ }
}
/**
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 5092d87..563d011 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -469,6 +469,22 @@
}
/**
+ * Convenience API for writing a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
+ const std::unique_ptr<P>& p) {
+ if (!p) {
+ return AParcel_writeInt32(parcel, 0); // null
+ }
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p->writeToParcel(parcel);
+}
+
+/**
* Convenience API for reading a nullable parcelable.
*/
template <typename P>
@@ -488,6 +504,25 @@
}
/**
+ * Convenience API for reading a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
+ std::unique_ptr<P>* p) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ p->reset();
+ return STATUS_OK;
+ }
+ *p = std::make_unique<P>();
+ return (*p)->readFromParcel(parcel);
+}
+
+/**
* Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
*/
template <typename P>
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 2277148..aa3b978 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -46,7 +46,7 @@
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
-
+ AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
binder_status_t writeToParcel(AParcel* parcel) const {
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 1c43948..b5c06e9 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -39,6 +39,7 @@
#include <condition_variable>
#include <iostream>
#include <mutex>
+#include <thread>
#include "android/binder_ibinder.h"
using namespace android;
@@ -223,6 +224,17 @@
return true;
}
+TEST(NdkBinder, DetectDoubleOwn) {
+ auto badService = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ EXPECT_DEATH(std::shared_ptr<MyBinderNdkUnitTest>(badService.get()),
+ "Is this object double-owned?");
+}
+
+TEST(NdkBinder, DetectNoSharedRefBaseCreated) {
+ EXPECT_DEATH(std::make_shared<MyBinderNdkUnitTest>(),
+ "SharedRefBase: no ref created during lifetime");
+}
+
TEST(NdkBinder, GetServiceThatDoesntExist) {
sp<IFoo> foo = IFoo::getService("asdfghkl;");
EXPECT_EQ(nullptr, foo.get());
@@ -250,6 +262,27 @@
EXPECT_EQ(2, out);
}
+TEST(NdkBinder, GetTestServiceStressTest) {
+ // libbinder has some complicated logic to make sure only one instance of
+ // ABpBinder is associated with each binder.
+
+ constexpr size_t kNumThreads = 10;
+ constexpr size_t kNumCalls = 1000;
+ std::vector<std::thread> threads;
+
+ for (size_t i = 0; i < kNumThreads; i++) {
+ threads.push_back(std::thread([&]() {
+ for (size_t j = 0; j < kNumCalls; j++) {
+ auto binder =
+ ndk::SpAIBinder(AServiceManager_checkService(IFoo::kSomeInstanceName));
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+ }
+ }));
+ }
+
+ for (auto& thread : threads) thread.join();
+}
+
void defaultInstanceCounter(const char* instance, void* context) {
if (strcmp(instance, "default") == 0) {
++*(size_t*)(context);
diff --git a/libs/binder/run_rpc_tests.sh b/libs/binder/run_rpc_tests.sh
new file mode 100755
index 0000000..7ba682d
--- /dev/null
+++ b/libs/binder/run_rpc_tests.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+set -ex
+
+if [ $# -eq 0 ]; then
+ :
+elif [ $# -eq 1 ] && [[ "$1" =~ ^host|device$ ]]; then
+ :
+else
+ echo "usage: $0 [device|host]"
+ false
+fi
+
+# Script maintained for RPC development, while it is active, to quickly run
+# tests. Generally, to match VTS/presubmit behavior, 'atest' should be used.
+
+function dtest () { adb shell /data/nativetest64/$1/$@; }
+function hbench () { $AT/out/host/linux-x86/benchmarktest/$1/$@; }
+function hfuzz () { time $ANDROID_HOST_OUT/fuzz/x86_64/$1/$@; }
+function htest () { time $ANDROID_BUILD_TOP/out/host/linux-x86/nativetest/$1/$@; }
+function pdtest () { adb wait-for-device && adb shell mkdir -p /data/nativetest64/$1 && adb push $OUT/data/nativetest64/$1/$1 /data/nativetest64/$1/$1 && dtest $@; }
+function dbench () { adb shell /data/benchmarktest64/$1/$@; }
+function pdbench () { adb wait-for-device && adb shell mkdir -p /data/benchmarktest64/$1 && adb push $OUT/data/benchmarktest64/$1/$1 /data/benchmarktest64/$1/$1 && dbench $@; }
+
+$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
+ binderRpcTest \
+ binder_rpc_fuzzer \
+ binder_parcel_fuzzer \
+ binderLibTest \
+ binderRpcBenchmark
+
+if ! [ "$1" = "device" ]; then
+ htest binderRpcTest
+ hbench binderRpcBenchmark
+ hfuzz binder_rpc_fuzzer -max_total_time=30
+ hfuzz binder_parcel_fuzzer -max_total_time=30
+fi
+
+if ! [ "$1" = "host" ]; then
+ pdtest binderRpcTest
+ pdtest binderLibTest
+ pdbench binderRpcBenchmark
+fi
+
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index f79b1b7..dd0c7b8 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -897,7 +897,7 @@
#[macro_export]
macro_rules! declare_binder_enum {
{
- $enum:ident : $backing:ty {
+ $enum:ident : [$backing:ty; $size:expr] {
$( $name:ident = $value:expr, )*
}
} => {
@@ -905,6 +905,11 @@
pub struct $enum(pub $backing);
impl $enum {
$( pub const $name: Self = Self($value); )*
+
+ #[inline(always)]
+ pub const fn enum_values() -> [Self; $size] {
+ [$(Self::$name),*]
+ }
}
impl $crate::parcel::Serialize for $enum {
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index a3f7620..a0e991c 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -493,7 +493,7 @@
assert_eq!(parcel.read::<i32>().unwrap(), 15);
let start = parcel.get_data_position();
- assert_eq!(parcel.read::<bool>().unwrap(), true);
+ assert!(parcel.read::<bool>().unwrap());
unsafe {
assert!(parcel.set_data_position(start).is_ok());
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 20e9178..179b7c8 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -23,7 +23,7 @@
use crate::sys;
use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
/// Rust version of the Java class android.os.ParcelFileDescriptor
#[derive(Debug)]
@@ -48,6 +48,12 @@
}
}
+impl AsRawFd for ParcelFileDescriptor {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
impl Serialize for ParcelFileDescriptor {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
let fd = self.0.as_raw_fd();
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 956ecfe..56c6165 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -764,6 +764,30 @@
}
}
+impl<T: Serialize> Serialize for Box<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Serialize::serialize(&**self, parcel)
+ }
+}
+
+impl<T: Deserialize> Deserialize for Box<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Deserialize::deserialize(parcel).map(Box::new)
+ }
+}
+
+impl<T: SerializeOption> SerializeOption for Box<T> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(|inner| &**inner), parcel)
+ }
+}
+
+impl<T: DeserializeOption> DeserializeOption for Box<T> {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ DeserializeOption::deserialize_option(parcel).map(|t| t.map(Box::new))
+ }
+}
+
#[test]
fn test_custom_parcelable() {
use crate::binder::Interface;
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 4702e45..415ede1 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -90,7 +90,7 @@
pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int {
let service_name = CStr::from_ptr(service_name).to_str().unwrap();
let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default());
- match binder::add_service(&service_name, service.as_binder()) {
+ match binder::add_service(service_name, service.as_binder()) {
Ok(_) => StatusCode::OK as c_int,
Err(e) => e as c_int,
}
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 62df9b7..9811cdf 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -23,9 +23,12 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android/debug/BnAdbCallback.h>
+#include <android/debug/IAdbManager.h>
#include <android/os/BnServiceManager.h>
#include <android/os/IServiceManager.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
using android::BBinder;
@@ -33,6 +36,7 @@
using android::OK;
using android::RpcServer;
using android::sp;
+using android::status_t;
using android::statusToString;
using android::String16;
using android::base::Basename;
@@ -47,7 +51,9 @@
namespace {
+const char* kLocalInetAddress = "127.0.0.1";
using ServiceRetriever = decltype(&android::IServiceManager::checkService);
+using android::debug::IAdbManager;
int Usage(const char* program) {
auto basename = Basename(program);
@@ -86,8 +92,8 @@
}
rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
- if (!rpcServer->setupInetServer(0, &port)) {
- LOG(ERROR) << "setupInetServer failed";
+ if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
+ LOG(ERROR) << "setupInetServer failed: " << statusToString(status);
return EX_SOFTWARE;
}
auto socket = rpcServer->releaseServer();
@@ -199,8 +205,8 @@
rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
rpcServer->setRootObject(service);
unsigned int port;
- if (!rpcServer->setupInetServer(0, &port)) {
- LOG(ERROR) << "Unable to set up inet server";
+ if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
+ LOG(ERROR) << "Unable to set up inet server: " << statusToString(status);
return EX_SOFTWARE;
}
LOG(INFO) << "Finish wrapping servicemanager with RPC on port " << port;
@@ -211,6 +217,25 @@
__builtin_unreachable();
}
+class AdbCallback : public android::debug::BnAdbCallback {
+public:
+ android::binder::Status onDebuggingChanged(bool enabled,
+ android::debug::AdbTransportType) override {
+ if (!enabled) {
+ LOG(ERROR) << "ADB debugging disabled, exiting.";
+ exit(EX_SOFTWARE);
+ }
+ return android::binder::Status::ok();
+ }
+};
+
+void exitOnAdbDebuggingDisabled() {
+ auto adb = android::waitForService<IAdbManager>(String16("adb"));
+ CHECK(adb != nullptr) << "Unable to retrieve service adb";
+ auto status = adb->registerCallback(sp<AdbCallback>::make());
+ CHECK(status.isOk()) << "Unable to call IAdbManager::registerCallback: " << status;
+}
+
// Log to logd. For warning and more severe messages, also log to stderr.
class ServiceDispatcherLogger {
public:
@@ -251,6 +276,10 @@
}
}
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ android::ProcessState::self()->startThreadPool();
+ exitOnAdbDebuggingDisabled();
+
if (optind + 1 != argc) return Usage(argv[0]);
auto name = argv[optind];
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 565f88e..24afcf6 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -348,3 +348,49 @@
},
},
}
+
+cc_test_host {
+ name: "binderHostDeviceTest",
+ defaults: ["binder_test_defaults"],
+ srcs: ["binderHostDeviceTest.cpp"],
+ test_config: "binderHostDeviceTest.xml",
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+ target_required: [
+ "binderHostDeviceTestService",
+ ],
+ test_suites: ["general-tests"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ test_options: {
+ unit_test: false,
+ },
+}
+
+cc_test {
+ name: "binderHostDeviceTestService",
+ // The binary is named differently from the module so that PushFilePreparer pushes the binary
+ // directly, not the test module directory.
+ stem: "binderHostDeviceTest-service",
+ defaults: ["binder_test_defaults"],
+ gtest: false,
+ auto_gen_config: false,
+ srcs: ["binderHostDeviceTestService.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/libs/binder/tests/IBinderRpcBenchmark.aidl b/libs/binder/tests/IBinderRpcBenchmark.aidl
index 1457422..2baf680 100644
--- a/libs/binder/tests/IBinderRpcBenchmark.aidl
+++ b/libs/binder/tests/IBinderRpcBenchmark.aidl
@@ -17,4 +17,5 @@
interface IBinderRpcBenchmark {
@utf8InCpp String repeatString(@utf8InCpp String str);
IBinder repeatBinder(IBinder binder);
+ byte[] repeatBytes(in byte[] bytes);
}
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
index 2d30c8d..307151c 100644
--- a/libs/binder/tests/binderClearBufTest.cpp
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/hex.h>
#include <android-base/logging.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
@@ -30,22 +31,6 @@
const String16 kServerName = String16("binderClearBuf");
-std::string hexString(const void* bytes, size_t len) {
- if (bytes == nullptr) return "<null>";
-
- const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
- char chars[] = "0123456789abcdef";
- std::string result;
- result.resize(len * 2);
-
- for (size_t i = 0; i < len; i++) {
- result[2 * i] = chars[bytes8[i] >> 4];
- result[2 * i + 1] = chars[bytes8[i] & 0xf];
- }
-
- return result;
-}
-
class FooBar : public BBinder {
public:
enum {
@@ -83,7 +68,7 @@
lastReply = reply.data();
lastReplySize = reply.dataSize();
}
- *outBuffer = hexString(lastReply, lastReplySize);
+ *outBuffer = android::base::HexString(lastReply, lastReplySize);
return result;
}
};
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
new file mode 100644
index 0000000..3f72b8f
--- /dev/null
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+// Integration test for servicedispatcher + adb forward. Requires ADB.
+
+#include <stdlib.h>
+
+#include <vector>
+
+#include <android-base/parsebool.h>
+#include <android-base/result-gmock.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/RpcSession.h>
+
+#include "../UtilsHost.h"
+
+using ::android::setDefaultServiceManager;
+using ::android::base::EndsWith;
+using ::android::base::Join;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Split;
+using ::android::base::StartsWith;
+using ::android::base::StringReplace;
+using ::android::base::Trim;
+using ::android::base::testing::Ok;
+using ::std::chrono_literals::operator""ms;
+using ::std::string_literals::operator""s;
+using ::testing::AllOf;
+using ::testing::Contains;
+using ::testing::ContainsRegex;
+using ::testing::ExplainMatchResult;
+using ::testing::InitGoogleMock;
+
+namespace android {
+
+namespace {
+
+constexpr const char* kServiceBinary = "/data/local/tmp/binderHostDeviceTest-service";
+constexpr const char* kServiceName = "binderHostDeviceTestService";
+constexpr const char* kDescriptor = "android.binderHostDeviceTestService";
+
+// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
+MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
+ *result_listener << statusToString(arg);
+ return expected == arg;
+}
+
+void initHostRpcServiceManagerOnce() {
+ static std::once_flag gSmOnce;
+ std::call_once(gSmOnce, [] { setDefaultServiceManager(createRpcDelegateServiceManager()); });
+}
+
+// Test for host service manager.
+class HostDeviceTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
+ ASSERT_THAT(debuggableResult, Ok());
+ ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
+ auto debuggableBool = ParseBool(Trim(debuggableResult->stdout));
+ ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdout);
+ if (debuggableBool == ParseBoolResult::kFalse) {
+ GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdout);
+ }
+
+ auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
+ ASSERT_THAT(lsResult, Ok());
+ if (lsResult->exitCode != 0) {
+ GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices "
+ "without servicedispatcher";
+ }
+
+ initHostRpcServiceManagerOnce();
+ ASSERT_NE(nullptr, defaultServiceManager()) << "No defaultServiceManager() over RPC";
+
+ auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor},
+ &CommandResult::stdoutEndsWithNewLine);
+ ASSERT_THAT(service, Ok());
+ ASSERT_EQ(std::nullopt, service->exitCode) << *service;
+ mService = std::move(*service);
+ }
+ void TearDown() override { mService.reset(); }
+
+ [[nodiscard]] static sp<IBinder> get(unsigned int hostPort) {
+ auto rpcSession = RpcSession::make();
+ if (status_t status = rpcSession->setupInetClient("127.0.0.1", hostPort); status != OK) {
+ ADD_FAILURE() << "Failed to setupInetClient on " << hostPort << ": "
+ << statusToString(status);
+ return nullptr;
+ }
+ return rpcSession->getRootObject();
+ }
+
+private:
+ std::optional<CommandResult> mService;
+};
+
+TEST_F(HostDeviceTest, List) {
+ auto sm = defaultServiceManager();
+
+ auto services = sm->listServices();
+ ASSERT_THAT(services, Contains(String16(kServiceName)));
+}
+
+TEST_F(HostDeviceTest, CheckService) {
+ auto sm = defaultServiceManager();
+
+ auto rpcBinder = sm->checkService(String16(kServiceName));
+ ASSERT_NE(nullptr, rpcBinder);
+
+ EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+ EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+}
+
+TEST_F(HostDeviceTest, GetService) {
+ auto sm = defaultServiceManager();
+
+ auto rpcBinder = sm->getService(String16(kServiceName));
+ ASSERT_NE(nullptr, rpcBinder);
+
+ EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+ EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+}
+
+TEST_F(HostDeviceTest, WaitForService) {
+ auto sm = defaultServiceManager();
+
+ auto rpcBinder = sm->waitForService(String16(kServiceName));
+ ASSERT_NE(nullptr, rpcBinder);
+
+ EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+ EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+}
+
+TEST_F(HostDeviceTest, TenClients) {
+ auto sm = defaultServiceManager();
+
+ auto threadFn = [&] {
+ auto rpcBinder = sm->checkService(String16(kServiceName));
+ ASSERT_NE(nullptr, rpcBinder);
+
+ EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+ EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+ };
+
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < 10; ++i) threads.emplace_back(threadFn);
+ for (auto& thread : threads) thread.join();
+}
+
+} // namespace
+
+} // namespace android
diff --git a/libs/binder/tests/binderHostDeviceTest.xml b/libs/binder/tests/binderHostDeviceTest.xml
new file mode 100644
index 0000000..250ed3a
--- /dev/null
+++ b/libs/binder/tests/binderHostDeviceTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs binderHostDeviceTest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="binderHostDeviceTest-service"
+ value="/data/local/tmp/binderHostDeviceTest-service"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.binary.ExecutableHostTest">
+ <option name="binary" value="binderHostDeviceTest"/>
+ </test>
+</configuration>
diff --git a/libs/binder/tests/binderHostDeviceTestService.cpp b/libs/binder/tests/binderHostDeviceTestService.cpp
new file mode 100644
index 0000000..6ddd2e7
--- /dev/null
+++ b/libs/binder/tests/binderHostDeviceTestService.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <sysexits.h>
+
+#include <android-base/logging.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+namespace {
+class Service : public android::BBinder {
+public:
+ Service(std::string_view descriptor) : mDescriptor(descriptor.data(), descriptor.size()) {}
+ const android::String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+private:
+ android::String16 mDescriptor;
+};
+} // namespace
+
+int main(int argc, char** argv) {
+ if (argc != 3) {
+ std::cerr << "usage: " << argv[0] << " <service-name> <interface-descriptor>" << std::endl;
+ return EX_USAGE;
+ }
+ auto name = argv[1];
+ auto descriptor = argv[2];
+
+ auto sm = android::defaultServiceManager();
+ CHECK(sm != nullptr);
+ auto service = android::sp<Service>::make(descriptor);
+ auto status = sm->addService(android::String16(name), service);
+ CHECK_EQ(android::OK, status) << android::statusToString(status);
+ std::cout << "running..." << std::endl;
+ android::ProcessState::self()->startThreadPool();
+ android::IPCThreadState::self()->joinThreadPool();
+ LOG(ERROR) << "joinThreadPool exits";
+ return EX_SOFTWARE;
+}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 4c3225f..eea7d8c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1192,8 +1192,8 @@
if (rpcServer == nullptr) return {};
rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
unsigned int port;
- if (!rpcServer->setupInetServer(0, &port)) {
- ADD_FAILURE() << "setupInetServer failed";
+ if (status_t status = rpcServer->setupInetServer("127.0.0.1", 0, &port); status != OK) {
+ ADD_FAILURE() << "setupInetServer failed" << statusToString(status);
return {};
}
return {rpcServer->releaseServer(), port};
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index a457e67..e430c28 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,21 +18,33 @@
#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::status_t;
+using android::statusToString;
+using android::String16;
using android::binder::Status;
class MyBinderRpcBenchmark : public BnBinderRpcBenchmark {
@@ -40,34 +52,60 @@
*out = str;
return Status::ok();
}
- Status repeatBinder(const sp<IBinder>& str, sp<IBinder>* out) override {
- *out = str;
+ Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
+ *out = binder;
+ return Status::ok();
+ }
+ Status repeatBytes(const std::vector<uint8_t>& bytes, std::vector<uint8_t>* out) override {
+ *out = bytes;
return Status::ok();
}
};
-static sp<RpcSession> gSession = RpcSession::make();
+enum Transport {
+ KERNEL,
+ RPC,
+};
-void BM_getRootObject(benchmark::State& state) {
- while (state.KeepRunning()) {
- CHECK(gSession->getRootObject() != nullptr);
+static const std::initializer_list<int64_t> kTransportList = {
+#ifdef __BIONIC__
+ Transport::KERNEL,
+#endif
+ 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)->ArgsProduct({kTransportList});
-void BM_repeatString(benchmark::State& state) {
- sp<IBinder> binder = gSession->getRootObject();
- CHECK(binder != nullptr);
+void BM_repeatTwoPageString(benchmark::State& state) {
+ sp<IBinder> binder = getBinderForOptions(state);
+
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -92,10 +130,30 @@
CHECK(ret.isOk()) << ret;
}
}
-BENCHMARK(BM_repeatString);
+BENCHMARK(BM_repeatTwoPageString)->ArgsProduct({kTransportList});
+
+void BM_throughputForTransportAndBytes(benchmark::State& state) {
+ sp<IBinder> binder = getBinderForOptions(state);
+ sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
+ CHECK(iface != nullptr);
+
+ std::vector<uint8_t> bytes = std::vector<uint8_t>(state.range(1));
+ for (size_t i = 0; i < bytes.size(); i++) {
+ bytes[i] = i % 256;
+ }
+
+ while (state.KeepRunning()) {
+ std::vector<uint8_t> out;
+ Status ret = iface->repeatBytes(bytes, &out);
+ CHECK(ret.isOk()) << ret;
+ }
+}
+BENCHMARK(BM_throughputForTransportAndBytes)
+ ->ArgsProduct({kTransportList,
+ {64, 1024, 2048, 4096, 8182, 16364, 32728, 65535, 65536, 65537}});
void BM_repeatBinder(benchmark::State& state) {
- sp<IBinder> binder = gSession->getRootObject();
+ sp<IBinder> binder = getBinderForOptions(state);
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -109,7 +167,7 @@
CHECK(ret.isOk()) << ret;
}
}
-BENCHMARK(BM_repeatBinder);
+BENCHMARK(BM_repeatBinder)->ArgsProduct({kTransportList});
int main(int argc, char** argv) {
::benchmark::Initialize(&argc, argv);
@@ -118,19 +176,44 @@
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()));
+ CHECK_EQ(OK, 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
+
+ status_t status;
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
- if (gSession->setupUnixDomainClient(addr.c_str())) goto success;
+ status = gSession->setupUnixDomainClient(addr.c_str());
+ if (status == OK) goto success;
}
- LOG(FATAL) << "Could not connect.";
+ LOG(FATAL) << "Could not connect: " << statusToString(status).c_str();
success:
::benchmark::RunSpecifiedBenchmarks();
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index e452678..15ccae9 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -29,6 +29,8 @@
#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
+#include <binder/RpcTransportRaw.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -40,6 +42,7 @@
#include <sys/prctl.h>
#include <unistd.h>
+#include "../RpcSocketAddress.h" // for testing preconnected clients
#include "../RpcState.h" // for debugging
#include "../vm_sockets.h" // for VMADDR_*
@@ -47,6 +50,25 @@
namespace android {
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+ RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+const char* kLocalInetAddress = "127.0.0.1";
+
+enum class RpcSecurity { RAW };
+
+static inline std::vector<RpcSecurity> RpcSecurityValues() {
+ return {RpcSecurity::RAW};
+}
+
+static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW:
+ return RpcTransportCtxFactoryRaw::make();
+ default:
+ LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
+ }
+}
+
TEST(BinderRpcParcel, EntireParcelFormatted) {
Parcel p;
p.writeInt32(3);
@@ -54,19 +76,39 @@
EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
}
-TEST(BinderRpc, SetExternalServer) {
+class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> {
+public:
+ static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
+ return newFactory(info.param)->toCString();
+ }
+};
+
+TEST_P(BinderRpcSimple, SetExternalServerTest) {
base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
int sinkFd = sink.get();
- auto server = RpcServer::make();
+ auto server = RpcServer::make(newFactory(GetParam()));
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
ASSERT_FALSE(server->hasServer());
- ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
+ ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
ASSERT_TRUE(server->hasServer());
base::unique_fd retrieved = server->releaseServer();
ASSERT_FALSE(server->hasServer());
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) \
@@ -127,11 +169,6 @@
out->clear();
for (auto session : spServer->listSessions()) {
size_t count = session->state()->countBinders();
- if (count != 1) {
- // this is called when there is only one binder held remaining,
- // so to aid debugging
- session->state()->dump();
- }
out->push_back(count);
}
return Status::ok();
@@ -360,7 +397,11 @@
EXPECT_EQ(remoteCount, 1);
}
- EXPECT_OK(rootIface->scheduleShutdown());
+ // even though it is on another thread, shutdown races with
+ // the transaction reply being written
+ if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
}
rootIface = nullptr;
@@ -369,12 +410,15 @@
};
enum class SocketType {
+ PRECONNECTED,
UNIX,
VSOCK,
INET,
};
-static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
- switch (info.param) {
+static inline std::string PrintToString(SocketType socketType) {
+ switch (socketType) {
+ case SocketType::PRECONNECTED:
+ return "preconnected_uds";
case SocketType::UNIX:
return "unix_domain_socket";
case SocketType::VSOCK:
@@ -387,16 +431,41 @@
}
}
-class BinderRpc : public ::testing::TestWithParam<SocketType> {
+static base::unique_fd connectToUds(const char* addrStr) {
+ UnixSocketAddress addr(addrStr);
+ base::unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ int savedErrno = errno;
+ CHECK(serverFd.ok()) << "Could not create socket " << addrStr << ": " << strerror(savedErrno);
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Could not connect to socket " << addrStr << ": " << strerror(savedErrno);
+ }
+ return serverFd;
+}
+
+class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> {
public:
+ struct Options {
+ size_t numThreads = 1;
+ size_t numSessions = 1;
+ size_t numIncomingConnections = 0;
+ };
+
+ static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [type, security] = info.param;
+ return PrintToString(type) + "_" + newFactory(security)->toCString();
+ }
+
// This creates a new process serving an interface on a certain number of
// threads.
ProcessSession createRpcTestSocketServerProcess(
- size_t numThreads, size_t numSessions, size_t numReverseConnections,
- const std::function<void(const sp<RpcServer>&)>& configure) {
- CHECK_GE(numSessions, 1) << "Must have at least one session to a server";
+ const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) {
+ CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
- SocketType socketType = GetParam();
+ SocketType socketType = std::get<0>(GetParam());
+ RpcSecurity rpcSecurity = std::get<1>(GetParam());
unsigned int vsockPort = allocateVsockPort();
std::string addr = allocateSocketAddress();
@@ -404,22 +473,24 @@
auto ret = ProcessSession{
.host = Process([&](android::base::borrowed_fd writeEnd) {
- sp<RpcServer> server = RpcServer::make();
+ sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity));
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
- server->setMaxThreads(numThreads);
+ server->setMaxThreads(options.numThreads);
unsigned int outPort = 0;
switch (socketType) {
+ case SocketType::PRECONNECTED:
+ [[fallthrough]];
case SocketType::UNIX:
- CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
+ CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())) << addr;
break;
case SocketType::VSOCK:
- CHECK(server->setupVsockServer(vsockPort));
+ CHECK_EQ(OK, server->setupVsockServer(vsockPort));
break;
case SocketType::INET: {
- CHECK(server->setupInetServer(0, &outPort));
+ CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
CHECK_NE(0, outPort);
break;
}
@@ -445,36 +516,44 @@
CHECK_NE(0, outPort);
}
- for (size_t i = 0; i < numSessions; i++) {
- sp<RpcSession> session = RpcSession::make();
- session->setMaxThreads(numReverseConnections);
+ status_t status;
+
+ for (size_t i = 0; i < options.numSessions; i++) {
+ sp<RpcSession> session = RpcSession::make(newFactory(rpcSecurity));
+ session->setMaxThreads(options.numIncomingConnections);
switch (socketType) {
+ case SocketType::PRECONNECTED:
+ status = session->setupPreconnectedClient({}, [=]() {
+ return connectToUds(addr.c_str());
+ });
+ if (status == OK) goto success;
+ break;
case SocketType::UNIX:
- if (session->setupUnixDomainClient(addr.c_str())) goto success;
+ status = session->setupUnixDomainClient(addr.c_str());
+ if (status == OK) goto success;
break;
case SocketType::VSOCK:
- if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+ status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
+ if (status == OK) goto success;
break;
case SocketType::INET:
- if (session->setupInetClient("127.0.0.1", outPort)) goto success;
+ status = session->setupInetClient("127.0.0.1", outPort);
+ if (status == OK) goto success;
break;
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
- LOG_ALWAYS_FATAL("Could not connect");
+ LOG_ALWAYS_FATAL("Could not connect %s", statusToString(status).c_str());
success:
ret.sessions.push_back({session, session->getRootObject()});
}
return ret;
}
- BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads,
- size_t numSessions = 1,
- size_t numReverseConnections = 0) {
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) {
BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcess(numThreads, numSessions,
- numReverseConnections,
+ .proc = createRpcTestSocketServerProcess(options,
[&](const sp<RpcServer>& server) {
sp<MyBinderRpcTest> service =
new MyBinderRpcTest;
@@ -491,19 +570,19 @@
};
TEST_P(BinderRpc, Ping) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
ASSERT_NE(proc.rootBinder, nullptr);
EXPECT_EQ(OK, proc.rootBinder->pingBinder());
}
TEST_P(BinderRpc, GetInterfaceDescriptor) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
ASSERT_NE(proc.rootBinder, nullptr);
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
TEST_P(BinderRpc, MultipleSessions) {
- auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*sessions*/);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
for (auto session : proc.proc.sessions) {
ASSERT_NE(nullptr, session.root);
EXPECT_EQ(OK, session.root->pingBinder());
@@ -511,14 +590,14 @@
}
TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
Parcel data;
Parcel reply;
EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
}
TEST_P(BinderRpc, AppendSeparateFormats) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
Parcel p1;
p1.markForBinder(proc.rootBinder);
@@ -531,7 +610,7 @@
}
TEST_P(BinderRpc, UnknownTransaction) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
Parcel data;
data.markForBinder(proc.rootBinder);
Parcel reply;
@@ -539,19 +618,19 @@
}
TEST_P(BinderRpc, SendSomethingOneway) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
EXPECT_OK(proc.rootIface->sendString("asdf"));
}
TEST_P(BinderRpc, SendAndGetResultBack) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
std::string doubled;
EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
EXPECT_EQ("cool cool ", doubled);
}
TEST_P(BinderRpc, SendAndGetResultBackBig) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
std::string single = std::string(1024, 'a');
std::string doubled;
EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
@@ -559,7 +638,7 @@
}
TEST_P(BinderRpc, CallMeBack) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
int32_t pingResult;
EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
@@ -569,7 +648,7 @@
}
TEST_P(BinderRpc, RepeatBinder) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> inBinder = new MyBinderRpcSession("foo");
sp<IBinder> outBinder;
@@ -591,7 +670,7 @@
}
TEST_P(BinderRpc, RepeatTheirBinder) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinderRpcSession> session;
EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
@@ -615,7 +694,7 @@
}
TEST_P(BinderRpc, RepeatBinderNull) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> outBinder;
EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
@@ -623,7 +702,7 @@
}
TEST_P(BinderRpc, HoldBinder) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
IBinder* ptr = nullptr;
{
@@ -649,8 +728,8 @@
// aren't supported.
TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
- auto proc1 = createRpcTestSocketServerProcess(1);
- auto proc2 = createRpcTestSocketServerProcess(1);
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
sp<IBinder> outBinder;
EXPECT_EQ(INVALID_OPERATION,
@@ -658,7 +737,7 @@
}
TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
- auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*sessions*/);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
sp<IBinder> outBinder;
EXPECT_EQ(INVALID_OPERATION,
@@ -667,7 +746,7 @@
}
TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
sp<IBinder> outBinder;
@@ -676,7 +755,7 @@
}
TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
// for historical reasons, IServiceManager interface only returns the
// exception code
@@ -687,7 +766,7 @@
// END TESTS FOR LIMITATIONS OF SOCKET BINDER
TEST_P(BinderRpc, RepeatRootObject) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> outBinder;
EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
@@ -695,7 +774,7 @@
}
TEST_P(BinderRpc, NestedTransactions) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
auto nastyNester = sp<MyBinderRpcTest>::make();
EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
@@ -706,7 +785,7 @@
}
TEST_P(BinderRpc, SameBinderEquality) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> a;
EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
@@ -718,7 +797,7 @@
}
TEST_P(BinderRpc, SameBinderEqualityWeak) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> a;
EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
@@ -750,7 +829,7 @@
} while (false)
TEST_P(BinderRpc, SingleSession) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
sp<IBinderRpcSession> session;
EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
@@ -764,7 +843,7 @@
}
TEST_P(BinderRpc, ManySessions) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
std::vector<sp<IBinderRpcSession>> sessions;
@@ -800,7 +879,7 @@
TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
constexpr size_t kNumThreads = 10;
- auto proc = createRpcTestSocketServerProcess(kNumThreads);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
EXPECT_OK(proc.rootIface->lock());
@@ -834,7 +913,7 @@
constexpr size_t kNumCalls = kNumThreads + 3;
constexpr size_t kSleepMs = 500;
- auto proc = createRpcTestSocketServerProcess(kNumThreads);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
size_t epochMsBefore = epochMillis();
@@ -858,7 +937,7 @@
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 100;
- auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
std::vector<std::thread> threads;
for (size_t i = 0; i < kNumClientThreads; i++) {
@@ -879,7 +958,7 @@
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 500;
- auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
std::vector<std::thread> threads;
for (size_t i = 0; i < kNumClientThreads; i++) {
@@ -900,7 +979,7 @@
constexpr size_t kReallyLongTimeMs = 100;
constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
size_t epochMsBefore = epochMillis();
@@ -916,7 +995,7 @@
constexpr size_t kSleepMs = 50;
// make sure calls to the same object happen on the same thread
- auto proc = createRpcTestSocketServerProcess(1 + kNumExtraServerThreads);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1 + kNumExtraServerThreads});
EXPECT_OK(proc.rootIface->lock());
@@ -946,7 +1025,7 @@
constexpr size_t kNumClients = 2;
constexpr size_t kTooLongMs = 1000;
- auto proc = createRpcTestSocketServerProcess(kNumClients /*threads*/, 2 /*sessions*/);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumClients, .numSessions = 2});
// Build up oneway calls on the second session to make sure it terminates
// and shuts down. The first session should be unaffected (proc destructor
@@ -968,6 +1047,12 @@
Status status = iface->sleepMsAsync(kTooLongMs);
EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ // now that it has died, wait for the remote session to shutdown
+ std::vector<int32_t> remoteCounts;
+ do {
+ EXPECT_OK(proc.rootIface->countBinders(&remoteCounts));
+ } while (remoteCounts.size() == kNumClients);
+
// the second session should be shutdown in the other process by the time we
// are able to join above (it'll only be hung up once it finishes processing
// any pending commands). We need to erase this session from the record
@@ -982,7 +1067,8 @@
for (bool callIsOneway : {true, false}) {
for (bool callbackIsOneway : {true, false}) {
for (bool delayed : {true, false}) {
- auto proc = createRpcTestSocketServerProcess(1, 1, 1);
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
auto cb = sp<MyBinderRpcCallback>::make();
if (callIsOneway) {
@@ -1007,9 +1093,11 @@
// since we are severing the connection, we need to go ahead and
// tell the server to shutdown and exit so that waitpid won't hang
- EXPECT_OK(proc.rootIface->scheduleShutdown());
+ if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
- // since this session has a reverse connection w/ a threadpool, we
+ // since this session has an incoming connection w/ a threadpool, we
// need to manually shut it down
EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
@@ -1020,7 +1108,7 @@
}
TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
auto cb = sp<MyBinderRpcCallback>::make();
Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
@@ -1029,7 +1117,7 @@
TEST_P(BinderRpc, Die) {
for (bool doDeathCleanup : {true, false}) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
// make sure there is some state during crash
// 1. we hold their binder
@@ -1047,7 +1135,7 @@
}
TEST_P(BinderRpc, UseKernelBinderCallingId) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
// we can't allocate IPCThreadState so actually the first time should
// succeed :(
@@ -1060,7 +1148,7 @@
}
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
ASSERT_NE(binder, nullptr);
@@ -1069,7 +1157,7 @@
}
TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
- auto proc = createRpcTestSocketServerProcess(1);
+ auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
ASSERT_NE(binder, nullptr);
@@ -1097,28 +1185,34 @@
ssize_t beforeFds = countFds();
ASSERT_GE(beforeFds, 0);
{
- auto proc = createRpcTestSocketServerProcess(10);
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 10});
ASSERT_EQ(OK, proc.rootBinder->pingBinder());
}
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
static bool testSupportVsockLoopback() {
+ // We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
- sp<RpcServer> server = RpcServer::make();
+ sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make());
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
- CHECK(server->setupVsockServer(vsockPort));
+ if (status_t status = server->setupVsockServer(vsockPort); status != OK) {
+ if (status == -EAFNOSUPPORT) {
+ return false;
+ }
+ LOG_ALWAYS_FATAL("Could not setup vsock server: %s", statusToString(status).c_str());
+ }
server->start();
- sp<RpcSession> session = RpcSession::make();
- bool okay = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
- CHECK(server->shutdown());
- ALOGE("Detected vsock loopback supported: %d", okay);
- return okay;
+ sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
+ status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
+ while (!server->shutdown()) usleep(10000);
+ ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str());
+ return status == OK;
}
static std::vector<SocketType> testSocketTypes() {
- std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+ std::vector<SocketType> ret = {SocketType::PRECONNECTED, SocketType::UNIX, SocketType::INET};
static bool hasVsockLoopback = testSupportVsockLoopback();
@@ -1129,10 +1223,13 @@
return ret;
}
-INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn(testSocketTypes()),
- PrintSocketType);
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
+ ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
+ ::testing::ValuesIn(RpcSecurityValues())),
+ BinderRpc::PrintParamInfo);
-class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+class BinderRpcServerRootObject
+ : public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
TEST_P(BinderRpcServerRootObject, WeakRootObject) {
using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
@@ -1140,8 +1237,8 @@
return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
};
- auto server = RpcServer::make();
- auto [isStrong1, isStrong2] = GetParam();
+ auto [isStrong1, isStrong2, rpcSecurity] = GetParam();
+ auto server = RpcServer::make(newFactory(rpcSecurity));
auto binder1 = sp<BBinder>::make();
IBinder* binderRaw1 = binder1.get();
setRootObject(isStrong1)(server.get(), binder1);
@@ -1158,7 +1255,8 @@
}
INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
- ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+ ::testing::Combine(::testing::Bool(), ::testing::Bool(),
+ ::testing::ValuesIn(RpcSecurityValues())));
class OneOffSignal {
public:
@@ -1181,12 +1279,12 @@
bool mValue = false;
};
-TEST(BinderRpc, Shutdown) {
+TEST_P(BinderRpcSimple, Shutdown) {
auto addr = allocateSocketAddress();
unlink(addr.c_str());
- auto server = RpcServer::make();
+ auto server = RpcServer::make(newFactory(GetParam()));
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
- ASSERT_TRUE(server->setupUnixDomainServer(addr.c_str()));
+ ASSERT_EQ(OK, 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
@@ -1208,6 +1306,46 @@
<< "After server->shutdown() returns true, join() did not stop after 2s";
}
+TEST(BinderRpc, Java) {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on"
+ "createRpcDelegateServiceManager() with a device attached, such test belongs "
+ "to binderHostDeviceTest. Hence, just disable this test on host.";
+#endif // !__ANDROID__
+ sp<IServiceManager> sm = defaultServiceManager();
+ ASSERT_NE(nullptr, sm);
+ // Any Java service with non-empty getInterfaceDescriptor() would do.
+ // Let's pick batteryproperties.
+ auto binder = sm->checkService(String16("batteryproperties"));
+ ASSERT_NE(nullptr, binder);
+ auto descriptor = binder->getInterfaceDescriptor();
+ ASSERT_GE(descriptor.size(), 0);
+ ASSERT_EQ(OK, binder->pingBinder());
+
+ auto rpcServer = RpcServer::make();
+ rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ unsigned int port;
+ ASSERT_EQ(OK, rpcServer->setupInetServer(kLocalInetAddress, 0, &port));
+ auto socket = rpcServer->releaseServer();
+
+ auto keepAlive = sp<BBinder>::make();
+ ASSERT_EQ(OK, binder->setRpcClientDebug(std::move(socket), keepAlive));
+
+ auto rpcSession = RpcSession::make();
+ ASSERT_EQ(OK, rpcSession->setupInetClient("127.0.0.1", port));
+ auto rpcBinder = rpcSession->getRootObject();
+ ASSERT_NE(nullptr, rpcBinder);
+
+ ASSERT_EQ(OK, rpcBinder->pingBinder());
+
+ ASSERT_EQ(descriptor, rpcBinder->getInterfaceDescriptor())
+ << "getInterfaceDescriptor should not crash system_server";
+ ASSERT_EQ(OK, rpcBinder->pingBinder());
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()),
+ BinderRpcSimple::PrintTestParam);
+
} // namespace android
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 74b8eb8..acf3f8f 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -23,7 +23,6 @@
"main.cpp",
"random_fd.cpp",
"random_parcel.cpp",
- "util.cpp",
],
static_libs: [
"libbase",
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 5f2c17c..9fe06dd 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -18,11 +18,13 @@
#include "binder.h"
#include "util.h"
+#include <android-base/hex.h>
#include <android/os/IServiceManager.h>
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
using ::android::status_t;
+using ::android::base::HexString;
enum ByteEnum : int8_t {};
enum IntEnum : int32_t {};
@@ -128,7 +130,7 @@
[] (const ::android::Parcel& p, uint8_t len) {
FUZZ_LOG() << "about to readInplace";
const void* r = p.readInplace(len);
- FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << hexString(r, len);
+ FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << (r ? HexString(r, len) : "null");
},
PARCEL_READ_OPT_STATUS(int32_t, readInt32),
PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
@@ -156,7 +158,7 @@
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
const char16_t* str = p.readString16Inplace(&outLen);
- FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen)
+ FUZZ_LOG() << "readString16Inplace: " << HexString(str, sizeof(char16_t) * outLen)
<< " size: " << outLen;
},
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
index 35b5ebc..ee9840f 100644
--- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
@@ -18,10 +18,12 @@
#include "hwbinder.h"
#include "util.h"
+#include <android-base/hex.h>
#include <android-base/logging.h>
#include <hwbinder/Parcel.h>
using ::android::status_t;
+using ::android::base::HexString;
// TODO: support scatter-gather types
@@ -70,13 +72,13 @@
FUZZ_LOG() << "about to read";
std::vector<uint8_t> data (length);
status_t status = p.read(data.data(), length);
- FUZZ_LOG() << "read status: " << status << " data: " << hexString(data.data(), data.size());
+ FUZZ_LOG() << "read status: " << status << " data: " << HexString(data.data(), data.size());
},
[] (const ::android::hardware::Parcel& p, uint8_t length) {
FUZZ_LOG() << "about to read";
std::vector<uint8_t> data (length);
const void* inplace = p.readInplace(length);
- FUZZ_LOG() << "read status: " << hexString(inplace, length);
+ FUZZ_LOG() << "read status: " << (inplace ? HexString(inplace, length) : "null");
},
PARCEL_READ_WITH_STATUS(int8_t, readInt8),
PARCEL_READ_WITH_STATUS(uint8_t, readUint8),
@@ -100,7 +102,7 @@
FUZZ_LOG() << "about to readString16Inplace";
size_t outSize = 0;
const char16_t* str = p.readString16Inplace(&outSize);
- FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outSize);
+ FUZZ_LOG() << "readString16Inplace: " << HexString(str, sizeof(char16_t) * outSize);
},
PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index 2a79e85..f435dae 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -22,6 +22,7 @@
#include <iostream>
+#include <android-base/hex.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
@@ -35,6 +36,7 @@
using android::fillRandomParcel;
using android::sp;
+using android::base::HexString;
void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
// TODO: functionality to create random parcels for libhwbinder parcels
@@ -78,8 +80,8 @@
CHECK(reads.size() <= 255) << reads.size();
FUZZ_LOG() << "backend: " << backend;
- FUZZ_LOG() << "input: " << hexString(p.data(), p.dataSize());
- FUZZ_LOG() << "instructions: " << hexString(instructions);
+ FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
+ FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size());
for (size_t i = 0; i + 1 < instructions.size(); i += 2) {
uint8_t a = instructions[i];
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 92fdc72..8bf04cc 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <binder/IServiceManager.h>
#include <binder/RpcSession.h>
+#include <binder/RpcTransportRaw.h>
#include <fuzzbinder/random_fd.h>
#include <utils/String16.h>
@@ -35,8 +36,8 @@
void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
if (provider.ConsumeBool()) {
- auto session = sp<RpcSession>::make();
- CHECK(session->addNullDebuggingClient());
+ auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
+ CHECK_EQ(OK, session->addNullDebuggingClient());
p->markForRpc(session);
fillRandomParcelData(p, std::move(provider));
return;
diff --git a/libs/binder/tests/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp
deleted file mode 100644
index 479f406..0000000
--- a/libs/binder/tests/parcel_fuzzer/util.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 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 FUZZ_LOG_TAG "util"
-#include "util.h"
-
-#include <android-base/logging.h>
-
-#include <iomanip>
-#include <sstream>
-
-std::string hexString(const void* bytes, size_t len) {
- if (bytes == nullptr) return "<null>";
-
- const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
- char chars[] = "0123456789abcdef";
- std::string result;
- result.resize(len * 2);
-
- for (size_t i = 0; i < len; i++) {
- result[2 * i] = chars[bytes8[i] >> 4];
- result[2 * i + 1] = chars[bytes8[i] & 0xf];
- }
-
- return result;
-}
-std::string hexString(const std::vector<uint8_t>& bytes) {
- return hexString(bytes.data(), bytes.size());
-}
diff --git a/libs/binder/tests/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h
index 45e8c57..a5d0dae 100644
--- a/libs/binder/tests/parcel_fuzzer/util.h
+++ b/libs/binder/tests/parcel_fuzzer/util.h
@@ -49,6 +49,3 @@
FuzzLog& log() { return *this; }
};
#endif
-
-std::string hexString(const void* bytes, size_t len);
-std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index 9fc496f..230f5c7 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -60,7 +60,7 @@
sp<RpcServer> server = RpcServer::make();
server->setRootObject(sp<SomeBinder>::make());
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
- CHECK(server->setupUnixDomainServer(kSock.c_str()));
+ CHECK_EQ(OK, server->setupUnixDomainServer(kSock.c_str()));
std::thread serverThread([=] { (void)server->join(); });
diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
index 626b758..4a0aeba 100644
--- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
@@ -62,20 +62,22 @@
object = fdp->ConsumeIntegral<uint32_t>();
cleanup_cookie = fdp->ConsumeIntegral<uint32_t>();
IBinder::object_cleanup_func func = IBinder::object_cleanup_func();
- ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
- : nullptr,
- fdp->ConsumeBool() ? reinterpret_cast<void*>(&object) : nullptr,
- fdp->ConsumeBool() ? reinterpret_cast<void*>(&cleanup_cookie)
- : nullptr,
- func);
+ (void)ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
+ : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&object)
+ : nullptr,
+ fdp->ConsumeBool()
+ ? reinterpret_cast<void*>(&cleanup_cookie)
+ : nullptr,
+ func);
},
[](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
uint32_t id = fdp->ConsumeIntegral<uint32_t>();
- ibinder->findObject(reinterpret_cast<void*>(&id));
+ (void)ibinder->findObject(reinterpret_cast<void*>(&id));
},
[](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
uint32_t id = fdp->ConsumeIntegral<uint32_t>();
- ibinder->detachObject(reinterpret_cast<void*>(&id));
+ (void)ibinder->detachObject(reinterpret_cast<void*>(&id));
},
[](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
uint32_t code = fdp->ConsumeIntegral<uint32_t>();
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
index b435dba..d086b49 100644
--- a/libs/binderdebug/BinderDebug.cpp
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <binder/Binder.h>
@@ -116,4 +117,60 @@
return ret;
}
+status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid,
+ int32_t handle, std::vector<pid_t>* pids) {
+ std::smatch match;
+ static const std::regex kNodeNumber("^\\s+ref \\d+:\\s+desc\\s+(\\d+)\\s+node\\s+(\\d+).*");
+ std::string contextStr = contextToString(context);
+ int32_t node;
+ status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
+ if (std::regex_search(line, match, kNodeNumber)) {
+ const std::string& descString = match.str(1);
+ int32_t desc;
+ if (!::android::base::ParseInt(descString.c_str(), &desc)) {
+ LOG(ERROR) << "Failed to parse desc int: " << descString;
+ return;
+ }
+ if (handle != desc) {
+ return;
+ }
+ const std::string& nodeString = match.str(2);
+ if (!::android::base::ParseInt(nodeString.c_str(), &node)) {
+ LOG(ERROR) << "Failed to parse node int: " << nodeString;
+ return;
+ }
+ return;
+ }
+ return;
+ });
+ if (ret != OK) {
+ return ret;
+ }
+ static const std::regex kClients("^\\s+node\\s+(\\d+).*proc\\s+([\\d+\\s*]*)");
+ ret = scanBinderContext(servicePid, contextStr, [&](const std::string& line) {
+ if (std::regex_search(line, match, kClients)) {
+ const std::string nodeString = match.str(1);
+ int32_t matchedNode;
+ if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
+ LOG(ERROR) << "Failed to parse node int: " << nodeString;
+ return;
+ }
+ if (node != matchedNode) {
+ return;
+ }
+ const std::string clients = match.str(2);
+ for (const std::string& pidStr : base::Split(clients, " ")) {
+ int32_t pid;
+ if (!::android::base::ParseInt(pidStr, &pid)) {
+ return;
+ }
+ pids->push_back(pid);
+ }
+ return;
+ }
+ return;
+ });
+ return ret;
+}
+
} // namespace android
diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h
index 14a0ef3..dfd5a7c 100644
--- a/libs/binderdebug/include/binderdebug/BinderDebug.h
+++ b/libs/binderdebug/include/binderdebug/BinderDebug.h
@@ -32,6 +32,14 @@
VNDBINDER,
};
+/**
+ * pid is the pid of the service
+ */
status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo);
+/**
+ * pid is typically the pid of this process that is making the query
+ */
+status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid,
+ int32_t handle, std::vector<pid_t>* pids);
} // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 3030068..de5f1ed 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -643,6 +643,8 @@
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
return 4;
+ case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+ return 8;
default:
return 0;
}
@@ -686,6 +688,10 @@
"gralloc and AHardwareBuffer flags don't match");
static_assert(AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE == (uint64_t)BufferUsage::GPU_MIPMAP_COMPLETE,
"gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CAMERA_WRITE == (uint64_t)BufferUsage::CAMERA_OUTPUT,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CAMERA_READ == (uint64_t)BufferUsage::CAMERA_INPUT,
+ "gralloc and AHardwareBuffer flags don't match");
return usage;
}
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 3392d7f..12f8691 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -81,6 +81,20 @@
AHARDWAREBUFFER_FORMAT_YCbCr_422_I = 0x14,
};
+/**
+ * Buffer usage flags.
+ */
+enum {
+ /* for future proofing, keep these in sync with hardware/gralloc.h */
+
+ /* The buffer will be written by the HW camera pipeline. */
+ AHARDWAREBUFFER_USAGE_CAMERA_WRITE = 2UL << 16,
+ /* The buffer will be read by the HW camera pipeline. */
+ AHARDWAREBUFFER_USAGE_CAMERA_READ = 4UL << 16,
+ /* Mask for the camera access values. */
+ AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16,
+};
+
__END_DECLS
#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
index c75213b..16e1e8a 100644
--- a/services/gpuservice/bpfprogs/gpu_mem.c
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -72,4 +72,4 @@
return 0;
}
-char _license[] SEC("license") = "Apache 2.0";
+LICENSE("Apache 2.0");
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index dadbd0e..9846640 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -90,4 +90,7 @@
export_header_lib_headers: [
"libinputreader_headers",
],
+ static_libs: [
+ "libc++fs"
+ ],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a1514af..20a843f 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -34,20 +34,20 @@
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
-
-#include "EventHub.h"
-
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
+#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/VirtualKeyMap.h>
#include <openssl/sha.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/threads.h>
-#include <input/KeyCharacterMap.h>
-#include <input/KeyLayoutMap.h>
-#include <input/VirtualKeyMap.h>
+#include <filesystem>
+
+#include "EventHub.h"
/* this macro is used to tell if "bit" is set in "array"
* it selects a byte from the array, and does a boolean AND
@@ -69,9 +69,9 @@
static constexpr bool DEBUG = false;
-static const char* DEVICE_PATH = "/dev/input";
+static const char* DEVICE_INPUT_PATH = "/dev/input";
// v4l2 devices go directly into /dev
-static const char* VIDEO_DEVICE_PATH = "/dev";
+static const char* DEVICE_PATH = "/dev";
static inline const char* toString(bool value) {
return value ? "true" : "false";
@@ -94,8 +94,8 @@
/**
* Return true if name matches "v4l-touch*"
*/
-static bool isV4lTouchNode(const char* name) {
- return strstr(name, "v4l-touch") == name;
+static bool isV4lTouchNode(std::string name) {
+ return name.find("v4l-touch") != std::string::npos;
}
/**
@@ -298,15 +298,23 @@
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
mINotifyFd = inotify_init();
- mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
- LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
- strerror(errno));
- if (isV4lScanningEnabled()) {
- mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
- LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
- VIDEO_DEVICE_PATH, strerror(errno));
+
+ std::error_code errorCode;
+ bool isDeviceInotifyAdded = false;
+ if (std::filesystem::exists(DEVICE_INPUT_PATH, errorCode)) {
+ addDeviceInputInotify();
} else {
- mVideoWd = -1;
+ addDeviceInotify();
+ isDeviceInotifyAdded = true;
+ if (errorCode) {
+ ALOGW("Could not run filesystem::exists() due to error %d : %s.", errorCode.value(),
+ errorCode.message().c_str());
+ }
+ }
+
+ if (isV4lScanningEnabled() && !isDeviceInotifyAdded) {
+ addDeviceInotify();
+ } else {
ALOGI("Video device scanning disabled");
}
@@ -352,6 +360,23 @@
::close(mWakeWritePipeFd);
}
+/**
+ * On devices that don't have any input devices (like some development boards), the /dev/input
+ * directory will be absent. However, the user may still plug in an input device at a later time.
+ * Add watch for contents of /dev/input only when /dev/input appears.
+ */
+void EventHub::addDeviceInputInotify() {
+ mDeviceInputWd = inotify_add_watch(mINotifyFd, DEVICE_INPUT_PATH, IN_DELETE | IN_CREATE);
+ LOG_ALWAYS_FATAL_IF(mDeviceInputWd < 0, "Could not register INotify for %s: %s",
+ DEVICE_INPUT_PATH, strerror(errno));
+}
+
+void EventHub::addDeviceInotify() {
+ mDeviceWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
+ LOG_ALWAYS_FATAL_IF(mDeviceWd < 0, "Could not register INotify for %s: %s",
+ DEVICE_PATH, strerror(errno));
+}
+
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
@@ -810,7 +835,7 @@
return index >= 0 ? mDevices.valueAt(index) : NULL;
}
-EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
+EventHub::Device* EventHub::getDeviceByPathLocked(const std::string& devicePath) const {
for (size_t i = 0; i < mDevices.size(); i++) {
Device* device = mDevices.valueAt(i);
if (device->path == devicePath) {
@@ -1109,14 +1134,24 @@
}
void EventHub::scanDevicesLocked() {
- status_t result = scanDirLocked(DEVICE_PATH);
- if (result < 0) {
- ALOGE("scan dir failed for %s", DEVICE_PATH);
+ status_t result;
+ std::error_code errorCode;
+
+ if (std::filesystem::exists(DEVICE_INPUT_PATH, errorCode)) {
+ result = scanDirLocked(DEVICE_INPUT_PATH);
+ if (result < 0) {
+ ALOGE("scan dir failed for %s", DEVICE_INPUT_PATH);
+ }
+ } else {
+ if (errorCode) {
+ ALOGW("Could not run filesystem::exists() due to error %d : %s.", errorCode.value(),
+ errorCode.message().c_str());
+ }
}
if (isV4lScanningEnabled()) {
- result = scanVideoDirLocked(VIDEO_DEVICE_PATH);
+ result = scanVideoDirLocked(DEVICE_PATH);
if (result != OK) {
- ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
+ ALOGE("scan video dir failed for %s", DEVICE_PATH);
}
}
if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) {
@@ -1215,14 +1250,14 @@
}
}
-status_t EventHub::openDeviceLocked(const char* devicePath) {
+status_t EventHub::openDeviceLocked(const std::string& devicePath) {
char buffer[80];
- ALOGV("Opening device: %s", devicePath);
+ ALOGV("Opening device: %s", devicePath.c_str());
- int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+ int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
- ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
return -1;
}
@@ -1230,7 +1265,7 @@
// Get device name.
if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
- ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
+ ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name = buffer;
@@ -1240,7 +1275,7 @@
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const std::string& item = mExcludedDevices[i];
if (identifier.name == item) {
- ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
+ ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
return -1;
}
@@ -1249,7 +1284,7 @@
// Get device driver version.
int driverVersion;
if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
- ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
return -1;
}
@@ -1257,7 +1292,7 @@
// Get device identifier.
struct input_id inputId;
if (ioctl(fd, EVIOCGID, &inputId)) {
- ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
return -1;
}
@@ -1289,7 +1324,7 @@
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, devicePath, identifier);
- ALOGV("add device %d: %s\n", deviceId, devicePath);
+ ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
@@ -1446,7 +1481,7 @@
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
- ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+ ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
device->identifier.name.c_str());
delete device;
return -1;
@@ -1492,7 +1527,7 @@
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
- deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
+ deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(), device->classes,
device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
@@ -1724,13 +1759,13 @@
return NAME_NOT_FOUND;
}
-void EventHub::closeDeviceByPathLocked(const char* devicePath) {
+void EventHub::closeDeviceByPathLocked(const std::string& devicePath) {
Device* device = getDeviceByPathLocked(devicePath);
if (device) {
closeDeviceLocked(device);
return;
}
- ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
+ ALOGV("Remove device: %s not found, device may already have been removed.", devicePath.c_str());
}
/**
@@ -1834,23 +1869,25 @@
while (res >= (int)sizeof(*event)) {
event = (struct inotify_event*)(event_buf + event_pos);
if (event->len) {
- if (event->wd == mInputWd) {
- std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+ if (event->wd == mDeviceInputWd) {
+ std::string filename = std::string(DEVICE_INPUT_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
- openDeviceLocked(filename.c_str());
+ openDeviceLocked(filename);
} else {
ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
- closeDeviceByPathLocked(filename.c_str());
+ closeDeviceByPathLocked(filename);
}
- } else if (event->wd == mVideoWd) {
+ } else if (event->wd == mDeviceWd) {
if (isV4lTouchNode(event->name)) {
- std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+ std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
openVideoDeviceLocked(filename);
} else {
ALOGI("Removing video device '%s' due to inotify event", filename.c_str());
closeVideoDeviceByPathLocked(filename);
}
+ } else if (strcmp(event->name, "input") == 0 && event->mask & IN_CREATE ) {
+ addDeviceInputInotify();
}
} else {
LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
@@ -1863,24 +1900,10 @@
return 0;
}
-status_t EventHub::scanDirLocked(const char* dirname) {
- char devname[PATH_MAX];
- char* filename;
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname);
- if (dir == nullptr) return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while ((de = readdir(dir))) {
- if (de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- continue;
- strcpy(filename, de->d_name);
- openDeviceLocked(devname);
+status_t EventHub::scanDirLocked(const std::string& dirname) {
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ openDeviceLocked(entry.path());
}
- closedir(dir);
return 0;
}
@@ -1888,22 +1911,12 @@
* Look for all dirname/v4l-touch* devices, and open them.
*/
status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname.c_str());
- if (!dir) {
- ALOGE("Could not open video directory %s", dirname.c_str());
- return BAD_VALUE;
- }
-
- while ((de = readdir(dir))) {
- const char* name = de->d_name;
- if (isV4lTouchNode(name)) {
- ALOGI("Found touch video device %s", name);
- openVideoDeviceLocked(dirname + "/" + name);
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ if (isV4lTouchNode(entry.path())) {
+ ALOGI("Found touch video device %s", entry.path().c_str());
+ openVideoDeviceLocked(entry.path());
}
}
- closedir(dir);
return OK;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index f5451d7..55f658e 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -373,13 +373,13 @@
}
};
- status_t openDeviceLocked(const char* devicePath);
+ status_t openDeviceLocked(const std::string& devicePath);
void openVideoDeviceLocked(const std::string& devicePath);
void createVirtualKeyboardLocked();
void addDeviceLocked(Device* device);
void assignDescriptorLocked(InputDeviceIdentifier& identifier);
- void closeDeviceByPathLocked(const char* devicePath);
+ void closeDeviceByPathLocked(const std::string& devicePath);
void closeVideoDeviceByPathLocked(const std::string& devicePath);
void closeDeviceLocked(Device* device);
void closeAllDevicesLocked();
@@ -396,14 +396,14 @@
status_t unregisterDeviceFromEpollLocked(Device* device);
void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
- status_t scanDirLocked(const char* dirname);
+ status_t scanDirLocked(const std::string& dirname);
status_t scanVideoDirLocked(const std::string& dirname);
void scanDevicesLocked();
status_t readNotifyLocked();
Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
Device* getDeviceLocked(int32_t deviceId) const;
- Device* getDeviceByPathLocked(const char* devicePath) const;
+ Device* getDeviceByPathLocked(const std::string& devicePath) const;
/**
* Look through all available fd's (both for input devices and for video devices),
* and return the device pointer.
@@ -426,6 +426,9 @@
status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const;
void setLedStateLocked(Device* device, int32_t led, bool on);
+ void addDeviceInputInotify();
+ void addDeviceInotify();
+
// Protect all internal state.
mutable Mutex mLock;
@@ -465,8 +468,8 @@
int mWakeReadPipeFd;
int mWakeWritePipeFd;
- int mInputWd;
- int mVideoWd;
+ int mDeviceInputWd;
+ int mDeviceWd = -1;
// Maximum number of signalled FDs to handle at a time.
static const int EPOLL_MAX_EVENTS = 16;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 86ed60d..24a27cf 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -46,5 +46,8 @@
"InputReader_test.cpp",
"UinputDevice.cpp",
],
+ static_libs: [
+ "libc++fs"
+ ],
require_root: true,
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 6b3bf8d..4e84e52 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -144,6 +144,7 @@
"DisplayHardware/ComposerHal.cpp",
"DisplayHardware/DisplayIdentification.cpp",
"DisplayHardware/FramebufferSurface.cpp",
+ "DisplayHardware/Hash.cpp",
"DisplayHardware/HWC2.cpp",
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/PowerAdvisor.cpp",
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 4dfc743..20f3fd2 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -29,6 +29,7 @@
#include <log/log.h>
#include "DisplayIdentification.h"
+#include "Hash.h"
namespace android {
namespace {
@@ -281,8 +282,9 @@
}
// Hash model string instead of using product code or (integer) serial number, since the latter
- // have been observed to change on some displays with multiple inputs.
- const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+ // have been observed to change on some displays with multiple inputs. Use a stable hash instead
+ // of std::hash which is only required to be same within a single execution of a program.
+ const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString));
// Parse extension blocks.
std::optional<Cea861ExtensionBlock> cea861Block;
diff --git a/services/surfaceflinger/DisplayHardware/Hash.cpp b/services/surfaceflinger/DisplayHardware/Hash.cpp
new file mode 100644
index 0000000..6056c8d
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/Hash.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DisplayIdentification"
+
+#include <cstring>
+#include <type_traits>
+
+#include <log/log.h>
+
+#include "Hash.h"
+
+namespace android {
+namespace {
+
+template <class T>
+inline T load(const void* p) {
+ static_assert(std::is_integral<T>::value, "T must be integral");
+
+ T r;
+ std::memcpy(&r, p, sizeof(r));
+ return r;
+}
+
+uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
+ return (val >> shift) | (val << (64 - shift));
+}
+
+uint64_t shiftMix(uint64_t val) {
+ return val ^ (val >> 47);
+}
+
+uint64_t hash64Len16(uint64_t u, uint64_t v) {
+ constexpr uint64_t kMul = 0x9ddfea08eb382d69;
+ uint64_t a = (u ^ v) * kMul;
+ a ^= (a >> 47);
+ uint64_t b = (v ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+uint64_t hash64Len0To16(const char* s, uint64_t len) {
+ constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
+ constexpr uint64_t k3 = 0xc949d7c7509e6557;
+
+ if (len > 8) {
+ const uint64_t a = load<uint64_t>(s);
+ const uint64_t b = load<uint64_t>(s + len - 8);
+ return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
+ }
+ if (len >= 4) {
+ const uint32_t a = load<uint32_t>(s);
+ const uint32_t b = load<uint32_t>(s + len - 4);
+ return hash64Len16(len + (a << 3), b);
+ }
+ if (len > 0) {
+ const unsigned char a = static_cast<unsigned char>(s[0]);
+ const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
+ const unsigned char c = static_cast<unsigned char>(s[len - 1]);
+ const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
+ const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
+ return shiftMix(y * k2 ^ z * k3) * k2;
+ }
+ return k2;
+}
+
+} // namespace
+
+uint64_t cityHash64Len0To16(std::string_view sv) {
+ auto len = sv.length();
+ if (len > 16) {
+ ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
+ len = 16;
+ }
+ return hash64Len0To16(sv.data(), len);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/Hash.h b/services/surfaceflinger/DisplayHardware/Hash.h
new file mode 100644
index 0000000..a7b6c71
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/Hash.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string_view>
+
+namespace android {
+
+// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
+uint64_t cityHash64Len0To16(std::string_view sv);
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 2a0e913..300ef26 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/Hash.h"
namespace android {
namespace {
@@ -128,7 +129,7 @@
}
uint32_t hash(const char* str) {
- return static_cast<uint32_t>(std::hash<std::string_view>()(str));
+ return static_cast<uint32_t>(cityHash64Len0To16(str));
}
} // namespace
@@ -303,9 +304,9 @@
ASSERT_TRUE(tertiaryInfo);
// Display IDs should be unique.
- EXPECT_NE(primaryInfo->id, secondaryInfo->id);
- EXPECT_NE(primaryInfo->id, tertiaryInfo->id);
- EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
+ EXPECT_EQ(21571479025788672, primaryInfo->id.value);
+ EXPECT_EQ(9834267132873217, secondaryInfo->id.value);
+ EXPECT_EQ(21441883803501570, tertiaryInfo->id.value);
}
TEST(DisplayIdentificationTest, deviceProductInfo) {
diff --git a/vulkan/README.md b/vulkan/README.md
index 185aa39..144805c 100644
--- a/vulkan/README.md
+++ b/vulkan/README.md
@@ -14,7 +14,7 @@
## Code Generation
-We generate several parts of the loader and tools driectly from the Vulkan Registry (external/vulkan-headers/registry/vk.xml). Code generation must be done manually because the generator is not part of the platform toolchain (yet?). Files named `foo_gen.*` are generated by the code generator.
+We generate several parts of the loader and tools directly from the Vulkan Registry (external/vulkan-headers/registry/vk.xml). Code generation must be done manually because the generator is not part of the platform toolchain (yet?). Files named `foo_gen.*` are generated by the code generator.
### Run The Code Generator