Merge "Predefine U_IN_DOXYGEN in NDK Doxyfile"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 37fc9a9..e3c4ede 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -266,7 +266,10 @@
chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace
chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace
-on post-fs-data
+# Only create the tracing instance if persist.mm_events.enabled
+# Attempting to remove the tracing instance after it has been created
+# will likely fail with EBUSY as it would be in use by traced_probes.
+on post-fs-data && property:persist.mm_events.enabled=true
# Create MM Events Tracing Instance for Kmem Activity Trigger
mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system
mkdir /sys/kernel/tracing/instances/mm_events 0755 system system
@@ -275,10 +278,18 @@
chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb
chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb
+# Set the default buffer size to the minimum
+ write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1
+ write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1
+
# Read and enable tracing
chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on
chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on
+# Tracing disabled by default
+ write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0
+ write /sys/kernel/tracing/instances/mm_events/tracing_on 0
+
# Read and truncate kernel trace
chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace
chmod 0666 /sys/kernel/tracing/instances/mm_events/trace
diff --git a/cmds/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp
new file mode 100644
index 0000000..0c78c5a
--- /dev/null
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_fuzz {
+ name: "cmd_fuzzer",
+ srcs: [
+ "cmd_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libcmd",
+ "libutils",
+ "liblog",
+ "libselinux",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
diff --git a/cmds/cmd/fuzzer/README.md b/cmds/cmd/fuzzer/README.md
new file mode 100644
index 0000000..db37ece
--- /dev/null
+++ b/cmds/cmd/fuzzer/README.md
@@ -0,0 +1,51 @@
+# Fuzzer for libcmd_fuzzer
+
+## Plugin Design Considerations
+The fuzzer plugin for libcmd is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libcmd supports the following parameters:
+1. In (parameter name: `in`)
+2. Out (parameter name: `out`)
+3. Err (parameter name: `err`)
+4. Run Mode (parameter name: `runMode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `in` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `out` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `err` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `runMode` | 1.`RunMode::kStandalone` 2. `RunMode::kLibrary` | Value chosen from valid values using FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the cmd module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build cmd_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) cmd_fuzzer
+```
+#### Steps to run
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/${TARGET_ARCH}/cmd_fuzzer/cmd_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/cmds/cmd/fuzzer/cmd_fuzzer.cpp b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
new file mode 100644
index 0000000..ab514a1
--- /dev/null
+++ b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/TextOutput.h>
+#include <cmd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace std;
+using namespace android;
+
+class TestTextOutput : public TextOutput {
+public:
+ TestTextOutput() {}
+ virtual ~TestTextOutput() {}
+
+ virtual status_t print(const char* /*txt*/, size_t /*len*/) { return NO_ERROR; }
+ virtual void moveIndent(int /*delta*/) { return; }
+ virtual void pushBundle() { return; }
+ virtual void popBundle() { return; }
+};
+
+class CmdFuzzer {
+public:
+ void process(const uint8_t* data, size_t size);
+
+private:
+ FuzzedDataProvider* mFDP = nullptr;
+};
+
+void CmdFuzzer::process(const uint8_t* data, size_t size) {
+ mFDP = new FuzzedDataProvider(data, size);
+ vector<string> arguments;
+ if (mFDP->ConsumeBool()) {
+ if (mFDP->ConsumeBool()) {
+ arguments = {"-w", "media.aaudio"};
+ } else {
+ arguments = {"-l"};
+ }
+ } else {
+ while (mFDP->remaining_bytes() > 0) {
+ size_t sizestr = mFDP->ConsumeIntegralInRange<size_t>(1, mFDP->remaining_bytes());
+ string argument = mFDP->ConsumeBytesAsString(sizestr);
+ arguments.emplace_back(argument);
+ }
+ }
+ vector<string_view> argSV;
+ for (auto& argument : arguments) {
+ argSV.emplace_back(argument.c_str());
+ }
+ int32_t in = open("/dev/null", O_RDWR | O_CREAT);
+ int32_t out = open("/dev/null", O_RDWR | O_CREAT);
+ int32_t err = open("/dev/null", O_RDWR | O_CREAT);
+ TestTextOutput output;
+ TestTextOutput error;
+ RunMode runMode = mFDP->ConsumeBool() ? RunMode::kStandalone : RunMode::kLibrary;
+ cmdMain(argSV, output, error, in, out, err, runMode);
+ delete mFDP;
+ close(in);
+ close(out);
+ close(err);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ CmdFuzzer cmdFuzzer;
+ cmdFuzzer.process(data, size);
+ return 0;
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8f466ca..1db2867 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1236,7 +1236,7 @@
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;
@@ -1315,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..f8fdaa0 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -62,13 +62,14 @@
"usage: dumpsys\n"
" To dump all services.\n"
"or:\n"
- " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | "
- "--skip SERVICES "
+ " dumpsys [-t TIMEOUT] [--priority LEVEL] [--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"
+ " --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"
@@ -127,10 +128,11 @@
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},
+ {"dump", no_argument, 0, 0},
{"pid", no_argument, 0, 0},
{"priority", required_argument, 0, 0},
{"proto", no_argument, 0, 0},
@@ -168,12 +170,14 @@
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;
}
break;
@@ -211,6 +215,10 @@
}
}
+ if (dumpTypeFlags == 0) {
+ dumpTypeFlags = TYPE_DUMP;
+ }
+
for (int i = optind; i < argc; i++) {
if (skipServices) {
skippedServices.add(String16(argv[i]));
@@ -263,7 +271,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 +338,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 +373,14 @@
return OK;
}
-status_t Dumpsys::startDumpThread(Type type, const String16& serviceName,
+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 +401,23 @@
// 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 (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..05c5d5e 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -51,24 +51,25 @@
*/
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
};
/**
* 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..312f4d7 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,28 @@
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/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/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3099296..765e21c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -261,15 +261,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/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/RpcServer.cpp b/libs/binder/RpcServer.cpp
index c6cf2c5..62ea187 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -110,6 +110,10 @@
return mMaxThreads;
}
+void RpcServer::setProtocolVersion(uint32_t version) {
+ mProtocolVersion = version;
+}
+
void RpcServer::setRootObject(const sp<IBinder>& binder) {
std::lock_guard<std::mutex> _l(mLock);
mRootObjectWeak = mRootObject = binder;
@@ -245,13 +249,37 @@
RpcConnectionHeader header;
status_t status = server->mShutdownTrigger->interruptableReadFully(clientFd.get(), &header,
sizeof(header));
- bool idValid = status == OK;
- if (!idValid) {
+ if (status != OK) {
ALOGE("Failed to read ID for client connecting to RPC server: %s",
statusToString(status).c_str());
// still need to cleanup before we can return
}
- bool incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+
+ bool incoming = false;
+ uint32_t protocolVersion = 0;
+ RpcAddress sessionId = RpcAddress::zero();
+ bool requestingNewSession = false;
+
+ if (status == OK) {
+ incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+ protocolVersion = std::min(header.version,
+ server->mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION));
+ sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
+ requestingNewSession = sessionId.isZero();
+
+ if (requestingNewSession) {
+ RpcNewSessionResponse response{
+ .version = protocolVersion,
+ };
+
+ status = server->mShutdownTrigger->interruptableWriteFully(clientFd.get(), &response,
+ sizeof(response));
+ if (status != OK) {
+ ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
+ // still need to cleanup before we can return
+ }
+ }
+ }
std::thread thisThread;
sp<RpcSession> session;
@@ -269,19 +297,16 @@
};
server->mConnectingThreads.erase(threadId);
- if (!idValid || server->mShutdownTrigger->isTriggered()) {
+ if (status != OK || server->mShutdownTrigger->isTriggered()) {
return;
}
- RpcAddress sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
-
- if (sessionId.isZero()) {
+ if (requestingNewSession) {
if (incoming) {
ALOGE("Cannot create a new session with an incoming connection, would leak");
return;
}
- sessionId = RpcAddress::zero();
size_t tries = 0;
do {
// don't block if there is some entropy issue
@@ -295,6 +320,7 @@
session = RpcSession::make();
session->setMaxThreads(server->mMaxThreads);
+ if (!session->setProtocolVersion(protocolVersion)) return;
if (!session->setForServer(server,
sp<RpcServer::EventListener>::fromExisting(
static_cast<RpcServer::EventListener*>(
@@ -369,7 +395,7 @@
return true;
}
-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());
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c01a03d..90ce4d6 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -77,6 +77,25 @@
return mMaxThreads;
}
+bool RpcSession::setProtocolVersion(uint32_t version) {
+ if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
+ version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+ ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
+ "is %u).",
+ version, RPC_WIRE_PROTOCOL_VERSION);
+ return false;
+ }
+
+ std::lock_guard<std::mutex> _l(mMutex);
+ mProtocolVersion = version;
+ return true;
+}
+
+std::optional<uint32_t> RpcSession::getProtocolVersion() {
+ std::lock_guard<std::mutex> _l(mMutex);
+ return mProtocolVersion;
+}
+
bool RpcSession::setupUnixDomainClient(const char* path) {
return setupSocketClient(UnixSocketAddress(path));
}
@@ -132,6 +151,7 @@
if (wait) {
LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed");
mShutdownListener->waitForShutdown(_l);
+
LOG_ALWAYS_FATAL_IF(!mThreads.empty(), "Shutdown failed");
}
@@ -181,9 +201,7 @@
status_t RpcSession::FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
while (true) {
- pollfd pfd[]{{.fd = fd.get(),
- .events = static_cast<int16_t>(event | POLLHUP),
- .revents = 0},
+ pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
{.fd = mRead.get(), .events = POLLHUP, .revents = 0}};
int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
if (ret < 0) {
@@ -261,7 +279,7 @@
return OK;
}
-void RpcSession::WaitForShutdownListener::onSessionLockedAllIncomingThreadsEnded(
+void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded(
const sp<RpcSession>& session) {
(void)session;
mShutdown = true;
@@ -293,7 +311,13 @@
// be able to do nested calls (we can't only read from it)
sp<RpcConnection> connection = assignIncomingConnectionToThisThread(std::move(fd));
- 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),
@@ -360,6 +384,7 @@
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,
@@ -375,9 +400,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);
@@ -389,6 +411,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) {
@@ -415,6 +443,18 @@
if (!setupOneSocketConnection(addr, RpcAddress::zero(), false /*incoming*/)) return false;
+ {
+ ExclusiveConnection connection;
+ status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
+ ConnectionUse::CLIENT, &connection);
+ if (status != OK) return false;
+
+ uint32_t version;
+ status = state()->readNewSessionResponse(connection.get(),
+ sp<RpcSession>::fromExisting(this), &version);
+ if (!setProtocolVersion(version)) return false;
+ }
+
// TODO(b/189955605): we should add additional sessions dynamically
// instead of all at once.
// TODO(b/186470974): first risk of blocking
@@ -475,7 +515,10 @@
return false;
}
- RpcConnectionHeader header{.options = 0};
+ RpcConnectionHeader header{
+ .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
+ .options = 0,
+ };
memcpy(&header.sessionId, &id.viewRawEmbedded(), sizeof(RpcWireAddress));
if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
@@ -579,24 +622,35 @@
sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(unique_fd fd) {
std::lock_guard<std::mutex> _l(mMutex);
+
+ // 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->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 332c75f..f3406bb 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -315,6 +315,18 @@
return OK;
}
+status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, uint32_t* version) {
+ RpcNewSessionResponse response;
+ if (status_t status =
+ rpcRec(connection, session, "new session response", &response, sizeof(response));
+ status != OK) {
+ return status;
+ }
+ *version = response.version;
+ return OK;
+}
+
status_t RpcState::sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session) {
RpcOutgoingConnectionInit init{
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 5ac0b97..1446eec 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -60,6 +60,8 @@
RpcState();
~RpcState();
+ status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, uint32_t* version);
status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session);
status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 2a44c7a..0f8efd2 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -37,9 +37,20 @@
* either as part of a new session or an existing session
*/
struct RpcConnectionHeader {
+ uint32_t version; // maximum supported by caller
+ uint8_t reserver0[4];
RpcWireAddress sessionId;
uint8_t options;
- uint8_t reserved[7];
+ uint8_t reserved1[7];
+};
+
+/**
+ * In response to an RpcConnectionHeader which corresponds to a new session,
+ * this returns information to the server.
+ */
+struct RpcNewSessionResponse {
+ uint32_t version; // maximum supported by callee <= maximum supported by caller
+ uint8_t reserved[4];
};
#define RPC_CONNECTION_INIT_OKAY "cci"
diff --git a/libs/binder/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/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 8e46147..d152005 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -167,7 +167,8 @@
bool checkCallingPermission(const String16& permission);
bool checkCallingPermission(const String16& permission,
int32_t* outPid, int32_t* outUid);
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid,
+ bool logPermissionFailure = true);
#ifndef __ANDROID__
// Create an IServiceManager that delegates the service manager on the device via adb.
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index c8d2857..40ff78c 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -105,6 +105,13 @@
size_t getMaxThreads();
/**
+ * By default, the latest protocol version which is supported by a client is
+ * used. However, this can be used in order to prevent newer protocol
+ * versions from ever being used. This is expected to be useful for testing.
+ */
+ void setProtocolVersion(uint32_t version);
+
+ /**
* The root object can be retrieved by any client, without any
* authentication. TODO(b/183988761)
*
@@ -156,7 +163,7 @@
friend sp<RpcServer>;
RpcServer();
- 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);
@@ -164,6 +171,7 @@
bool mAgreedExperimental = false;
size_t mMaxThreads = 1;
+ std::optional<uint32_t> mProtocolVersion;
base::unique_fd mServer; // socket we are accepting sessions on
std::mutex mLock; // for below
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index fdca2a9..1f7c029 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -37,6 +37,10 @@
class RpcSocketAddress;
class RpcState;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 0;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+
/**
* This represents a session (group of connections) between a client
* and a server. Multiple connections are needed for multiple parallel "binder"
@@ -60,6 +64,13 @@
size_t getMaxThreads();
/**
+ * By default, the minimum of the supported versions of the client and the
+ * server will be used. Usually, this API should only be used for debugging.
+ */
+ [[nodiscard]] bool setProtocolVersion(uint32_t version);
+ std::optional<uint32_t> getProtocolVersion();
+
+ /**
* This should be called once per thread, matching 'join' in the remote
* process.
*/
@@ -177,19 +188,19 @@
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 {
@@ -291,12 +302,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/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/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/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index a457e67..5f4a7b5 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,21 +18,31 @@
#include <android-base/logging.h>
#include <benchmark/benchmark.h>
#include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
#include <thread>
+#include <signal.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
using android::BBinder;
+using android::defaultServiceManager;
using android::IBinder;
using android::interface_cast;
+using android::IPCThreadState;
+using android::IServiceManager;
using android::OK;
+using android::ProcessState;
using android::RpcServer;
using android::RpcSession;
using android::sp;
+using android::String16;
using android::binder::Status;
class MyBinderRpcBenchmark : public BnBinderRpcBenchmark {
@@ -46,28 +56,51 @@
}
};
-static sp<RpcSession> gSession = RpcSession::make();
+enum Transport {
+ KERNEL,
+ RPC,
+};
-void BM_getRootObject(benchmark::State& state) {
- while (state.KeepRunning()) {
- CHECK(gSession->getRootObject() != nullptr);
+static void EachTransport(benchmark::internal::Benchmark* b) {
+#ifdef __BIONIC__
+ b->Args({Transport::KERNEL});
+#endif
+ b->Args({Transport::RPC});
+}
+
+static sp<RpcSession> gSession = RpcSession::make();
+#ifdef __BIONIC__
+static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
+static sp<IBinder> gKernelBinder;
+#endif
+
+static sp<IBinder> getBinderForOptions(benchmark::State& state) {
+ Transport transport = static_cast<Transport>(state.range(0));
+ switch (transport) {
+#ifdef __BIONIC__
+ case KERNEL:
+ return gKernelBinder;
+#endif
+ case RPC:
+ return gSession->getRootObject();
+ default:
+ LOG(FATAL) << "Unknown transport value: " << transport;
+ return nullptr;
}
}
-BENCHMARK(BM_getRootObject);
void BM_pingTransaction(benchmark::State& state) {
- sp<IBinder> binder = gSession->getRootObject();
- CHECK(binder != nullptr);
+ sp<IBinder> binder = getBinderForOptions(state);
while (state.KeepRunning()) {
CHECK_EQ(OK, binder->pingBinder());
}
}
-BENCHMARK(BM_pingTransaction);
+BENCHMARK(BM_pingTransaction)->Apply(EachTransport);
void BM_repeatString(benchmark::State& state) {
- sp<IBinder> binder = gSession->getRootObject();
- CHECK(binder != nullptr);
+ sp<IBinder> binder = getBinderForOptions(state);
+
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -92,7 +125,7 @@
CHECK(ret.isOk()) << ret;
}
}
-BENCHMARK(BM_repeatString);
+BENCHMARK(BM_repeatString)->Apply(EachTransport);
void BM_repeatBinder(benchmark::State& state) {
sp<IBinder> binder = gSession->getRootObject();
@@ -109,7 +142,7 @@
CHECK(ret.isOk()) << ret;
}
}
-BENCHMARK(BM_repeatBinder);
+BENCHMARK(BM_repeatBinder)->Apply(EachTransport);
int main(int argc, char** argv) {
::benchmark::Initialize(&argc, argv);
@@ -118,13 +151,36 @@
std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
(void)unlink(addr.c_str());
- std::thread([addr]() {
+ std::cerr << "Tests suffixes:" << std::endl;
+ std::cerr << "\t\\" << Transport::KERNEL << " is KERNEL" << std::endl;
+ std::cerr << "\t\\" << Transport::RPC << " is RPC" << std::endl;
+
+ if (0 == fork()) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
sp<RpcServer> server = RpcServer::make();
server->setRootObject(sp<MyBinderRpcBenchmark>::make());
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK(server->setupUnixDomainServer(addr.c_str()));
server->join();
- }).detach();
+ exit(1);
+ }
+
+#ifdef __BIONIC__
+ if (0 == fork()) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
+ CHECK_EQ(OK,
+ defaultServiceManager()->addService(kKernelBinderInstance,
+ sp<MyBinderRpcBenchmark>::make()));
+ IPCThreadState::self()->joinThreadPool();
+ exit(1);
+ }
+
+ ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ProcessState::self()->startThreadPool();
+
+ gKernelBinder = defaultServiceManager()->waitForService(kKernelBinderInstance);
+ CHECK_NE(nullptr, gKernelBinder.get());
+#endif
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 40ebd9c..d5786bc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -47,6 +47,9 @@
namespace android {
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+ RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+
TEST(BinderRpcParcel, EntireParcelFormatted) {
Parcel p;
p.writeInt32(3);
@@ -67,6 +70,19 @@
ASSERT_EQ(sinkFd, retrieved.get());
}
+TEST(BinderRpc, CannotUseNextWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
using android::binder::Status;
#define EXPECT_OK(status) \
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) {