Reconcile native and Java parcel code for WorkSource. am: c1359ac51c am: a2bcc5fe99
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/19135348
Change-Id: Ia73d017eb5fe4c86150c14f42ef63a45ec3fa778
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6fb9a4d..b37a457 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -1195,6 +1195,11 @@
bool traceStream = false;
bool onlyUserspace = false;
+ fprintf(stderr,
+ "** Warning: atrace will end vendor support in the next Android Release. **\n"
+ "** Perfetto is the suggested replacement tool. It will gain vendor **\n"
+ "** support. See https://perfetto.dev/docs/quickstart/android-tracing **\n\n");
+
if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
showHelp(argv[0]);
exit(0);
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a2491e5..a60972b 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -101,6 +101,7 @@
"libhidlbase",
"liblog",
"libutils",
+ "libvintf",
"libbinderdebug",
"packagemanager_aidl-cpp",
],
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2b94b71..f759674 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "dumpstate"
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
#include <dirent.h>
#include <errno.h>
@@ -76,6 +77,7 @@
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <cutils/trace.h>
#include <debuggerd/client.h>
#include <dumpsys.h>
#include <dumputils/dump_utils.h>
@@ -88,6 +90,7 @@
#include <private/android_logger.h>
#include <serviceutils/PriorityDumper.h>
#include <utils/StrongPointer.h>
+#include <vintf/VintfObject.h>
#include "DumpstateInternal.h"
#include "DumpstateService.h"
#include "dumpstate.h"
@@ -829,7 +832,8 @@
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+ size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression;
+ int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), flags,
get_mtime(fd, ds.now_));
if (err != 0) {
MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
@@ -921,7 +925,8 @@
bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
- int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+ size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression;
+ int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), flags, ds.now_);
if (err != 0) {
MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
ZipWriter::ErrorCodeString(err));
@@ -1394,6 +1399,23 @@
}
}
+// Dump all of the files that make up the vendor interface.
+// See the files listed in dumpFileList() for the latest list of files.
+static void DumpVintf() {
+ const auto vintfFiles = android::vintf::details::dumpFileList();
+ for (const auto vintfFile : vintfFiles) {
+ struct stat st;
+ if (stat(vintfFile.c_str(), &st) == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ ds.AddDir(vintfFile, true /* recursive */);
+ } else {
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded(ZIP_ROOT_DIR + vintfFile,
+ vintfFile);
+ }
+ }
+ }
+}
+
static void DumpExternalFragmentationInfo() {
struct stat st;
if (stat("/proc/buddyinfo", &st) != 0) {
@@ -1619,6 +1641,8 @@
do_dmesg();
}
+ DumpVintf();
+
RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES");
@@ -3076,7 +3100,9 @@
TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
// Zip the (now complete) .tmp file within the internal directory.
+ ATRACE_BEGIN("FinalizeFile");
FinalizeFile();
+ ATRACE_END();
// Share the final file with the caller if the user has consented or Shell is the caller.
Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
@@ -3387,6 +3413,9 @@
duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
+ if (title_.find("SHOW MAP") == std::string::npos) {
+ ATRACE_ASYNC_BEGIN(title_.c_str(), 0);
+ }
}
}
@@ -3401,6 +3430,9 @@
dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
elapsed, title_.c_str());
}
+ if (title_.find("SHOW MAP") == std::string::npos) {
+ ATRACE_ASYNC_END(title_.c_str(), 0);
+ }
}
}
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index c9f680b..0f7c489 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -72,8 +72,6 @@
},
},
- clang: true,
-
tidy: true,
tidy_checks: [
"-*",
@@ -81,8 +79,9 @@
"cert-*",
"-cert-err58-cpp",
],
- tidy_flags: [
- "-warnings-as-errors=clang-analyzer-security*,cert-*",
+ tidy_checks_as_errors: [
+ "clang-analyzer-security*",
+ "cert-*",
],
}
@@ -127,7 +126,6 @@
cc_test_host {
name: "run_dex2oat_test",
test_suites: ["general-tests"],
- clang: true,
srcs: [
"run_dex2oat_test.cpp",
"run_dex2oat.cpp",
@@ -187,7 +185,6 @@
"-Wall",
"-Werror",
],
- clang: true,
srcs: [
"otapreopt_chroot.cpp",
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index a49f563..1d7dd5f 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1869,8 +1869,9 @@
binder::Status res = ok();
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_path(uuid_, userId);
- if (delete_dir_contents_and_dir(path, true) != 0) {
- res = error("Failed to delete " + path);
+ // Contents only, as vold is responsible for the user_de dir itself.
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
}
auto sdk_sandbox_de_path =
create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId);
@@ -1890,8 +1891,9 @@
}
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_path(uuid_, userId);
- if (delete_dir_contents_and_dir(path, true) != 0) {
- res = error("Failed to delete " + path);
+ // Contents only, as vold is responsible for the user_ce dir itself.
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
}
auto sdk_sandbox_ce_path =
create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId);
@@ -1899,8 +1901,9 @@
res = error("Failed to delete " + sdk_sandbox_ce_path);
}
path = findDataMediaPath(uuid, userId);
- if (delete_dir_contents_and_dir(path, true) != 0) {
- res = error("Failed to delete " + path);
+ // Contents only, as vold is responsible for the media dir itself.
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
}
}
return res;
@@ -2876,6 +2879,9 @@
auto obbPath = StringPrintf("%s/Android/obb",
create_data_media_path(uuid_, userId).c_str());
calculate_tree_size(obbPath, &obbSize);
+ if (!(flags & FLAG_USE_QUOTA)) {
+ totalSize -= obbSize;
+ }
ATRACE_END();
}
@@ -3565,10 +3571,10 @@
return error("Failed to stat " + mirrorVolCePath);
}
- if (mirrorCeStat.st_ino == ceStat.st_ino) {
+ if (mirrorCeStat.st_ino == ceStat.st_ino && mirrorCeStat.st_dev == ceStat.st_dev) {
// As it's being called by prepareUserStorage, it can be called multiple times.
// Hence, we if we mount it already, we should skip it.
- LOG(WARNING) << "CE dir is mounted already: " + cePath;
+ LOG(INFO) << "CE dir is mounted already: " + cePath;
return ok();
}
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index b3baca5..07f73b9 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -11,7 +11,6 @@
cc_test {
name: "installd_utils_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_utils_test.cpp"],
cflags: [
"-Wall",
@@ -36,7 +35,6 @@
cc_test {
name: "installd_cache_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_cache_test.cpp"],
cflags: [
"-Wall",
@@ -82,7 +80,6 @@
cc_test {
name: "installd_service_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_service_test.cpp"],
cflags: [
"-Wall",
@@ -130,7 +127,6 @@
cc_test {
name: "installd_dexopt_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_dexopt_test.cpp"],
cflags: [
"-Wall",
@@ -177,7 +173,6 @@
cc_test {
name: "installd_otapreopt_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_otapreopt_test.cpp"],
cflags: [
"-Wall",
@@ -198,7 +193,6 @@
cc_test {
name: "installd_file_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_file_test.cpp"],
cflags: [
"-Wall",
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 4eb30e2..3849c40 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -920,6 +920,11 @@
TEST_F(DexoptTest, DexoptDex2oat64Enabled) {
LOG(INFO) << "DexoptDex2oat64Enabled";
+ std::string zygote_prop = android::base::GetProperty("ro.zygote", "");
+ ASSERT_GT(zygote_prop.size(), 0);
+ if (zygote_prop != "zygote32_64" && zygote_prop != "zygote64_32") {
+ GTEST_SKIP() << "DexoptDex2oat64Enabled skipped for single-bitness Zygote.";
+ }
const std::string property = "dalvik.vm.dex2oat64.enabled";
const std::string previous_value = android::base::GetProperty(property, "");
auto restore_property = android::base::make_scope_guard([=]() {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 45aeab6..4d9b710 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1184,8 +1184,8 @@
int wait_child_with_timeout(pid_t pid, int timeout_ms) {
int pidfd = pidfd_open(pid, /*flags=*/0);
if (pidfd < 0) {
- PLOG(ERROR) << "pidfd_open failed for pid " << pid;
- kill(pid, SIGKILL);
+ PLOG(ERROR) << "pidfd_open failed for pid " << pid
+ << ", waiting for child process without timeout";
return wait_child(pid);
}
diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp
index cbfbdc9..af85666 100644
--- a/cmds/lshal/libprocpartition/Android.bp
+++ b/cmds/lshal/libprocpartition/Android.bp
@@ -35,5 +35,6 @@
],
export_include_dirs: [
"include",
- ]
+ ],
+ min_sdk_version: "30",
}
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 2fb9c2b..1d458b7 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -121,6 +121,8 @@
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
+ LOG(INFO) << "Starting sm instance on " << driver;
+
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index c4f03c9..3585392 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -300,6 +300,8 @@
/*
* Gets the path of an input device configuration file, if one is available.
* Considers both system provided and user installed configuration files.
+ * The optional suffix is appended to the end of the file name (before the
+ * extension).
*
* The device identifier is used to construct several default configuration file
* names to try based on the device name, vendor, product, and version.
@@ -307,8 +309,8 @@
* Returns an empty string if not found.
*/
extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
- const InputDeviceIdentifier& deviceIdentifier,
- InputDeviceConfigurationFileType type);
+ const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type,
+ const char* suffix = "");
/*
* Gets the path of an input device configuration file, if one is available.
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index b2bd535..1da78aa 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -20,9 +20,8 @@
#include <android-base/result.h>
#include <stdint.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
#include <utils/Tokenizer.h>
+#include <set>
#include <input/InputDevice.h>
@@ -65,18 +64,18 @@
*/
class KeyLayoutMap {
public:
- static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename);
- static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename,
+ const char* contents = nullptr);
static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename,
const char* contents);
status_t mapKey(int32_t scanCode, int32_t usageCode,
int32_t* outKeyCode, uint32_t* outFlags) const;
- status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const;
- status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const;
- status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
+ std::vector<int32_t> findScanCodesForKey(int32_t keyCode) const;
+ std::optional<int32_t> findScanCodeForLed(int32_t ledCode) const;
+ std::optional<int32_t> findUsageCodeForLed(int32_t ledCode) const;
- status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
+ std::optional<AxisInfo> mapAxis(int32_t scanCode) const;
const std::string getLoadFileName() const;
// Return pair of sensor type and sensor data index, for the input device abs code
base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode);
@@ -84,6 +83,8 @@
virtual ~KeyLayoutMap();
private:
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+
struct Key {
int32_t keyCode;
uint32_t flags;
@@ -98,12 +99,13 @@
int32_t sensorDataIndex;
};
- KeyedVector<int32_t, Key> mKeysByScanCode;
- KeyedVector<int32_t, Key> mKeysByUsageCode;
- KeyedVector<int32_t, AxisInfo> mAxes;
- KeyedVector<int32_t, Led> mLedsByScanCode;
- KeyedVector<int32_t, Led> mLedsByUsageCode;
+ std::unordered_map<int32_t, Key> mKeysByScanCode;
+ std::unordered_map<int32_t, Key> mKeysByUsageCode;
+ std::unordered_map<int32_t, AxisInfo> mAxes;
+ std::unordered_map<int32_t, Led> mLedsByScanCode;
+ std::unordered_map<int32_t, Led> mLedsByUsageCode;
std::unordered_map<int32_t, Sensor> mSensorsByAbsCode;
+ std::set<std::string> mRequiredKernelConfigs;
std::string mLoadFileName;
KeyLayoutMap();
@@ -124,6 +126,7 @@
status_t parseAxis();
status_t parseLed();
status_t parseSensor();
+ status_t parseRequiredKernelConfig();
};
};
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 08ad8c6..9a3e15f 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -61,9 +61,7 @@
bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
- const std::string& name);
- std::string getPath(const InputDeviceIdentifier& deviceIdentifier,
- const std::string& name, InputDeviceConfigurationFileType type);
+ const std::string& name);
};
/**
diff --git a/include/input/OWNERS b/include/input/OWNERS
new file mode 100644
index 0000000..c88bfe9
--- /dev/null
+++ b/include/input/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index ea3c341..2bf15d4 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -28,11 +28,9 @@
"-Werror",
],
srcs: [
- "HmacKeyManager.cpp"
+ "HmacKeyManager.cpp",
],
- clang: true,
-
shared_libs: [
"liblog",
"libcrypto",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d8d2cf2..441a4a8 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -60,27 +60,19 @@
//
// Currently, these are only on system android (not vendor, not host)
// TODO(b/183654927) - move these into separate libraries
-libbinder_device_interface_sources = [
- "IPermissionController.cpp",
- "PermissionCache.cpp",
- "PermissionController.cpp",
-]
-cc_library {
- name: "libbinder",
+filegroup {
+ name: "libbinder_device_interface_sources",
+ srcs: [
+ "IPermissionController.cpp",
+ "PermissionCache.cpp",
+ "PermissionController.cpp",
+ ],
+}
- version_script: "libbinder.map",
-
- // for vndbinder
- vendor_available: true,
- vndk: {
- enabled: true,
- },
- recovery_available: true,
- double_loadable: true,
+cc_defaults {
+ name: "libbinder_defaults",
host_supported: true,
- // TODO(b/153609531): remove when no longer needed.
- native_bridge_supported: true,
// TODO(b/31559095): get headers from bionic on host
include_dirs: [
@@ -88,72 +80,32 @@
"bionic/libc/kernel/uapi/",
],
- // libbinder does not offer a stable wire protocol.
- // if a second copy of it is installed, then it may break after security
- // or dessert updates. Instead, apex users should use libbinder_ndk.
- apex_available: [
- "//apex_available:platform",
- ],
-
srcs: [
"Binder.cpp",
"BpBinder.cpp",
- "BufferedTextOutput.cpp",
"Debug.cpp",
"FdTrigger.cpp",
"IInterface.cpp",
- "IMemory.cpp",
- "IPCThreadState.cpp",
"IResultReceiver.cpp",
- "IServiceManager.cpp",
- "IShellCallback.cpp",
- "LazyServiceRegistrar.cpp",
- "MemoryBase.cpp",
- "MemoryDealer.cpp",
- "MemoryHeapBase.cpp",
+ "OS.cpp",
"Parcel.cpp",
- "ParcelableHolder.cpp",
"ParcelFileDescriptor.cpp",
- "PersistableBundle.cpp",
- "ProcessState.cpp",
"RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
"RpcTransportRaw.cpp",
- "Static.cpp",
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
"Utils.cpp",
- ":libbinder_aidl",
],
target: {
- android: {
- srcs: libbinder_device_interface_sources,
-
- // NOT static to keep the wire protocol unfrozen
- static: {
- enabled: false,
- },
- },
- vendor: {
- exclude_srcs: libbinder_device_interface_sources,
- },
- darwin: {
- enabled: false,
- },
host: {
srcs: [
- "ServiceManagerHost.cpp",
"UtilsHost.cpp",
],
},
- recovery: {
- exclude_header_libs: [
- "libandroid_runtime_vm_headers",
- ],
- },
},
aidl: {
@@ -161,7 +113,6 @@
},
cflags: [
- "-Wall",
"-Wextra",
"-Wextra-semi",
"-Werror",
@@ -199,7 +150,6 @@
"libbinder_headers",
],
- clang: true,
sanitize: {
misc_undefined: ["integer"],
},
@@ -217,6 +167,7 @@
"abseil-*",
"android-*",
"bugprone-*",
+ "-bugprone-branch-clone", // b/155034972
"cert-*",
"clang-analyzer-*",
"google-*",
@@ -224,6 +175,89 @@
"performance*",
"portability*",
],
+}
+
+cc_defaults {
+ name: "libbinder_kernel_defaults",
+ srcs: [
+ "BufferedTextOutput.cpp",
+ "IPCThreadState.cpp",
+ "IServiceManager.cpp",
+ "ProcessState.cpp",
+ "Static.cpp",
+ ":libbinder_aidl",
+ ":libbinder_device_interface_sources",
+ ],
+ target: {
+ vendor: {
+ exclude_srcs: [
+ ":libbinder_device_interface_sources",
+ ],
+ },
+ host: {
+ srcs: [
+ "ServiceManagerHost.cpp",
+ ],
+ },
+ },
+ cflags: [
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+}
+
+cc_library {
+ name: "libbinder",
+ defaults: [
+ "libbinder_defaults",
+ "libbinder_kernel_defaults",
+ ],
+
+ version_script: "libbinder.map",
+
+ // for vndbinder
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ recovery_available: true,
+ double_loadable: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+
+ // libbinder does not offer a stable wire protocol.
+ // if a second copy of it is installed, then it may break after security
+ // or dessert updates. Instead, apex users should use libbinder_ndk.
+ apex_available: [
+ "//apex_available:platform",
+ ],
+
+ srcs: [
+ "IMemory.cpp",
+ "IShellCallback.cpp",
+ "LazyServiceRegistrar.cpp",
+ "MemoryBase.cpp",
+ "MemoryDealer.cpp",
+ "MemoryHeapBase.cpp",
+ "ParcelableHolder.cpp",
+ "PersistableBundle.cpp",
+ ],
+
+ target: {
+ android: {
+ // NOT static to keep the wire protocol unfrozen
+ static: {
+ enabled: false,
+ },
+ },
+ darwin: {
+ enabled: false,
+ },
+ recovery: {
+ exclude_header_libs: [
+ "libandroid_runtime_vm_headers",
+ ],
+ },
+ },
afdo: true,
@@ -232,6 +266,39 @@
},
}
+cc_library_static {
+ name: "libbinder_rpc_no_kernel",
+ defaults: ["libbinder_defaults"],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_single_threaded",
+ defaults: [
+ "libbinder_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_single_threaded_no_kernel",
+ defaults: ["libbinder_defaults"],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
cc_defaults {
name: "libbinder_tls_shared_deps",
shared_libs: [
@@ -370,6 +437,7 @@
cc_library {
name: "libbatterystats_aidl",
+ host_supported: true,
srcs: [
"IBatteryStats.cpp",
],
@@ -382,6 +450,7 @@
cc_library {
name: "libprocessinfoservice_aidl",
+ host_supported: true,
srcs: [
"IProcessInfoService.cpp",
"ProcessInfoService.cpp",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 39befbe..532bacb 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -32,9 +32,13 @@
#include <utils/misc.h>
#include <inttypes.h>
-#include <linux/sched.h>
#include <stdio.h>
+#ifdef __linux__
+#include <linux/sched.h>
+#endif
+
+#include "BuildFlags.h"
#include "RpcState.h"
namespace android {
@@ -49,10 +53,11 @@
static_assert(sizeof(BBinder) == 20);
#endif
+// global b/c b/230079120 - consistent symbol table
#ifdef BINDER_RPC_DEV_SERVERS
-constexpr const bool kEnableRpcDevServers = true;
+bool kEnableRpcDevServers = true;
#else
-constexpr const bool kEnableRpcDevServers = false;
+bool kEnableRpcDevServers = false;
#endif
// Log any reply transactions for which the data exceeds this size
@@ -156,10 +161,14 @@
status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd,
const sp<IBinder>& keepAliveBinder) {
- if constexpr (!kEnableRpcDevServers) {
+ if (!kEnableRpcDevServers) {
ALOGW("setRpcClientDebug disallowed because RPC is not enabled");
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
BBinder* local = this->localBinder();
if (local != nullptr) {
@@ -201,6 +210,7 @@
RpcServerLink(const sp<RpcServer>& rpcServer, const sp<IBinder>& keepAliveBinder,
const wp<BBinder>& binder)
: mRpcServer(rpcServer), mKeepAliveBinder(keepAliveBinder), mBinder(binder) {}
+ virtual ~RpcServerLink();
void binderDied(const wp<IBinder>&) override {
LOG_RPC_DETAIL("RpcServerLink: binder died, shutting down RpcServer");
if (mRpcServer == nullptr) {
@@ -226,16 +236,19 @@
sp<IBinder> mKeepAliveBinder; // hold to avoid automatically unlinking
wp<BBinder> mBinder;
};
+BBinder::RpcServerLink::~RpcServerLink() {}
class BBinder::Extras
{
public:
// unlocked objects
- bool mRequestingSid = false;
- bool mInheritRt = false;
sp<IBinder> mExtension;
+#ifdef __linux__
int mPolicy = SCHED_NORMAL;
int mPriority = 0;
+#endif
+ bool mRequestingSid = false;
+ bool mInheritRt = false;
// for below objects
Mutex mLock;
@@ -404,6 +417,7 @@
return e->mExtension;
}
+#ifdef __linux__
void BBinder::setMinSchedulerPolicy(int policy, int priority) {
LOG_ALWAYS_FATAL_IF(mParceled,
"setMinSchedulerPolicy() should not be called after a binder object "
@@ -448,6 +462,7 @@
if (e == nullptr) return 0;
return e->mPriority;
}
+#endif // __linux__
bool BBinder::isInheritRt() {
Extras* e = mExtras.load(std::memory_order_acquire);
@@ -475,7 +490,12 @@
}
pid_t BBinder::getDebugPid() {
+#ifdef __linux__
return getpid();
+#else
+ // TODO: handle other OSes
+ return 0;
+#endif // __linux__
}
void BBinder::setExtension(const sp<IBinder>& extension) {
@@ -496,10 +516,14 @@
}
status_t BBinder::setRpcClientDebug(const Parcel& data) {
- if constexpr (!kEnableRpcDevServers) {
+ if (!kEnableRpcDevServers) {
ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid != AID_ROOT) {
ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid);
@@ -521,10 +545,14 @@
status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd,
const sp<IBinder>& keepAliveBinder) {
- if constexpr (!kEnableRpcDevServers) {
+ if (!kEnableRpcDevServers) {
ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
const int socketFdForPrint = socketFd.get();
LOG_RPC_DETAIL("%s(fd=%d)", __PRETTY_FUNCTION__, socketFdForPrint);
@@ -539,7 +567,7 @@
return UNEXPECTED_NULL;
}
- size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxThreadCount();
+ size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxTotalThreadCount();
if (binderThreadPoolMaxCount <= 1) {
ALOGE("%s: ProcessState thread pool max count is %zu. RPC is disabled for this service "
"because RPC requires the service to support multithreading.",
@@ -620,13 +648,14 @@
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(
- data.readStrongBinder());
+ sp<IBinder> shellCallbackBinder = data.readStrongBinder();
sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
data.readStrongBinder());
// XXX can't add virtuals until binaries are updated.
- //return shellCommand(in, out, err, args, resultReceiver);
+ // sp<IShellCallback> shellCallback = IShellCallback::asInterface(
+ // shellCallbackBinder);
+ // return shellCommand(in, out, err, args, resultReceiver);
(void)in;
(void)out;
(void)err;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 921e57c..49fc195 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -28,6 +28,8 @@
#include <stdio.h>
+#include "BuildFlags.h"
+
//#undef ALOGV
//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
@@ -115,6 +117,11 @@
// ---------------------------------------------------------------------------
sp<BpBinder> BpBinder::create(int32_t handle) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return nullptr;
+ }
+
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
trackedUid = IPCThreadState::self()->getCallingUid();
@@ -177,6 +184,11 @@
}
BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
mTrackedUid = trackedUid;
ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle());
@@ -279,7 +291,7 @@
if (mAlive) {
bool privateVendor = flags & FLAG_PRIVATE_VENDOR;
// don't send userspace flags to the kernel
- flags = flags & ~FLAG_PRIVATE_VENDOR;
+ flags = flags & ~static_cast<uint32_t>(FLAG_PRIVATE_VENDOR);
// user transactions require a given stability level
if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
@@ -303,6 +315,11 @@
status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply,
flags);
} else {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+ }
+
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
@@ -328,6 +345,11 @@
{
if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+ }
+
Obituary ob;
ob.recipient = recipient;
ob.cookie = cookie;
@@ -366,6 +388,11 @@
{
if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+ }
+
AutoMutex _l(mLock);
if (mObitsSent) {
@@ -401,6 +428,11 @@
{
LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder.");
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(),
mObitsSent ? "true" : "false");
@@ -469,12 +501,16 @@
return this;
}
-BpBinder::~BpBinder()
-{
- ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle());
-
+BpBinder::~BpBinder() {
if (CC_UNLIKELY(isRpcBinder())) return;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
+ ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle());
+
IPCThreadState* ipc = IPCThreadState::self();
if (mTrackedUid >= 0) {
@@ -505,21 +541,31 @@
}
}
-void BpBinder::onFirstRef()
-{
- ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
+void BpBinder::onFirstRef() {
if (CC_UNLIKELY(isRpcBinder())) return;
+
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
+ ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
IPCThreadState* ipc = IPCThreadState::self();
if (ipc) ipc->incStrongHandle(binderHandle(), this);
}
-void BpBinder::onLastStrongRef(const void* /*id*/)
-{
- ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
+void BpBinder::onLastStrongRef(const void* /*id*/) {
if (CC_UNLIKELY(isRpcBinder())) {
(void)rpcSession()->sendDecStrong(this);
return;
}
+
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return;
+ }
+
+ ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
IF_ALOGV() {
printRefs();
}
@@ -552,6 +598,11 @@
// RPC binder doesn't currently support inc from weak binders
if (CC_UNLIKELY(isRpcBinder())) return false;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return false;
+ }
+
ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle());
IPCThreadState* ipc = IPCThreadState::self();
return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false;
diff --git a/libs/binder/BuildFlags.h b/libs/binder/BuildFlags.h
new file mode 100644
index 0000000..3e9d1c2
--- /dev/null
+++ b/libs/binder/BuildFlags.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace android {
+
+#ifdef BINDER_RPC_SINGLE_THREADED
+constexpr bool kEnableRpcThreads = false;
+#else
+constexpr bool kEnableRpcThreads = true;
+#endif
+
+#ifdef BINDER_WITH_KERNEL_IPC
+constexpr bool kEnableKernelIpc = true;
+#else // BINDER_WITH_KERNEL_IPC
+constexpr bool kEnableKernelIpc = false;
+#endif // BINDER_WITH_KERNEL_IPC
+
+} // namespace android
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index e4ac4b4..c6e4fb3 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -15,6 +15,7 @@
*/
#include "Debug.h"
+#include "BuildFlags.h"
#include <binder/ProcessState.h>
@@ -301,6 +302,11 @@
}
ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return 0;
+ }
+
sp<ProcessState> proc = ProcessState::selfOrNull();
if (proc.get() == nullptr) {
return 0;
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 5e22593..d123fd1 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -28,25 +28,45 @@
std::unique_ptr<FdTrigger> FdTrigger::make() {
auto ret = std::make_unique<FdTrigger>();
+#ifndef BINDER_RPC_SINGLE_THREADED
if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) {
ALOGE("Could not create pipe %s", strerror(errno));
return nullptr;
}
+#endif
return ret;
}
void FdTrigger::trigger() {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ mTriggered = true;
+#else
mWrite.reset();
+#endif
}
bool FdTrigger::isTriggered() {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ return mTriggered;
+#else
return mWrite == -1;
+#endif
}
status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
+#ifdef BINDER_RPC_SINGLE_THREADED
+ if (mTriggered) {
+ return DEAD_OBJECT;
+ }
+#endif
+
LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get());
- pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
- {.fd = mRead.get(), .events = 0, .revents = 0}};
+ pollfd pfd[]{
+ {.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
+#ifndef BINDER_RPC_SINGLE_THREADED
+ {.fd = mRead.get(), .events = 0, .revents = 0},
+#endif
+ };
int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
if (ret < 0) {
return -errno;
@@ -55,6 +75,7 @@
// At least one FD has events. Check them.
+#ifndef BINDER_RPC_SINGLE_THREADED
// Detect explicit trigger(): DEAD_OBJECT
if (pfd[1].revents & POLLHUP) {
return DEAD_OBJECT;
@@ -68,6 +89,7 @@
// pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are
// a subset of event | POLLHUP | POLLERR | POLLNVAL.
+#endif
// POLLNVAL: invalid FD number, e.g. not opened.
if (pfd[0].revents & POLLNVAL) {
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a545d6c..5c7102e 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -55,7 +55,11 @@
[[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
private:
+#ifdef BINDER_RPC_SINGLE_THREADED
+ bool mTriggered = false;
+#else
base::unique_fd mWrite;
base::unique_fd mRead;
+#endif
};
} // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 3c97dca..d536219 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -638,7 +638,9 @@
void IPCThreadState::joinThreadPool(bool isMain)
{
LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
-
+ pthread_mutex_lock(&mProcess->mThreadCountLock);
+ mProcess->mCurrentThreads++;
+ pthread_mutex_unlock(&mProcess->mThreadCountLock);
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
mIsLooper = true;
@@ -666,6 +668,13 @@
mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
talkWithDriver(false);
+ pthread_mutex_lock(&mProcess->mThreadCountLock);
+ LOG_ALWAYS_FATAL_IF(mProcess->mCurrentThreads == 0,
+ "Threadpool thread count = 0. Thread cannot exist and exit in empty "
+ "threadpool\n"
+ "Misconfiguration. Increase threadpool max threads configuration\n");
+ mProcess->mCurrentThreads--;
+ pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
status_t IPCThreadState::setupPolling(int* fd)
@@ -677,6 +686,9 @@
mOut.writeInt32(BC_ENTER_LOOPER);
flushCommands();
*fd = mProcess->mDriverFD;
+ pthread_mutex_lock(&mProcess->mThreadCountLock);
+ mProcess->mCurrentThreads++;
+ pthread_mutex_unlock(&mProcess->mThreadCountLock);
return 0;
}
@@ -989,6 +1001,7 @@
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
+ logExtendedError();
}
return err;
@@ -1443,6 +1456,23 @@
return ret;
}
+void IPCThreadState::logExtendedError() {
+ struct binder_extended_error ee = {.command = BR_OK};
+
+ if (!ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::EXTENDED_ERROR))
+ return;
+
+#if defined(__ANDROID__)
+ if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0) {
+ ALOGE("Failed to get extended error: %s", strerror(errno));
+ return;
+ }
+#endif
+
+ ALOGE_IF(ee.command != BR_OK, "Binder transaction failure: %d/%d/%d",
+ ee.id, ee.command, ee.param);
+}
+
void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
size_t /*dataSize*/,
const binder_size_t* /*objects*/,
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index fd2d868..fd47783 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -167,7 +167,7 @@
}
}
-#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
+#if !defined(__ANDROID_VNDK__)
// IPermissionController is not accessible to vendors
bool checkCallingPermission(const String16& permission)
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 8132d46..8fe1d2b 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -74,7 +74,7 @@
fd = memfd_create_region(name ? name : "MemoryHeapBase", size);
if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
- ((mFlags & MEMFD_ALLOW_SEALING) ? 0 : F_SEAL_SEAL);
+ ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name,
SEAL_FLAGS, strerror(errno));
@@ -85,12 +85,9 @@
}
return;
#else
- mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING);
+ mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG);
#endif
}
- if (mFlags & MEMFD_ALLOW_SEALING) {
- LOG_ALWAYS_FATAL("Invalid Flags. MEMFD_ALLOW_SEALING only valid with FORCE_MEMFD.");
- }
fd = ashmem_create_region(name ? name : "MemoryHeapBase", size);
ALOGE_IF(fd < 0, "MemoryHeapBase: error creating ashmem region: %s", strerror(errno));
if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
@@ -103,7 +100,7 @@
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
- if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) {
+ if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) {
LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor");
}
int open_flags = O_RDWR;
@@ -125,7 +122,7 @@
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
- if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) {
+ if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) {
LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor");
}
const size_t pagesize = getpagesize();
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
new file mode 100644
index 0000000..6eb7272
--- /dev/null
+++ b/libs/binder/OS.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OS.h"
+
+#include <android-base/file.h>
+#include <string.h>
+
+using android::base::ErrnoError;
+using android::base::Result;
+
+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 {};
+}
+
+status_t getRandomBytes(uint8_t* data, size_t size) {
+ int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (ret == -1) {
+ return -errno;
+ }
+
+ base::unique_fd fd(ret);
+ if (!base::ReadFully(fd, data, size)) {
+ return -errno;
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
new file mode 100644
index 0000000..e802e9c
--- /dev/null
+++ b/libs/binder/OS.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stddef.h>
+#include <cstdint>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+
+status_t getRandomBytes(uint8_t* data, size_t size);
+
+} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 58b0b35..537527e 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -20,15 +20,14 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <linux/sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/resource.h>
#include <unistd.h>
#include <binder/Binder.h>
@@ -40,6 +39,7 @@
#include <binder/Status.h>
#include <binder/TextOutput.h>
+#include <android-base/scopeguard.h>
#include <cutils/ashmem.h>
#include <cutils/compiler.h>
#include <utils/Flattenable.h>
@@ -51,12 +51,27 @@
#include "RpcState.h"
#include "Static.h"
#include "Utils.h"
+
+// A lot of code in this file uses definitions from the
+// Linux kernel header for Binder <linux/android/binder.h>
+// which is included indirectly via "binder_module.h".
+// Non-Linux OSes do not have that header, so libbinder should be
+// built for those targets without kernel binder support, i.e.,
+// without BINDER_WITH_KERNEL_IPC. For this reason, all code in this
+// file that depends on kernel binder, including the header itself,
+// is conditional on BINDER_WITH_KERNEL_IPC.
+#ifdef BINDER_WITH_KERNEL_IPC
+#include <linux/sched.h>
#include "binder_module.h"
+#else // BINDER_WITH_KERNEL_IPC
+// Needed by {read,write}Pointer
+typedef uintptr_t binder_uintptr_t;
+#endif // BINDER_WITH_KERNEL_IPC
#define LOG_REFS(...)
-//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOG_ALLOC(...)
-//#define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+// #define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
// ---------------------------------------------------------------------------
@@ -87,7 +102,8 @@
static std::atomic<size_t> gParcelGlobalAllocCount;
static std::atomic<size_t> gParcelGlobalAllocSize;
-static size_t gMaxFds = 0;
+// Maximum number of file descriptors per Parcel.
+constexpr size_t kMaxFds = 1024;
// Maximum size of a blob to transfer in-place.
static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
@@ -98,6 +114,7 @@
BLOB_ASHMEM_MUTABLE = 2,
};
+#ifdef BINDER_WITH_KERNEL_IPC
static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
const void* who) {
switch (obj.hdr.type) {
@@ -150,6 +167,15 @@
ALOGE("Invalid object type 0x%08x", obj.hdr.type);
}
+#endif // BINDER_WITH_KERNEL_IPC
+
+static int toRawFd(const std::variant<base::unique_fd, base::borrowed_fd>& v) {
+ return std::visit([](const auto& fd) { return fd.get(); }, v);
+}
+
+Parcel::RpcFields::RpcFields(const sp<RpcSession>& session) : mSession(session) {
+ LOG_ALWAYS_FATAL_IF(mSession == nullptr);
+}
status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder)
{
@@ -173,22 +199,25 @@
return OK;
}
+#ifdef BINDER_WITH_KERNEL_IPC
static constexpr inline int schedPolicyMask(int policy, int priority) {
return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT);
}
+#endif // BINDER_WITH_KERNEL_IPC
status_t Parcel::flattenBinder(const sp<IBinder>& binder) {
BBinder* local = nullptr;
if (binder) local = binder->localBinder();
if (local) local->setParceled();
- if (isForRpc()) {
+ if (const auto* rpcFields = maybeRpcFields()) {
if (binder) {
status_t status = writeInt32(1); // non-null
if (status != OK) return status;
uint64_t address;
// TODO(b/167966510): need to undo this if the Parcel is not sent
- status = mSession->state()->onBinderLeaving(mSession, binder, &address);
+ status = rpcFields->mSession->state()->onBinderLeaving(rpcFields->mSession, binder,
+ &address);
if (status != OK) return status;
status = writeUint64(address);
if (status != OK) return status;
@@ -199,6 +228,7 @@
return finishFlattenBinder(binder);
}
+#ifdef BINDER_WITH_KERNEL_IPC
flat_binder_object obj;
int schedBits = 0;
@@ -255,13 +285,15 @@
if (status != OK) return status;
return finishFlattenBinder(binder);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
- if (isForRpc()) {
- LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
-
+ if (const auto* rpcFields = maybeRpcFields()) {
int32_t isPresent;
status_t status = readInt32(&isPresent);
if (status != OK) return status;
@@ -271,10 +303,14 @@
if (isPresent & 1) {
uint64_t addr;
if (status_t status = readUint64(&addr); status != OK) return status;
- if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder);
+ if (status_t status =
+ rpcFields->mSession->state()->onBinderEntering(rpcFields->mSession, addr,
+ &binder);
status != OK)
return status;
- if (status_t status = mSession->state()->flushExcessBinderRefs(mSession, addr, binder);
+ if (status_t status =
+ rpcFields->mSession->state()->flushExcessBinderRefs(rpcFields->mSession,
+ addr, binder);
status != OK)
return status;
}
@@ -282,6 +318,7 @@
return finishUnflattenBinder(binder, out);
}
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* flat = readObject(false);
if (flat) {
@@ -299,6 +336,10 @@
}
}
return BAD_TYPE;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
// ---------------------------------------------------------------------------
@@ -378,8 +419,10 @@
}
mDataPos = pos;
- mNextObjectHint = 0;
- mObjectsSorted = false;
+ if (const auto* kernelFields = maybeKernelFields()) {
+ kernelFields->mNextObjectHint = 0;
+ kernelFields->mObjectsSorted = false;
+ }
}
status_t Parcel::setDataCapacity(size_t size)
@@ -406,25 +449,27 @@
if (err == NO_ERROR) {
memcpy(const_cast<uint8_t*>(data()), buffer, len);
mDataSize = len;
- mFdsKnown = false;
+ if (auto* kernelFields = maybeKernelFields()) {
+ kernelFields->mFdsKnown = false;
+ }
}
return err;
}
-status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
-{
- if (mSession != parcel->mSession) {
+status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) {
+ if (isForRpc() != parcel->isForRpc()) {
ALOGE("Cannot append Parcel from one context to another. They may be different formats, "
"and objects are specific to a context.");
return BAD_TYPE;
}
+ if (isForRpc() && maybeRpcFields()->mSession != parcel->maybeRpcFields()->mSession) {
+ ALOGE("Cannot append Parcels from different sessions");
+ return BAD_TYPE;
+ }
status_t err;
- const uint8_t *data = parcel->mData;
- const binder_size_t *objects = parcel->mObjects;
- size_t size = parcel->mObjectsSize;
+ const uint8_t* data = parcel->mData;
int startPos = mDataPos;
- int firstIndex = -1, lastIndex = -2;
if (len == 0) {
return NO_ERROR;
@@ -443,18 +488,6 @@
return BAD_VALUE;
}
- // Count objects in range
- for (int i = 0; i < (int) size; i++) {
- size_t off = objects[i];
- if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) {
- if (firstIndex == -1) {
- firstIndex = i;
- }
- lastIndex = i;
- }
- }
- int numObjects = lastIndex - firstIndex + 1;
-
if ((mDataSize+len) > mDataCapacity) {
// grow data
err = growData(len);
@@ -470,43 +503,125 @@
err = NO_ERROR;
- if (numObjects > 0) {
- const sp<ProcessState> proc(ProcessState::self());
- // grow objects
- if (mObjectsCapacity < mObjectsSize + numObjects) {
- if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow
- if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow
- size_t newSize = ((mObjectsSize + numObjects)*3)/2;
- if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
- binder_size_t *objects =
- (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
- if (objects == (binder_size_t*)nullptr) {
- return NO_MEMORY;
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ auto* otherKernelFields = parcel->maybeKernelFields();
+ LOG_ALWAYS_FATAL_IF(otherKernelFields == nullptr);
+
+ const binder_size_t* objects = otherKernelFields->mObjects;
+ size_t size = otherKernelFields->mObjectsSize;
+ // Count objects in range
+ int firstIndex = -1, lastIndex = -2;
+ for (int i = 0; i < (int)size; i++) {
+ size_t off = objects[i];
+ if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) {
+ if (firstIndex == -1) {
+ firstIndex = i;
+ }
+ lastIndex = i;
}
- mObjects = objects;
- mObjectsCapacity = newSize;
+ }
+ int numObjects = lastIndex - firstIndex + 1;
+ if (numObjects > 0) {
+ const sp<ProcessState> proc(ProcessState::self());
+ // grow objects
+ if (kernelFields->mObjectsCapacity < kernelFields->mObjectsSize + numObjects) {
+ if ((size_t)numObjects > SIZE_MAX - kernelFields->mObjectsSize)
+ return NO_MEMORY; // overflow
+ if (kernelFields->mObjectsSize + numObjects > SIZE_MAX / 3)
+ return NO_MEMORY; // overflow
+ size_t newSize = ((kernelFields->mObjectsSize + numObjects) * 3) / 2;
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
+ binder_size_t* objects = (binder_size_t*)realloc(kernelFields->mObjects,
+ newSize * sizeof(binder_size_t));
+ if (objects == (binder_size_t*)nullptr) {
+ return NO_MEMORY;
+ }
+ kernelFields->mObjects = objects;
+ kernelFields->mObjectsCapacity = newSize;
+ }
+
+ // append and acquire objects
+ int idx = kernelFields->mObjectsSize;
+ for (int i = firstIndex; i <= lastIndex; i++) {
+ size_t off = objects[i] - offset + startPos;
+ kernelFields->mObjects[idx++] = off;
+ kernelFields->mObjectsSize++;
+
+ flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off);
+ acquire_object(proc, *flat, this);
+
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ // If this is a file descriptor, we need to dup it so the
+ // new Parcel now owns its own fd, and can declare that we
+ // officially know we have fds.
+ flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
+ flat->cookie = 1;
+ kernelFields->mHasFds = kernelFields->mFdsKnown = true;
+ if (!mAllowFds) {
+ err = FDS_NOT_ALLOWED;
+ }
+ }
+ }
+ }
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else {
+ auto* rpcFields = maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+ auto* otherRpcFields = parcel->maybeRpcFields();
+ if (otherRpcFields == nullptr) {
+ return BAD_TYPE;
+ }
+ if (rpcFields->mSession != otherRpcFields->mSession) {
+ return BAD_TYPE;
}
- // append and acquire objects
- int idx = mObjectsSize;
- for (int i = firstIndex; i <= lastIndex; i++) {
- size_t off = objects[i] - offset + startPos;
- mObjects[idx++] = off;
- mObjectsSize++;
+ const size_t savedDataPos = mDataPos;
+ base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; };
- flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData + off);
- acquire_object(proc, *flat, this);
+ rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size());
+ if (otherRpcFields->mFds != nullptr) {
+ if (rpcFields->mFds == nullptr) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ }
+ rpcFields->mFds->reserve(otherRpcFields->mFds->size());
+ }
+ for (size_t i = 0; i < otherRpcFields->mObjectPositions.size(); i++) {
+ const binder_size_t objPos = otherRpcFields->mObjectPositions[i];
+ if (offset <= objPos && objPos < offset + len) {
+ size_t newDataPos = objPos - offset + startPos;
+ rpcFields->mObjectPositions.push_back(newDataPos);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- // If this is a file descriptor, we need to dup it so the
- // new Parcel now owns its own fd, and can declare that we
- // officially know we have fds.
- flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
- flat->cookie = 1;
- mHasFds = mFdsKnown = true;
+ mDataPos = newDataPos;
+ int32_t objectType;
+ if (status_t status = readInt32(&objectType); status != OK) {
+ return status;
+ }
+ if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ continue;
+ }
+
if (!mAllowFds) {
- err = FDS_NOT_ALLOWED;
+ return FDS_NOT_ALLOWED;
+ }
+
+ // Read FD, duplicate, and add to list.
+ int32_t fdIndex;
+ if (status_t status = readInt32(&fdIndex); status != OK) {
+ return status;
+ }
+ const auto& oldFd = otherRpcFields->mFds->at(fdIndex);
+ // To match kernel binder behavior, we always dup, even if the
+ // FD was unowned in the source parcel.
+ rpcFields->mFds->emplace_back(
+ base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0)));
+ // Fixup the index in the data.
+ mDataPos = newDataPos + 4;
+ if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) {
+ return status;
}
}
}
@@ -563,18 +678,28 @@
bool Parcel::hasFileDescriptors() const
{
- if (!mFdsKnown) {
+ if (const auto* rpcFields = maybeRpcFields()) {
+ return rpcFields->mFds != nullptr && !rpcFields->mFds->empty();
+ }
+ auto* kernelFields = maybeKernelFields();
+ if (!kernelFields->mFdsKnown) {
scanForFds();
}
- return mHasFds;
+ return kernelFields->mHasFds;
}
std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const {
std::vector<sp<IBinder>> ret;
+#ifdef BINDER_WITH_KERNEL_IPC
+ const auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return ret;
+ }
+
size_t initPosition = dataPosition();
- for (size_t i = 0; i < mObjectsSize; i++) {
- binder_size_t offset = mObjects[i];
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ binder_size_t offset = kernelFields->mObjects[i];
const flat_binder_object* flat =
reinterpret_cast<const flat_binder_object*>(mData + offset);
if (flat->hdr.type != BINDER_TYPE_BINDER) continue;
@@ -586,27 +711,39 @@
}
setDataPosition(initPosition);
+#endif // BINDER_WITH_KERNEL_IPC
+
return ret;
}
std::vector<int> Parcel::debugReadAllFileDescriptors() const {
std::vector<int> ret;
- size_t initPosition = dataPosition();
- for (size_t i = 0; i < mObjectsSize; i++) {
- binder_size_t offset = mObjects[i];
- const flat_binder_object* flat =
- reinterpret_cast<const flat_binder_object*>(mData + offset);
- if (flat->hdr.type != BINDER_TYPE_FD) continue;
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ size_t initPosition = dataPosition();
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ binder_size_t offset = kernelFields->mObjects[i];
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + offset);
+ if (flat->hdr.type != BINDER_TYPE_FD) continue;
- setDataPosition(offset);
+ setDataPosition(offset);
- int fd = readFileDescriptor();
- LOG_ALWAYS_FATAL_IF(fd == -1);
- ret.push_back(fd);
+ int fd = readFileDescriptor();
+ LOG_ALWAYS_FATAL_IF(fd == -1);
+ ret.push_back(fd);
+ }
+ setDataPosition(initPosition);
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+#endif
+ } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) {
+ for (const auto& fd : *rpcFields->mFds) {
+ ret.push_back(toRawFd(fd));
+ }
}
- setDataPosition(initPosition);
return ret;
}
@@ -621,17 +758,38 @@
return BAD_VALUE;
}
*result = false;
- for (size_t i = 0; i < mObjectsSize; i++) {
- size_t pos = mObjects[i];
- if (pos < offset) continue;
- if (pos + sizeof(flat_binder_object) > offset + len) {
- if (mObjectsSorted) break;
- else continue;
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ size_t pos = kernelFields->mObjects[i];
+ if (pos < offset) continue;
+ if (pos + sizeof(flat_binder_object) > offset + len) {
+ if (kernelFields->mObjectsSorted) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + pos);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ *result = true;
+ break;
+ }
}
- const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- *result = true;
- break;
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (const auto* rpcFields = maybeRpcFields()) {
+ for (uint32_t pos : rpcFields->mObjectPositions) {
+ if (offset <= pos && pos < limit) {
+ const auto* type = reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ if (*type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ *result = true;
+ break;
+ }
+ }
}
}
return NO_ERROR;
@@ -654,23 +812,28 @@
LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
"format must be set before data is written OR on IPC data");
- LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
- mSession = session;
+ mVariantFields.emplace<RpcFields>(session);
}
bool Parcel::isForRpc() const {
- return mSession != nullptr;
+ return std::holds_alternative<RpcFields>(mVariantFields);
}
void Parcel::updateWorkSourceRequestHeaderPosition() const {
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return;
+ }
+
// Only update the request headers once. We only want to point
// to the first headers read/written.
- if (!mRequestHeaderPresent) {
- mWorkSourceRequestHeaderPosition = dataPosition();
- mRequestHeaderPresent = true;
+ if (!kernelFields->mRequestHeaderPresent) {
+ kernelFields->mWorkSourceRequestHeaderPosition = dataPosition();
+ kernelFields->mRequestHeaderPresent = true;
}
}
+#ifdef BINDER_WITH_KERNEL_IPC
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#elif defined(__ANDROID_RECOVERY__)
@@ -678,6 +841,7 @@
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
+#endif // BINDER_WITH_KERNEL_IPC
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
@@ -686,13 +850,18 @@
}
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
- if (CC_LIKELY(!isForRpc())) {
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid()
: IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
// currently the interface identification token is just its name as a string
@@ -701,12 +870,16 @@
bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
{
- if (!mRequestHeaderPresent) {
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return false;
+ }
+ if (!kernelFields->mRequestHeaderPresent) {
return false;
}
const size_t initialPosition = dataPosition();
- setDataPosition(mWorkSourceRequestHeaderPosition);
+ setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition);
status_t err = writeInt32(uid);
setDataPosition(initialPosition);
return err == NO_ERROR;
@@ -714,12 +887,16 @@
uid_t Parcel::readCallingWorkSourceUid() const
{
- if (!mRequestHeaderPresent) {
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return false;
+ }
+ if (!kernelFields->mRequestHeaderPresent) {
return IPCThreadState::kUnsetWorkSource;
}
const size_t initialPosition = dataPosition();
- setDataPosition(mWorkSourceRequestHeaderPosition);
+ setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition);
uid_t uid = readInt32();
setDataPosition(initialPosition);
return uid;
@@ -740,7 +917,8 @@
size_t len,
IPCThreadState* threadState) const
{
- if (CC_LIKELY(!isForRpc())) {
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
// StrictModePolicy.
int32_t strictPolicy = readInt32();
if (threadState == nullptr) {
@@ -766,6 +944,11 @@
header);
return false;
}
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)threadState;
+ return false;
+#endif // BINDER_WITH_KERNEL_IPC
}
// Interface descriptor.
@@ -795,7 +978,10 @@
size_t Parcel::objectsCount() const
{
- return mObjectsSize;
+ if (const auto* kernelFields = maybeKernelFields()) {
+ return kernelFields->mObjectsSize;
+ }
+ return 0;
}
status_t Parcel::errorCheck() const
@@ -1237,13 +1423,43 @@
return err;
}
-status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
-{
- if (isForRpc()) {
- ALOGE("Cannot write file descriptor to remote binder.");
- return BAD_TYPE;
+status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) {
+ if (auto* rpcFields = maybeRpcFields()) {
+ std::variant<base::unique_fd, base::borrowed_fd> fdVariant;
+ if (takeOwnership) {
+ fdVariant = base::unique_fd(fd);
+ } else {
+ fdVariant = base::borrowed_fd(fd);
+ }
+ if (!mAllowFds) {
+ return FDS_NOT_ALLOWED;
+ }
+ switch (rpcFields->mSession->getFileDescriptorTransportMode()) {
+ case RpcSession::FileDescriptorTransportMode::NONE: {
+ return FDS_NOT_ALLOWED;
+ }
+ case RpcSession::FileDescriptorTransportMode::UNIX: {
+ if (rpcFields->mFds == nullptr) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ }
+ size_t dataPos = mDataPos;
+ if (dataPos > UINT32_MAX) {
+ return NO_MEMORY;
+ }
+ if (status_t err = writeInt32(RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR); err != OK) {
+ return err;
+ }
+ if (status_t err = writeInt32(rpcFields->mFds->size()); err != OK) {
+ return err;
+ }
+ rpcFields->mObjectPositions.push_back(dataPos);
+ rpcFields->mFds->push_back(std::move(fdVariant));
+ return OK;
+ }
+ }
}
+#ifdef BINDER_WITH_KERNEL_IPC
flat_binder_object obj;
obj.hdr.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
@@ -1251,6 +1467,12 @@
obj.handle = fd;
obj.cookie = takeOwnership ? 1 : 0;
return writeObject(obj, true);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)fd;
+ (void)takeOwnership;
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::writeDupFileDescriptor(int fd)
@@ -1361,7 +1583,7 @@
const size_t len = val.getFlattenedSize();
const size_t fd_count = val.getFdCount();
- if ((len > INT32_MAX) || (fd_count >= gMaxFds)) {
+ if ((len > INT32_MAX) || (fd_count > kMaxFds)) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return BAD_VALUE;
@@ -1401,8 +1623,12 @@
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
+ auto* kernelFields = maybeKernelFields();
+ LOG_ALWAYS_FATAL_IF(kernelFields == nullptr, "Can't write flat_binder_object to RPC Parcel");
+
+#ifdef BINDER_WITH_KERNEL_IPC
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
- const bool enoughObjects = mObjectsSize < mObjectsCapacity;
+ const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
@@ -1413,14 +1639,14 @@
// fail before modifying our object index
return FDS_NOT_ALLOWED;
}
- mHasFds = mFdsKnown = true;
+ kernelFields->mHasFds = kernelFields->mFdsKnown = true;
}
// Need to write meta-data?
if (nullMetaData || val.binder != 0) {
- mObjects[mObjectsSize] = mDataPos;
+ kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos;
acquire_object(ProcessState::self(), val, this);
- mObjectsSize++;
+ kernelFields->mObjectsSize++;
}
return finishWrite(sizeof(flat_binder_object));
@@ -1431,17 +1657,24 @@
if (err != NO_ERROR) return err;
}
if (!enoughObjects) {
- if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow
- if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow
- size_t newSize = ((mObjectsSize+2)*3)/2;
+ if (kernelFields->mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow
+ if ((kernelFields->mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow
+ size_t newSize = ((kernelFields->mObjectsSize + 2) * 3) / 2;
if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
- binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
+ binder_size_t* objects =
+ (binder_size_t*)realloc(kernelFields->mObjects, newSize * sizeof(binder_size_t));
if (objects == nullptr) return NO_MEMORY;
- mObjects = objects;
- mObjectsCapacity = newSize;
+ kernelFields->mObjects = objects;
+ kernelFields->mObjectsCapacity = newSize;
}
goto restart_write;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)val;
+ (void)nullMetaData;
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::writeNoException()
@@ -1452,55 +1685,70 @@
status_t Parcel::validateReadData(size_t upperBound) const
{
+ const auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ // Can't validate RPC Parcel reads because the location of binder
+ // objects is unknown.
+ return OK;
+ }
+
+#ifdef BINDER_WITH_KERNEL_IPC
// Don't allow non-object reads on object data
- if (mObjectsSorted || mObjectsSize <= 1) {
-data_sorted:
+ if (kernelFields->mObjectsSorted || kernelFields->mObjectsSize <= 1) {
+ data_sorted:
// Expect to check only against the next object
- if (mNextObjectHint < mObjectsSize && upperBound > mObjects[mNextObjectHint]) {
+ if (kernelFields->mNextObjectHint < kernelFields->mObjectsSize &&
+ upperBound > kernelFields->mObjects[kernelFields->mNextObjectHint]) {
// For some reason the current read position is greater than the next object
// hint. Iterate until we find the right object
- size_t nextObject = mNextObjectHint;
+ size_t nextObject = kernelFields->mNextObjectHint;
do {
- if (mDataPos < mObjects[nextObject] + sizeof(flat_binder_object)) {
+ if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) {
// Requested info overlaps with an object
ALOGE("Attempt to read from protected data in Parcel %p", this);
return PERMISSION_DENIED;
}
nextObject++;
- } while (nextObject < mObjectsSize && upperBound > mObjects[nextObject]);
- mNextObjectHint = nextObject;
+ } while (nextObject < kernelFields->mObjectsSize &&
+ upperBound > kernelFields->mObjects[nextObject]);
+ kernelFields->mNextObjectHint = nextObject;
}
return NO_ERROR;
}
// Quickly determine if mObjects is sorted.
- binder_size_t* currObj = mObjects + mObjectsSize - 1;
+ binder_size_t* currObj = kernelFields->mObjects + kernelFields->mObjectsSize - 1;
binder_size_t* prevObj = currObj;
- while (currObj > mObjects) {
+ while (currObj > kernelFields->mObjects) {
prevObj--;
if(*prevObj > *currObj) {
goto data_unsorted;
}
currObj--;
}
- mObjectsSorted = true;
+ kernelFields->mObjectsSorted = true;
goto data_sorted;
data_unsorted:
// Insertion Sort mObjects
// Great for mostly sorted lists. If randomly sorted or reverse ordered mObjects become common,
// switch to std::sort(mObjects, mObjects + mObjectsSize);
- for (binder_size_t* iter0 = mObjects + 1; iter0 < mObjects + mObjectsSize; iter0++) {
+ for (binder_size_t* iter0 = kernelFields->mObjects + 1;
+ iter0 < kernelFields->mObjects + kernelFields->mObjectsSize; iter0++) {
binder_size_t temp = *iter0;
binder_size_t* iter1 = iter0 - 1;
- while (iter1 >= mObjects && *iter1 > temp) {
+ while (iter1 >= kernelFields->mObjects && *iter1 > temp) {
*(iter1 + 1) = *iter1;
iter1--;
}
*(iter1 + 1) = temp;
}
- mNextObjectHint = 0;
- mObjectsSorted = true;
+ kernelFields->mNextObjectHint = 0;
+ kernelFields->mObjectsSorted = true;
goto data_sorted;
+#else // BINDER_WITH_KERNEL_IPC
+ (void)upperBound;
+ return NO_ERROR;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::read(void* outData, size_t len) const
@@ -1513,7 +1761,8 @@
if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
&& len <= pad_size(len)) {
- if (mObjectsSize > 0) {
+ const auto* kernelFields = maybeKernelFields();
+ if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) {
status_t err = validateReadData(mDataPos + pad_size(len));
if(err != NO_ERROR) {
// Still increment the data position by the expected length
@@ -1540,7 +1789,8 @@
if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
&& len <= pad_size(len)) {
- if (mObjectsSize > 0) {
+ const auto* kernelFields = maybeKernelFields();
+ if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) {
status_t err = validateReadData(mDataPos + pad_size(len));
if(err != NO_ERROR) {
// Still increment the data position by the expected length
@@ -1587,7 +1837,8 @@
static_assert(std::is_trivially_copyable_v<T>);
if ((mDataPos+sizeof(T)) <= mDataSize) {
- if (mObjectsSize > 0) {
+ const auto* kernelFields = maybeKernelFields();
+ if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) {
status_t err = validateReadData(mDataPos + sizeof(T));
if(err != NO_ERROR) {
// Still increment the data position by the expected length
@@ -1965,8 +2216,32 @@
return h;
}
-int Parcel::readFileDescriptor() const
-{
+int Parcel::readFileDescriptor() const {
+ if (const auto* rpcFields = maybeRpcFields()) {
+ if (!std::binary_search(rpcFields->mObjectPositions.begin(),
+ rpcFields->mObjectPositions.end(), mDataPos)) {
+ ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the "
+ "object list",
+ this, mDataPos);
+ return BAD_TYPE;
+ }
+
+ int32_t objectType = readInt32();
+ if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ return BAD_TYPE;
+ }
+
+ int32_t fdIndex = readInt32();
+ if (rpcFields->mFds == nullptr || fdIndex < 0 ||
+ static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
+ ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
+ fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0);
+ return BAD_VALUE;
+ }
+ return toRawFd(rpcFields->mFds->at(fdIndex));
+ }
+
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* flat = readObject(true);
if (flat && flat->hdr.type == BINDER_TYPE_FD) {
@@ -1974,10 +2249,13 @@
}
return BAD_TYPE;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
-int Parcel::readParcelFileDescriptor() const
-{
+int Parcel::readParcelFileDescriptor() const {
int32_t hasComm = readInt32();
int fd = readFileDescriptor();
if (hasComm != 0) {
@@ -2086,7 +2364,7 @@
const size_t len = this->readInt32();
const size_t fd_count = this->readInt32();
- if ((len > INT32_MAX) || (fd_count >= gMaxFds)) {
+ if ((len > INT32_MAX) || (fd_count > kMaxFds)) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return BAD_VALUE;
@@ -2130,8 +2408,15 @@
return err;
}
+
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* Parcel::readObject(bool nullMetaData) const
{
+ const auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return nullptr;
+ }
+
const size_t DPOS = mDataPos;
if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) {
const flat_binder_object* obj
@@ -2146,9 +2431,9 @@
}
// Ensure that this object is valid...
- binder_size_t* const OBJS = mObjects;
- const size_t N = mObjectsSize;
- size_t opos = mNextObjectHint;
+ binder_size_t* const OBJS = kernelFields->mObjects;
+ const size_t N = kernelFields->mObjectsSize;
+ size_t opos = kernelFields->mNextObjectHint;
if (N > 0) {
ALOGV("Parcel %p looking for obj at %zu, hint=%zu",
@@ -2167,7 +2452,7 @@
// Found it!
ALOGV("Parcel %p found obj %zu at index %zu with forward search",
this, DPOS, opos);
- mNextObjectHint = opos+1;
+ kernelFields->mNextObjectHint = opos + 1;
ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos);
return obj;
}
@@ -2180,7 +2465,7 @@
// Found it!
ALOGV("Parcel %p found obj %zu at index %zu with backward search",
this, DPOS, opos);
- mNextObjectHint = opos+1;
+ kernelFields->mNextObjectHint = opos + 1;
ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos);
return obj;
}
@@ -2190,21 +2475,29 @@
}
return nullptr;
}
+#endif // BINDER_WITH_KERNEL_IPC
-void Parcel::closeFileDescriptors()
-{
- size_t i = mObjectsSize;
- if (i > 0) {
- //ALOGI("Closing file descriptors for %zu objects...", i);
- }
- while (i > 0) {
- i--;
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- //ALOGI("Closing fd: %ld", flat->handle);
- close(flat->handle);
+void Parcel::closeFileDescriptors() {
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ size_t i = kernelFields->mObjectsSize;
+ if (i > 0) {
+ // ALOGI("Closing file descriptors for %zu objects...", i);
}
+ while (i > 0) {
+ i--;
+ const flat_binder_object* flat =
+ reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ // ALOGI("Closing fd: %ld", flat->handle);
+ close(flat->handle);
+ }
+ }
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (auto* rpcFields = maybeRpcFields()) {
+ rpcFields->mFds.reset();
}
}
@@ -2220,35 +2513,44 @@
uintptr_t Parcel::ipcObjects() const
{
- return reinterpret_cast<uintptr_t>(mObjects);
+ if (const auto* kernelFields = maybeKernelFields()) {
+ return reinterpret_cast<uintptr_t>(kernelFields->mObjects);
+ }
+ return 0;
}
size_t Parcel::ipcObjectsCount() const
{
- return mObjectsSize;
+ if (const auto* kernelFields = maybeKernelFields()) {
+ return kernelFields->mObjectsSize;
+ }
+ return 0;
}
-void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount, release_func relFunc)
-{
+void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
+ size_t objectsCount, release_func relFunc) {
// this code uses 'mOwner == nullptr' to understand whether it owns memory
LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
freeData();
+ auto* kernelFields = maybeKernelFields();
+ LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData.
+
mData = const_cast<uint8_t*>(data);
mDataSize = mDataCapacity = dataSize;
- mObjects = const_cast<binder_size_t*>(objects);
- mObjectsSize = mObjectsCapacity = objectsCount;
+ kernelFields->mObjects = const_cast<binder_size_t*>(objects);
+ kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsCount;
mOwner = relFunc;
+#ifdef BINDER_WITH_KERNEL_IPC
binder_size_t minOffset = 0;
- for (size_t i = 0; i < mObjectsSize; i++) {
- binder_size_t offset = mObjects[i];
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ binder_size_t offset = kernelFields->mObjects[i];
if (offset < minOffset) {
ALOGE("%s: bad object offset %" PRIu64 " < %" PRIu64 "\n",
__func__, (uint64_t)offset, (uint64_t)minOffset);
- mObjectsSize = 0;
+ kernelFields->mObjectsSize = 0;
break;
}
const flat_binder_object* flat
@@ -2266,12 +2568,54 @@
// WARNING: callers of ipcSetDataReference need to make sure they
// don't rely on mObjectsSize in their release_func.
- mObjectsSize = 0;
+ kernelFields->mObjectsSize = 0;
break;
}
minOffset = offset + sizeof(flat_binder_object);
}
scanForFds();
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL_IF(objectsCount != 0,
+ "Non-zero objects count passed to Parcel with kernel driver disabled");
+#endif // BINDER_WITH_KERNEL_IPC
+}
+
+status_t Parcel::rpcSetDataReference(
+ const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
+ const uint32_t* objectTable, size_t objectTableSize,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds,
+ release_func relFunc) {
+ // this code uses 'mOwner == nullptr' to understand whether it owns memory
+ LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
+
+ LOG_ALWAYS_FATAL_IF(session == nullptr);
+
+ freeData();
+ markForRpc(session);
+
+ auto* rpcFields = maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc.
+
+ mData = const_cast<uint8_t*>(data);
+ mDataSize = mDataCapacity = dataSize;
+ mOwner = relFunc;
+
+ if (objectTableSize != ancillaryFds.size()) {
+ ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size());
+ freeData(); // don't leak mData
+ return BAD_VALUE;
+ }
+
+ rpcFields->mObjectPositions.reserve(objectTableSize);
+ for (size_t i = 0; i < objectTableSize; i++) {
+ rpcFields->mObjectPositions.push_back(objectTable[i]);
+ }
+ if (!ancillaryFds.empty()) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ *rpcFields->mFds = std::move(ancillaryFds);
+ }
+
+ return OK;
}
void Parcel::print(TextOutput& to, uint32_t /*flags*/) const
@@ -2284,15 +2628,19 @@
} else if (dataSize() > 0) {
const uint8_t* DATA = data();
to << indent << HexDump(DATA, dataSize()) << dedent;
- const binder_size_t* OBJS = mObjects;
- const size_t N = objectsCount();
- for (size_t i=0; i<N; i++) {
- const flat_binder_object* flat
- = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);
- to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
- << TypeCode(flat->hdr.type & 0x7f7f7f00)
- << " = " << flat->binder;
+#ifdef BINDER_WITH_KERNEL_IPC
+ if (const auto* kernelFields = maybeKernelFields()) {
+ const binder_size_t* OBJS = kernelFields->mObjects;
+ const size_t N = objectsCount();
+ for (size_t i = 0; i < N; i++) {
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(DATA + OBJS[i]);
+ to << endl
+ << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
+ << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder;
+ }
}
+#endif // BINDER_WITH_KERNEL_IPC
} else {
to << "NULL";
}
@@ -2302,36 +2650,48 @@
void Parcel::releaseObjects()
{
- size_t i = mObjectsSize;
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return;
+ }
+
+#ifdef BINDER_WITH_KERNEL_IPC
+ size_t i = kernelFields->mObjectsSize;
if (i == 0) {
return;
}
sp<ProcessState> proc(ProcessState::self());
uint8_t* const data = mData;
- binder_size_t* const objects = mObjects;
+ binder_size_t* const objects = kernelFields->mObjects;
while (i > 0) {
i--;
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(data+objects[i]);
+ const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
release_object(proc, *flat, this);
}
+#endif // BINDER_WITH_KERNEL_IPC
}
void Parcel::acquireObjects()
{
- size_t i = mObjectsSize;
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return;
+ }
+
+#ifdef BINDER_WITH_KERNEL_IPC
+ size_t i = kernelFields->mObjectsSize;
if (i == 0) {
return;
}
const sp<ProcessState> proc(ProcessState::self());
uint8_t* const data = mData;
- binder_size_t* const objects = mObjects;
+ binder_size_t* const objects = kernelFields->mObjects;
while (i > 0) {
i--;
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(data+objects[i]);
+ const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
acquire_object(proc, *flat, this);
}
+#endif // BINDER_WITH_KERNEL_IPC
}
void Parcel::freeData()
@@ -2345,7 +2705,9 @@
if (mOwner) {
LOG_ALLOC("Parcel %p: freeing other owner data", this);
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
+ auto* kernelFields = maybeKernelFields();
+ mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
+ kernelFields ? kernelFields->mObjectsSize : 0);
} else {
LOG_ALLOC("Parcel %p: freeing allocated data", this);
releaseObjects();
@@ -2358,7 +2720,8 @@
}
free(mData);
}
- if (mObjects) free(mObjects);
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields && kernelFields->mObjects) free(kernelFields->mObjects);
}
}
@@ -2433,13 +2796,18 @@
ALOGV("restartWrite Setting data size of %p to %zu", this, mDataSize);
ALOGV("restartWrite Setting data pos of %p to %zu", this, mDataPos);
- free(mObjects);
- mObjects = nullptr;
- mObjectsSize = mObjectsCapacity = 0;
- mNextObjectHint = 0;
- mObjectsSorted = false;
- mHasFds = false;
- mFdsKnown = true;
+ if (auto* kernelFields = maybeKernelFields()) {
+ free(kernelFields->mObjects);
+ kernelFields->mObjects = nullptr;
+ kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = 0;
+ kernelFields->mNextObjectHint = 0;
+ kernelFields->mObjectsSorted = false;
+ kernelFields->mHasFds = false;
+ kernelFields->mFdsKnown = true;
+ } else if (auto* rpcFields = maybeRpcFields()) {
+ rpcFields->mObjectPositions.clear();
+ rpcFields->mFds.reset();
+ }
mAllowFds = true;
return NO_ERROR;
@@ -2453,17 +2821,27 @@
return BAD_VALUE;
}
+ auto* kernelFields = maybeKernelFields();
+ auto* rpcFields = maybeRpcFields();
+
// If shrinking, first adjust for any objects that appear
// after the new data size.
- size_t objectsSize = mObjectsSize;
+ size_t objectsSize =
+ kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size();
if (desired < mDataSize) {
if (desired == 0) {
objectsSize = 0;
} else {
- while (objectsSize > 0) {
- if (mObjects[objectsSize-1] < desired)
- break;
- objectsSize--;
+ if (kernelFields) {
+ while (objectsSize > 0) {
+ if (kernelFields->mObjects[objectsSize - 1] < desired) break;
+ objectsSize--;
+ }
+ } else {
+ while (objectsSize > 0) {
+ if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
+ objectsSize--;
+ }
}
}
}
@@ -2484,7 +2862,7 @@
}
binder_size_t* objects = nullptr;
- if (objectsSize) {
+ if (kernelFields && objectsSize) {
objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
if (!objects) {
free(data);
@@ -2495,20 +2873,27 @@
// Little hack to only acquire references on objects
// we will be keeping.
- size_t oldObjectsSize = mObjectsSize;
- mObjectsSize = objectsSize;
+ size_t oldObjectsSize = kernelFields->mObjectsSize;
+ kernelFields->mObjectsSize = objectsSize;
acquireObjects();
- mObjectsSize = oldObjectsSize;
+ kernelFields->mObjectsSize = oldObjectsSize;
+ }
+ if (rpcFields) {
+ if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
+ free(data);
+ return status;
+ }
}
if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
- if (objects && mObjects) {
- memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
+ if (objects && kernelFields && kernelFields->mObjects) {
+ memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
+ mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
+ kernelFields ? kernelFields->mObjectsSize : 0);
mOwner = nullptr;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
@@ -2516,43 +2901,55 @@
gParcelGlobalAllocCount++;
mData = data;
- mObjects = objects;
mDataSize = (mDataSize < desired) ? mDataSize : desired;
ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
mDataCapacity = desired;
- mObjectsSize = mObjectsCapacity = objectsSize;
- mNextObjectHint = 0;
- mObjectsSorted = false;
+ if (kernelFields) {
+ kernelFields->mObjects = objects;
+ kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsSize;
+ kernelFields->mNextObjectHint = 0;
+ kernelFields->mObjectsSorted = false;
+ }
} else if (mData) {
- if (objectsSize < mObjectsSize) {
+ if (kernelFields && objectsSize < kernelFields->mObjectsSize) {
+#ifdef BINDER_WITH_KERNEL_IPC
// Need to release refs on any objects we are dropping.
const sp<ProcessState> proc(ProcessState::self());
- for (size_t i=objectsSize; i<mObjectsSize; i++) {
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
+ for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) {
+ const flat_binder_object* flat =
+ reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
if (flat->hdr.type == BINDER_TYPE_FD) {
// will need to rescan because we may have lopped off the only FDs
- mFdsKnown = false;
+ kernelFields->mFdsKnown = false;
}
release_object(proc, *flat, this);
}
if (objectsSize == 0) {
- free(mObjects);
- mObjects = nullptr;
- mObjectsCapacity = 0;
+ free(kernelFields->mObjects);
+ kernelFields->mObjects = nullptr;
+ kernelFields->mObjectsCapacity = 0;
} else {
binder_size_t* objects =
- (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
+ (binder_size_t*)realloc(kernelFields->mObjects,
+ objectsSize * sizeof(binder_size_t));
if (objects) {
- mObjects = objects;
- mObjectsCapacity = objectsSize;
+ kernelFields->mObjects = objects;
+ kernelFields->mObjectsCapacity = objectsSize;
}
}
- mObjectsSize = objectsSize;
- mNextObjectHint = 0;
- mObjectsSorted = false;
+ kernelFields->mObjectsSize = objectsSize;
+ kernelFields->mNextObjectHint = 0;
+ kernelFields->mObjectsSorted = false;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel");
+#endif // BINDER_WITH_KERNEL_IPC
+ }
+ if (rpcFields) {
+ if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
+ return status;
+ }
}
// We own the data, so we can just do a realloc().
@@ -2588,9 +2985,12 @@
return NO_MEMORY;
}
- if(!(mDataCapacity == 0 && mObjects == nullptr
- && mObjectsCapacity == 0)) {
- ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired);
+ if (!(mDataCapacity == 0 &&
+ (kernelFields == nullptr ||
+ (kernelFields->mObjects == nullptr && kernelFields->mObjectsCapacity == 0)))) {
+ ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity,
+ kernelFields ? kernelFields->mObjects : nullptr,
+ kernelFields ? kernelFields->mObjectsCapacity : 0, desired);
}
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
@@ -2607,6 +3007,35 @@
return NO_ERROR;
}
+status_t Parcel::truncateRpcObjects(size_t newObjectsSize) {
+ auto* rpcFields = maybeRpcFields();
+ if (newObjectsSize == 0) {
+ rpcFields->mObjectPositions.clear();
+ if (rpcFields->mFds) {
+ rpcFields->mFds->clear();
+ }
+ return OK;
+ }
+ while (rpcFields->mObjectPositions.size() > newObjectsSize) {
+ uint32_t pos = rpcFields->mObjectPositions.back();
+ rpcFields->mObjectPositions.pop_back();
+ const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ const auto fdIndex =
+ *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType));
+ if (rpcFields->mFds == nullptr || fdIndex < 0 ||
+ static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
+ ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
+ fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0);
+ return BAD_VALUE;
+ }
+ // In practice, this always removes the last element.
+ rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex);
+ }
+ }
+ return OK;
+}
+
void Parcel::initState()
{
LOG_ALLOC("Parcel %p: initState", this);
@@ -2617,39 +3046,23 @@
mDataPos = 0;
ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
- mSession = nullptr;
- mObjects = nullptr;
- mObjectsSize = 0;
- mObjectsCapacity = 0;
- mNextObjectHint = 0;
- mObjectsSorted = false;
- mHasFds = false;
- mFdsKnown = true;
+ mVariantFields.emplace<KernelFields>();
mAllowFds = true;
mDeallocZero = false;
mOwner = nullptr;
- mWorkSourceRequestHeaderPosition = 0;
- mRequestHeaderPresent = false;
-
- // racing multiple init leads only to multiple identical write
- if (gMaxFds == 0) {
- struct rlimit result;
- if (!getrlimit(RLIMIT_NOFILE, &result)) {
- gMaxFds = (size_t)result.rlim_cur;
- //ALOGI("parcel fd limit set to %zu", gMaxFds);
- } else {
- ALOGW("Unable to getrlimit: %s", strerror(errno));
- gMaxFds = 1024;
- }
- }
}
void Parcel::scanForFds() const {
- status_t status = hasFileDescriptorsInRange(0, dataSize(), &mHasFds);
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return;
+ }
+ status_t status = hasFileDescriptorsInRange(0, dataSize(), &kernelFields->mHasFds);
ALOGE_IF(status != NO_ERROR, "Error %d calling hasFileDescriptorsInRange()", status);
- mFdsKnown = true;
+ kernelFields->mFdsKnown = true;
}
+#ifdef BINDER_WITH_KERNEL_IPC
size_t Parcel::getBlobAshmemSize() const
{
// This used to return the size of all blobs that were written to ashmem, now we're returning
@@ -2660,10 +3073,15 @@
size_t Parcel::getOpenAshmemSize() const
{
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return 0;
+ }
+
size_t openAshmemSize = 0;
- for (size_t i = 0; i < mObjectsSize; i++) {
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
const flat_binder_object* flat =
- reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
+ reinterpret_cast<const flat_binder_object*>(mData + kernelFields->mObjects[i]);
// cookie is compared against zero for historical reasons
// > obj.cookie = takeOwnership ? 1 : 0;
@@ -2677,6 +3095,7 @@
}
return openAshmemSize;
}
+#endif // BINDER_WITH_KERNEL_IPC
// --- Parcel::Blob ---
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4a01d81..7faff47 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -35,14 +35,15 @@
#include <errno.h>
#include <fcntl.h>
-#include <mutex>
+#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
+#include <mutex>
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
@@ -100,6 +101,11 @@
sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{
+#ifdef BINDER_IPC_32BIT
+ LOG_ALWAYS_FATAL("32-bit binder IPC is not supported for new devices starting in Android P. If "
+ "you do need to use this mode, please see b/232423610 or file an issue with "
+ "AOSP upstream as otherwise this will be removed soon.");
+#endif
if (driver == nullptr) {
std::lock_guard<std::mutex> l(gProcessMutex);
@@ -170,6 +176,10 @@
// the thread handler is installed
if (gProcess) {
gProcess->mForked = true;
+
+ // "O_CLOFORK"
+ close(gProcess->mDriverFD);
+ gProcess->mDriverFD = -1;
}
gProcessMutex.unlock();
}
@@ -182,7 +192,6 @@
ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
"*startThreadPool when zero threads are requested.");
}
-
mThreadPoolStarted = true;
spawnPooledThread(true);
}
@@ -290,12 +299,17 @@
return &mHandleToObject.editItemAt(handle);
}
+// see b/166779391: cannot change the VNDK interface, so access like this
+extern sp<BBinder> the_context_object;
+
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
+ if (handle == 0 && the_context_object != nullptr) return the_context_object;
+
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
@@ -386,6 +400,9 @@
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = sp<PoolThread>::make(isMain);
t->run(name.string());
+ pthread_mutex_lock(&mThreadCountLock);
+ mKernelStartedThreads++;
+ pthread_mutex_unlock(&mThreadCountLock);
}
}
@@ -402,12 +419,20 @@
return result;
}
-size_t ProcessState::getThreadPoolMaxThreadCount() const {
+size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
// may actually be one more than this, if join is called
- if (mThreadPoolStarted) return mMaxThreads;
+ if (mThreadPoolStarted) {
+ return mCurrentThreads < mKernelStartedThreads
+ ? mMaxThreads
+ : mMaxThreads + mCurrentThreads - mKernelStartedThreads;
+ }
// must not be initialized or maybe has poll thread setup, we
// currently don't track this in libbinder
- return 0;
+ LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0,
+ "Expecting 0 kernel started threads but have"
+ " %zu",
+ mKernelStartedThreads);
+ return mCurrentThreads;
}
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
@@ -415,6 +440,8 @@
static const char* const names[] = {
[static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
DRIVER_FEATURES_PATH "oneway_spam_detection",
+ [static_cast<int>(DriverFeature::EXTENDED_ERROR)] =
+ DRIVER_FEATURES_PATH "extended_error",
};
int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
char on;
@@ -491,6 +518,8 @@
mExecutingThreadsCount(0),
mWaitingForThreads(0),
mMaxThreads(DEFAULT_MAX_BINDER_THREADS),
+ mCurrentThreads(0),
+ mKernelStartedThreads(0),
mStarvationStartTimeMs(0),
mForked(false),
mThreadPoolStarted(false),
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index ace5cd5..096d5cc 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -24,18 +24,21 @@
#include <thread>
#include <vector>
-#include <android-base/file.h>
#include <android-base/hex.h>
#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
#include <log/log.h>
+#include <utils/Compat.h>
+#include "BuildFlags.h"
#include "FdTrigger.h"
+#include "OS.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
#include "RpcWireFormat.h"
+#include "Utils.h"
namespace android {
@@ -121,28 +124,36 @@
mProtocolVersion = version;
}
+void RpcServer::setSupportedFileDescriptorTransportModes(
+ const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
+ mSupportedFileDescriptorTransportModes.reset();
+ for (RpcSession::FileDescriptorTransportMode mode : modes) {
+ mSupportedFileDescriptorTransportModes.set(static_cast<size_t>(mode));
+ }
+}
+
void RpcServer::setRootObject(const sp<IBinder>& binder) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
mRootObjectFactory = nullptr;
mRootObjectWeak = mRootObject = binder;
}
void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
mRootObject.clear();
mRootObjectFactory = nullptr;
mRootObjectWeak = binder;
}
void RpcServer::setPerSessionRootObject(
- std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) {
- std::lock_guard<std::mutex> _l(mLock);
+ std::function<sp<IBinder>(const void*, size_t)>&& makeObject) {
+ RpcMutexLockGuard _l(mLock);
mRootObject.clear();
mRootObjectWeak.clear();
mRootObjectFactory = std::move(makeObject);
}
sp<IBinder> RpcServer::getRootObject() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
bool hasWeak = mRootObjectWeak.unsafe_get();
sp<IBinder> ret = mRootObjectWeak.promote();
ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
@@ -150,7 +161,7 @@
}
std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return mCtx->getCertificate(format);
}
@@ -159,15 +170,17 @@
}
void RpcServer::start() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!");
- mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
+ mJoinThread =
+ std::make_unique<RpcMaybeThread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
+ rpcJoinIfSingleThreaded(*mJoinThread);
}
void RpcServer::join() {
{
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join.");
LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
mJoinThreadRunning = true;
@@ -177,14 +190,16 @@
status_t status;
while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
- sockaddr_storage addr;
- socklen_t addrLen = sizeof(addr);
+ std::array<uint8_t, kRpcAddressSize> addr;
+ static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small");
+ socklen_t addrLen = addr.size();
unique_fd clientFd(
- TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr),
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(addr.data()),
&addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK)));
- LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address");
+ LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)),
+ "Truncated address");
if (clientFd < 0) {
ALOGE("Could not accept4 socket: %s", strerror(errno));
@@ -193,24 +208,31 @@
LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
{
- std::lock_guard<std::mutex> _l(mLock);
- std::thread thread =
- std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this),
- std::move(clientFd), addr, addrLen);
- mConnectingThreads[thread.get_id()] = std::move(thread);
+ RpcMutexLockGuard _l(mLock);
+ RpcMaybeThread thread = RpcMaybeThread(&RpcServer::establishConnection,
+ sp<RpcServer>::fromExisting(this),
+ std::move(clientFd), addr, addrLen);
+
+ auto& threadRef = mConnectingThreads[thread.get_id()];
+ threadRef = std::move(thread);
+ rpcJoinIfSingleThreaded(threadRef);
}
}
LOG_RPC_DETAIL("RpcServer::join exiting with %s", statusToString(status).c_str());
- {
- std::lock_guard<std::mutex> _l(mLock);
+ if constexpr (kEnableRpcThreads) {
+ RpcMutexLockGuard _l(mLock);
mJoinThreadRunning = false;
+ } else {
+ // Multi-threaded builds clear this in shutdown(), but we need it valid
+ // so the loop above exits cleanly
+ mShutdownTrigger = nullptr;
}
mShutdownCv.notify_all();
}
bool RpcServer::shutdown() {
- std::unique_lock<std::mutex> _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
if (mShutdownTrigger == nullptr) {
LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)");
return false;
@@ -221,10 +243,16 @@
for (auto& [id, session] : mSessions) {
(void)id;
// server lock is a more general lock
- std::lock_guard<std::mutex> _lSession(session->mMutex);
+ RpcMutexLockGuard _lSession(session->mMutex);
session->mShutdownTrigger->trigger();
}
+ if constexpr (!kEnableRpcThreads) {
+ // In single-threaded mode we're done here, everything else that
+ // needs to happen should be at the end of RpcServer::join()
+ return true;
+ }
+
while (mJoinThreadRunning || !mConnectingThreads.empty() || !mSessions.empty()) {
if (std::cv_status::timeout == mShutdownCv.wait_for(_l, std::chrono::seconds(1))) {
ALOGE("Waiting for RpcServer to shut down (1s w/o progress). Join thread running: %d, "
@@ -252,7 +280,7 @@
}
std::vector<sp<RpcSession>> RpcServer::listSessions() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
std::vector<sp<RpcSession>> sessions;
for (auto& [id, session] : mSessions) {
(void)id;
@@ -262,12 +290,12 @@
}
size_t RpcServer::numUninitializedSessions() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return mConnectingThreads.size();
}
void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
- const sockaddr_storage addr, socklen_t addrLen) {
+ std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen) {
// 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);
@@ -288,7 +316,8 @@
RpcConnectionHeader header;
if (status == OK) {
iovec iov{&header, sizeof(header)};
- status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {});
+ status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1,
+ std::nullopt, /*ancillaryFds=*/nullptr);
if (status != OK) {
ALOGE("Failed to read ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -302,8 +331,8 @@
if (header.sessionIdSize == kSessionIdBytes) {
sessionId.resize(header.sessionIdSize);
iovec iov{sessionId.data(), sessionId.size()};
- status =
- client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {});
+ status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1,
+ std::nullopt, /*ancillaryFds=*/nullptr);
if (status != OK) {
ALOGE("Failed to read session ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -333,7 +362,8 @@
};
iovec iov{&response, sizeof(response)};
- status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, {});
+ status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1,
+ std::nullopt, nullptr);
if (status != OK) {
ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
// still need to cleanup before we can return
@@ -341,12 +371,12 @@
}
}
- std::thread thisThread;
+ RpcMaybeThread thisThread;
sp<RpcSession> session;
{
- std::unique_lock<std::mutex> _l(server->mLock);
+ RpcMutexUniqueLock _l(server->mLock);
- auto threadId = server->mConnectingThreads.find(std::this_thread::get_id());
+ auto threadId = server->mConnectingThreads.find(rpc_this_thread::get_id());
LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(),
"Must establish connection on owned thread");
thisThread = std::move(threadId->second);
@@ -380,24 +410,34 @@
return;
}
- base::unique_fd fd(TEMP_FAILURE_RETRY(
- open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)));
- if (!base::ReadFully(fd, sessionId.data(), sessionId.size())) {
- ALOGE("Could not read from /dev/urandom to create session ID");
+ auto status = getRandomBytes(sessionId.data(), sessionId.size());
+ if (status != OK) {
+ ALOGE("Failed to read random session ID: %s", strerror(-status));
return;
}
} while (server->mSessions.end() != server->mSessions.find(sessionId));
- session = RpcSession::make();
+ session = sp<RpcSession>::make(nullptr);
session->setMaxIncomingThreads(server->mMaxThreads);
if (!session->setProtocolVersion(protocolVersion)) return;
+ if (header.fileDescriptorTransportMode <
+ server->mSupportedFileDescriptorTransportModes.size() &&
+ server->mSupportedFileDescriptorTransportModes.test(
+ header.fileDescriptorTransportMode)) {
+ session->setFileDescriptorTransportMode(
+ static_cast<RpcSession::FileDescriptorTransportMode>(
+ header.fileDescriptorTransportMode));
+ } else {
+ ALOGE("Rejecting connection: FileDescriptorTransportMode is not supported: %hhu",
+ header.fileDescriptorTransportMode);
+ return;
+ }
+
// if null, falls back to server root
sp<IBinder> sessionSpecificRoot;
if (server->mRootObjectFactory != nullptr) {
- sessionSpecificRoot =
- server->mRootObjectFactory(reinterpret_cast<const sockaddr*>(&addr),
- addrLen);
+ sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen);
if (sessionSpecificRoot == nullptr) {
ALOGE("Warning: server returned null from root object factory");
}
@@ -484,7 +524,7 @@
LOG_RPC_DETAIL("Dropping session with address %s",
base::HexString(id.data(), id.size()).c_str());
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
auto it = mSessions.find(id);
LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s",
base::HexString(id.data(), id.size()).c_str());
@@ -498,17 +538,17 @@
}
bool RpcServer::hasServer() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return mServer.ok();
}
unique_fd RpcServer::releaseServer() {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
return std::move(mServer);
}
status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
- std::lock_guard<std::mutex> _l(mLock);
+ RpcMutexLockGuard _l(mLock);
if (mServer.ok()) {
ALOGE("Each RpcServer can only have one server.");
return INVALID_OPERATION;
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index d40778a..80f6a37 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -21,7 +21,6 @@
#include <dlfcn.h>
#include <inttypes.h>
#include <poll.h>
-#include <pthread.h>
#include <unistd.h>
#include <string_view>
@@ -34,19 +33,17 @@
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
#include <binder/Stability.h>
+#include <utils/Compat.h>
#include <utils/String8.h>
#include "FdTrigger.h"
+#include "OS.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
#include "RpcWireFormat.h"
#include "Utils.h"
-#ifdef __GLIBC__
-extern "C" pid_t gettid();
-#endif
-
-#ifndef __ANDROID_RECOVERY__
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
#include <android_runtime/vm.h>
#include <jni.h>
#endif
@@ -63,7 +60,7 @@
RpcSession::~RpcSession() {
LOG_RPC_DETAIL("RpcSession destroyed %p", this);
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mConnections.mIncoming.size() != 0,
"Should not be able to destroy a session with servers in use.");
}
@@ -80,7 +77,7 @@
}
void RpcSession::setMaxIncomingThreads(size_t threads) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
"Must set max incoming threads before setting up connections, but has %zu "
"client(s) and %zu server(s)",
@@ -89,12 +86,12 @@
}
size_t RpcSession::getMaxIncomingThreads() {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
return mMaxIncomingThreads;
}
void RpcSession::setMaxOutgoingThreads(size_t threads) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
"Must set max outgoing threads before setting up connections, but has %zu "
"client(s) and %zu server(s)",
@@ -103,7 +100,7 @@
}
size_t RpcSession::getMaxOutgoingThreads() {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
return mMaxOutgoingThreads;
}
@@ -116,7 +113,7 @@
return false;
}
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
if (mProtocolVersion && version > *mProtocolVersion) {
ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u",
*mProtocolVersion, version);
@@ -128,10 +125,18 @@
}
std::optional<uint32_t> RpcSession::getProtocolVersion() {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
return mProtocolVersion;
}
+void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) {
+ mFileDescriptorTransportMode = mode;
+}
+
+RpcSession::FileDescriptorTransportMode RpcSession::getFileDescriptorTransportMode() {
+ return mFileDescriptorTransportMode;
+}
+
status_t RpcSession::setupUnixDomainClient(const char* path) {
return setupSocketClient(UnixSocketAddress(path));
}
@@ -152,13 +157,7 @@
}
status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
- // Why passing raw fd? When fd is passed as reference, Clang analyzer sees that the variable
- // `fd` is a moved-from object. To work-around the issue, unwrap the raw fd from the outer `fd`,
- // pass the raw fd by value to the lambda, and then finally wrap it in unique_fd inside the
- // lambda.
- return setupClient([&, raw = fd.release()](const std::vector<uint8_t>& sessionId,
- bool incoming) -> status_t {
- unique_fd fd(raw);
+ return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
if (!fd.ok()) {
fd = request();
if (!fd.ok()) return BAD_VALUE;
@@ -167,7 +166,9 @@
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 status = initAndAddConnection(std::move(fd), sessionId, incoming);
+ fd = unique_fd(); // Explicitly reset after move to avoid analyzer warning.
+ return status;
});
}
@@ -208,7 +209,7 @@
}
bool RpcSession::shutdownAndWait(bool wait) {
- std::unique_lock<std::mutex> _l(mMutex);
+ RpcMutexUniqueLock _l(mMutex);
LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Shutdown trigger not installed");
mShutdownTrigger->trigger();
@@ -221,6 +222,7 @@
}
_l.unlock();
+
mRpcBinderState->clear();
return true;
@@ -255,7 +257,7 @@
status_t RpcSession::readId() {
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
}
@@ -281,7 +283,7 @@
mCv.notify_all();
}
-void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock,
+void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock,
const sp<RpcSession>& session) {
while (session->mConnections.mIncoming.size() > 0) {
if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
@@ -292,11 +294,11 @@
}
}
-void RpcSession::preJoinThreadOwnership(std::thread thread) {
- LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+void RpcSession::preJoinThreadOwnership(RpcMaybeThread thread) {
+ LOG_ALWAYS_FATAL_IF(thread.get_id() != rpc_this_thread::get_id(), "Must own this thread");
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
mConnections.mThreads[thread.get_id()] = std::move(thread);
}
}
@@ -323,7 +325,7 @@
}
namespace {
-#ifdef __ANDROID_RECOVERY__
+#if !defined(__ANDROID__) || defined(__ANDROID_RECOVERY__)
class JavaThreadAttacher {};
#else
// RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If
@@ -403,8 +405,8 @@
sp<RpcSession::EventListener> listener;
{
- std::lock_guard<std::mutex> _l(session->mMutex);
- auto it = session->mConnections.mThreads.find(std::this_thread::get_id());
+ RpcMutexLockGuard _l(session->mMutex);
+ auto it = session->mConnections.mThreads.find(rpc_this_thread::get_id());
LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end());
it->second.detach();
session->mConnections.mThreads.erase(it);
@@ -437,7 +439,7 @@
status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
bool incoming)>& connectAndInit) {
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0,
"Must only setup session once, but already has %zu clients",
mConnections.mOutgoing.size());
@@ -499,7 +501,11 @@
return status;
}
+#ifdef BINDER_RPC_SINGLE_THREADED
+ constexpr size_t outgoingThreads = 1;
+#else // BINDER_RPC_SINGLE_THREADED
size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
+#endif // BINDER_RPC_SINGLE_THREADED
ALOGI_IF(outgoingThreads != numThreadsAvailable,
"Server hints client to start %zu outgoing threads, but client will only start %zu "
"because it is preconfigured to start at most %zu outgoing threads.",
@@ -613,6 +619,7 @@
RpcConnectionHeader header{
.version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
.options = 0,
+ .fileDescriptorTransportMode = static_cast<uint8_t>(mFileDescriptorTransportMode),
.sessionIdSize = static_cast<uint16_t>(sessionId.size()),
};
@@ -621,8 +628,8 @@
}
iovec headerIov{&header, sizeof(header)};
- auto sendHeaderStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, {});
+ auto sendHeaderStatus = server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1,
+ std::nullopt, nullptr);
if (sendHeaderStatus != OK) {
ALOGE("Could not write connection header to socket: %s",
statusToString(sendHeaderStatus).c_str());
@@ -633,7 +640,8 @@
iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())),
sessionId.size()};
auto sendSessionIdStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, {});
+ server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1,
+ std::nullopt, nullptr);
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
base::HexString(sessionId.data(), sessionId.size()).c_str(),
@@ -652,14 +660,14 @@
}
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;
+ RpcMutex mutex;
+ RpcConditionVariable joinCv;
+ RpcMutexUniqueLock lock(mutex);
+ RpcMaybeThread thread;
sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this);
bool ownershipTransferred = false;
- thread = std::thread([&]() {
- std::unique_lock<std::mutex> threadLock(mutex);
+ thread = RpcMaybeThread([&]() {
+ RpcMutexUniqueLock threadLock(mutex);
std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport);
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
sp<RpcSession> session = thiz;
@@ -675,6 +683,7 @@
RpcSession::join(std::move(session), std::move(setupResult));
});
+ rpcJoinIfSingleThreaded(thread);
joinCv.wait(lock, [&] { return ownershipTransferred; });
LOG_ALWAYS_FATAL_IF(!ownershipTransferred);
return OK;
@@ -694,9 +703,9 @@
status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) {
sp<RpcConnection> connection = sp<RpcConnection>::make();
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
connection->rpcTransport = std::move(rpcTransport);
- connection->exclusiveTid = gettid();
+ connection->exclusiveTid = rpcGetThreadId();
mConnections.mOutgoing.push_back(connection);
}
@@ -706,10 +715,7 @@
mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
}
- {
- std::lock_guard<std::mutex> _l(mMutex);
- connection->exclusiveTid = std::nullopt;
- }
+ clearConnectionTid(connection);
return status;
}
@@ -722,6 +728,7 @@
LOG_ALWAYS_FATAL_IF(mEventListener != nullptr);
LOG_ALWAYS_FATAL_IF(eventListener == nullptr);
LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr);
+ LOG_ALWAYS_FATAL_IF(mCtx != nullptr);
mShutdownTrigger = FdTrigger::make();
if (mShutdownTrigger == nullptr) return false;
@@ -735,7 +742,7 @@
sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
if (mConnections.mIncoming.size() >= mMaxIncomingThreads) {
ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)",
@@ -753,7 +760,7 @@
sp<RpcConnection> session = sp<RpcConnection>::make();
session->rpcTransport = std::move(rpcTransport);
- session->exclusiveTid = gettid();
+ session->exclusiveTid = rpcGetThreadId();
mConnections.mIncoming.push_back(session);
mConnections.mMaxIncoming = mConnections.mIncoming.size();
@@ -762,7 +769,7 @@
}
bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) {
- std::unique_lock<std::mutex> _l(mMutex);
+ RpcMutexUniqueLock _l(mMutex);
if (auto it =
std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection);
it != mConnections.mIncoming.end()) {
@@ -779,6 +786,15 @@
return false;
}
+void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) {
+ RpcMutexUniqueLock _l(mMutex);
+ connection->exclusiveTid = std::nullopt;
+ if (mConnections.mWaitingThreads > 0) {
+ _l.unlock();
+ mAvailableConnectionCv.notify_one();
+ }
+}
+
std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) {
return mCtx->getCertificate(format);
}
@@ -789,8 +805,8 @@
connection->mConnection = nullptr;
connection->mReentrant = false;
- pid_t tid = gettid();
- std::unique_lock<std::mutex> _l(session->mMutex);
+ uint64_t tid = rpcGetThreadId();
+ RpcMutexUniqueLock _l(session->mMutex);
session->mConnections.mWaitingThreads++;
while (true) {
@@ -876,7 +892,7 @@
return OK;
}
-void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+void RpcSession::ExclusiveConnection::findConnection(uint64_t tid, sp<RpcConnection>* exclusive,
sp<RpcConnection>* available,
std::vector<sp<RpcConnection>>& sockets,
size_t socketsIndexHint) {
@@ -908,12 +924,7 @@
// is using this fd, and it retains the right to it. So, we don't give up
// exclusive ownership, and no thread is freed.
if (!mReentrant && mConnection != nullptr) {
- std::unique_lock<std::mutex> _l(mSession->mMutex);
- mConnection->exclusiveTid = std::nullopt;
- if (mSession->mConnections.mWaitingThreads > 0) {
- _l.unlock();
- mSession->mAvailableConnectionCv.notify_one();
- }
+ mSession->clearConnectionTid(mConnection);
}
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 6d89064..0ae75cd 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -21,12 +21,14 @@
#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
#include "Debug.h"
#include "RpcWireFormat.h"
+#include "Utils.h"
#include <random>
@@ -34,7 +36,7 @@
namespace android {
-using base::ScopeGuard;
+using base::StringPrintf;
#if RPC_FLAKE_PRONE
void rpcMaybeWaitToFlake() {
@@ -42,13 +44,22 @@
[[clang::no_destroy]] static std::mutex m;
unsigned num;
{
- std::lock_guard<std::mutex> lock(m);
+ RpcMutexLockGuard lock(m);
num = r();
}
if (num % 10 == 0) usleep(num % 1000);
}
#endif
+static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) {
+ switch (mode) {
+ case RpcSession::FileDescriptorTransportMode::NONE:
+ return false;
+ case RpcSession::FileDescriptorTransportMode::UNIX:
+ return true;
+ }
+}
+
RpcState::RpcState() {}
RpcState::~RpcState() {}
@@ -77,7 +88,7 @@
return INVALID_OPERATION;
}
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
// TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map
@@ -153,7 +164,7 @@
return BAD_VALUE;
}
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -188,7 +199,7 @@
// extra reference counting packets now.
if (binder->remoteBinder()) return OK;
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
auto it = mNodeForAddress.find(address);
@@ -216,17 +227,17 @@
}
size_t RpcState::countBinders() {
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
return mNodeForAddress.size();
}
void RpcState::dump() {
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
dumpLocked();
}
void RpcState::clear() {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) {
LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(),
@@ -309,9 +320,11 @@
mData.reset(new (std::nothrow) uint8_t[size]);
}
-status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) {
+status_t RpcState::rpcSend(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
for (int i = 0; i < niovs; i++) {
LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
what, i + 1, niovs, connection->rpcTransport.get(),
@@ -320,7 +333,8 @@
if (status_t status =
connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
- iovs, niovs, altPoll);
+ iovs, niovs, altPoll,
+ ancillaryFds);
status != OK) {
LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -331,11 +345,14 @@
return OK;
}
-status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) {
+status_t RpcState::rpcRec(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
- iovs, niovs, {});
+ iovs, niovs, std::nullopt,
+ ancillaryFds);
status != OK) {
LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -355,7 +372,7 @@
const sp<RpcSession>& session, uint32_t* version) {
RpcNewSessionResponse response;
iovec iov{&response, sizeof(response)};
- if (status_t status = rpcRec(connection, session, "new session response", &iov, 1);
+ if (status_t status = rpcRec(connection, session, "new session response", &iov, 1, nullptr);
status != OK) {
return status;
}
@@ -369,14 +386,15 @@
.msg = RPC_CONNECTION_INIT_OKAY,
};
iovec iov{&init, sizeof(init)};
- return rpcSend(connection, session, "connection init", &iov, 1);
+ return rpcSend(connection, session, "connection init", &iov, 1, std::nullopt);
}
status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session) {
RpcOutgoingConnectionInit init;
iovec iov{&init, sizeof(init)};
- if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK)
+ if (status_t status = rpcRec(connection, session, "connection init", &iov, 1, nullptr);
+ status != OK)
return status;
static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY));
@@ -448,20 +466,12 @@
status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection,
const sp<IBinder>& binder, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply, uint32_t flags) {
- if (!data.isForRpc()) {
- ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code "
- "%" PRIu32,
- binder.get(), code);
- return BAD_TYPE;
+ std::string errorMsg;
+ if (status_t status = validateParcel(session, data, &errorMsg); status != OK) {
+ ALOGE("Refusing to send RPC on binder %p code %" PRIu32 ": Parcel %p failed validation: %s",
+ binder.get(), code, &data, errorMsg.c_str());
+ return status;
}
-
- if (data.objectsCount() != 0) {
- ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p "
- "code %" PRIu32,
- &data, binder.get(), code);
- return BAD_TYPE;
- }
-
uint64_t address;
if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status;
@@ -477,7 +487,7 @@
uint64_t asyncNumber = 0;
if (address != 0) {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(address);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -493,14 +503,21 @@
}
}
- LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) -
- sizeof(RpcWireTransaction) <
- data.dataSize(),
- "Too much data %zu", data.dataSize());
+ auto* rpcFields = data.maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+ Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(),
+ rpcFields->mObjectPositions.size()};
+
+ uint32_t bodySize;
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(),
+ &bodySize) ||
+ __builtin_add_overflow(objectTableSpan.byteSize(), bodySize,
+ &bodySize),
+ "Too much data %zu", data.dataSize());
RpcWireHeader command{
.command = RPC_COMMAND_TRANSACT,
- .bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()),
+ .bodySize = bodySize,
};
RpcWireTransaction transaction{
@@ -508,6 +525,8 @@
.code = code,
.flags = flags,
.asyncNumber = asyncNumber,
+ // bodySize didn't overflow => this cast is safe
+ .parcelDataSize = static_cast<uint32_t>(data.dataSize()),
};
constexpr size_t kWaitMaxUs = 1000000;
@@ -516,31 +535,33 @@
// Oneway calls have no sync point, so if many are sent before, whether this
// is a twoway or oneway transaction, they may have filled up the socket.
- // So, make sure we drain them before polling.
- std::function<status_t()> drainRefs = [&] {
- if (waitUs > kWaitLogUs) {
- ALOGE("Cannot send command, trying to process pending refcounts. Waiting %zuus. Too "
- "many oneway calls?",
- waitUs);
- }
-
- if (waitUs > 0) {
- usleep(waitUs);
- waitUs = std::min(kWaitMaxUs, waitUs * 2);
- } else {
- waitUs = 1;
- }
-
- return drainCommands(connection, session, CommandType::CONTROL_ONLY);
- };
+ // So, make sure we drain them before polling
iovec iovs[]{
{&command, sizeof(RpcWireHeader)},
{&transaction, sizeof(RpcWireTransaction)},
{const_cast<uint8_t*>(data.data()), data.dataSize()},
+ objectTableSpan.toIovec(),
};
- if (status_t status =
- rpcSend(connection, session, "transaction", iovs, arraysize(iovs), drainRefs);
+ if (status_t status = rpcSend(
+ connection, session, "transaction", iovs, arraysize(iovs),
+ [&] {
+ if (waitUs > kWaitLogUs) {
+ ALOGE("Cannot send command, trying to process pending refcounts. Waiting "
+ "%zuus. Too many oneway calls?",
+ waitUs);
+ }
+
+ if (waitUs > 0) {
+ usleep(waitUs);
+ waitUs = std::min(kWaitMaxUs, waitUs * 2);
+ } else {
+ waitUs = 1;
+ }
+
+ return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ },
+ rpcFields->mFds.get());
status != OK) {
// TODO(b/167966510): need to undo onBinderLeaving - we know the
// refcount isn't successfully transferred.
@@ -563,7 +584,7 @@
static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount) {
(void)p;
- delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
+ delete[] const_cast<uint8_t*>(data);
(void)dataSize;
LOG_ALWAYS_FATAL_IF(objects != nullptr);
LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount);
@@ -571,43 +592,74 @@
status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply) {
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
RpcWireHeader command;
while (true) {
iovec iov{&command, sizeof(command)};
- if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1);
+ if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1,
+ enableAncillaryFds(session->getFileDescriptorTransportMode())
+ ? &ancillaryFds
+ : nullptr);
status != OK)
return status;
if (command.command == RPC_COMMAND_REPLY) break;
- if (status_t status = processCommand(connection, session, command, CommandType::ANY);
+ if (status_t status = processCommand(connection, session, command, CommandType::ANY,
+ std::move(ancillaryFds));
status != OK)
return status;
+
+ // Reset to avoid spurious use-after-move warning from clang-tidy.
+ ancillaryFds = decltype(ancillaryFds)();
}
- CommandData data(command.bodySize);
- if (!data.valid()) return NO_MEMORY;
+ const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value());
- iovec iov{data.data(), command.bodySize};
- if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK)
- return status;
-
- if (command.bodySize < sizeof(RpcWireReply)) {
+ if (command.bodySize < rpcReplyWireSize) {
ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!",
sizeof(RpcWireReply), command.bodySize);
(void)session->shutdownAndWait(false);
return BAD_VALUE;
}
- RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
- if (rpcReply->status != OK) return rpcReply->status;
+
+ RpcWireReply rpcReply;
+ memset(&rpcReply, 0, sizeof(RpcWireReply)); // zero because of potential short read
+
+ CommandData data(command.bodySize - rpcReplyWireSize);
+ if (!data.valid()) return NO_MEMORY;
+
+ iovec iovs[]{
+ {&rpcReply, rpcReplyWireSize},
+ {data.data(), data.size()},
+ };
+ if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr);
+ status != OK)
+ return status;
+
+ if (rpcReply.status != OK) return rpcReply.status;
+
+ Span<const uint8_t> parcelSpan = {data.data(), data.size()};
+ Span<const uint32_t> objectTableSpan;
+ if (session->getProtocolVersion().value() >=
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
+ Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(rpcReply.parcelDataSize);
+ std::optional<Span<const uint32_t>> maybeSpan =
+ objectTableBytes.reinterpret<const uint32_t>();
+ if (!maybeSpan.has_value()) {
+ ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32
+ " sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!",
+ command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize,
+ objectTableBytes.size);
+ return BAD_VALUE;
+ }
+ objectTableSpan = *maybeSpan;
+ }
data.release();
- reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
- nullptr, 0, cleanup_reply_data);
-
- reply->markForRpc(session);
-
- return OK;
+ return reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size,
+ objectTableSpan.data, objectTableSpan.size,
+ std::move(ancillaryFds), cleanup_reply_data);
}
status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection,
@@ -618,7 +670,7 @@
};
{
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexLockGuard _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(addr);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -643,31 +695,32 @@
.bodySize = sizeof(RpcDecStrong),
};
iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}};
- return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs));
+ return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt);
}
status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
RpcWireHeader command;
iovec iov{&command, sizeof(command)};
- if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1);
+ if (status_t status =
+ rpcRec(connection, session, "command header (for server)", &iov, 1,
+ enableAncillaryFds(session->getFileDescriptorTransportMode()) ? &ancillaryFds
+ : nullptr);
status != OK)
return status;
- return processCommand(connection, session, command, type);
+ return processCommand(connection, session, command, type, std::move(ancillaryFds));
}
status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
- uint8_t buf;
while (true) {
- size_t num_bytes;
- status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes);
+ status_t status = connection->rpcTransport->pollRead();
if (status == WOULD_BLOCK) break;
if (status != OK) return status;
- if (!num_bytes) break;
status = getAndExecuteCommand(connection, session, type);
if (status != OK) return status;
@@ -675,28 +728,33 @@
return OK;
}
-status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcWireHeader& command,
- CommandType type) {
+status_t RpcState::processCommand(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command, CommandType type,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
+#ifdef BINDER_WITH_KERNEL_IPC
IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
IPCThreadState::SpGuard spGuard{
.address = __builtin_frame_address(0),
- .context = "processing binder RPC command",
+ .context = "processing binder RPC command (where RpcServer::setPerSessionRootObject is "
+ "used to distinguish callers)",
};
const IPCThreadState::SpGuard* origGuard;
if (kernelBinderState != nullptr) {
origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
}
- ScopeGuard guardUnguard = [&]() {
+
+ base::ScopeGuard guardUnguard = [&]() {
if (kernelBinderState != nullptr) {
kernelBinderState->restoreGetCallingSpGuard(origGuard);
}
};
+#endif // BINDER_WITH_KERNEL_IPC
switch (command.command) {
case RPC_COMMAND_TRANSACT:
if (type != CommandType::ANY) return BAD_TYPE;
- return processTransact(connection, session, command);
+ return processTransact(connection, session, command, std::move(ancillaryFds));
case RPC_COMMAND_DEC_STRONG:
return processDecStrong(connection, session, command);
}
@@ -710,8 +768,10 @@
(void)session->shutdownAndWait(false);
return DEAD_OBJECT;
}
-status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcWireHeader& command) {
+status_t RpcState::processTransact(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
CommandData transactionData(command.bodySize);
@@ -719,10 +779,12 @@
return NO_MEMORY;
}
iovec iov{transactionData.data(), transactionData.size()};
- if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK)
+ if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1, nullptr);
+ status != OK)
return status;
- return processTransactInternal(connection, session, std::move(transactionData));
+ return processTransactInternal(connection, session, std::move(transactionData),
+ std::move(ancillaryFds));
}
static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -734,9 +796,10 @@
(void)objectsCount;
}
-status_t RpcState::processTransactInternal(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- CommandData transactionData) {
+status_t RpcState::processTransactInternal(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ CommandData transactionData,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
// for 'recursive' calls to this, we have already read and processed the
// binder from the transaction data and taken reference counts into account,
// so it is cached here.
@@ -780,7 +843,7 @@
(void)session->shutdownAndWait(false);
replyStatus = BAD_VALUE;
} else if (oneway) {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it->second.binder.promote() != target) {
ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr);
@@ -824,54 +887,78 @@
reply.markForRpc(session);
if (replyStatus == OK) {
+ Span<const uint8_t> parcelSpan = {transaction->data,
+ transactionData.size() -
+ offsetof(RpcWireTransaction, data)};
+ Span<const uint32_t> objectTableSpan;
+ if (session->getProtocolVersion().value() >
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
+ Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(transaction->parcelDataSize);
+ std::optional<Span<const uint32_t>> maybeSpan =
+ objectTableBytes.reinterpret<const uint32_t>();
+ if (!maybeSpan.has_value()) {
+ ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu "
+ "sizeofHeader=%zu parcelSize=%" PRId32
+ " objectTableBytesSize=%zu. Terminating!",
+ transactionData.size(), sizeof(RpcWireTransaction),
+ transaction->parcelDataSize, objectTableBytes.size);
+ return BAD_VALUE;
+ }
+ objectTableSpan = *maybeSpan;
+ }
+
Parcel data;
// transaction->data is owned by this function. Parcel borrows this data and
// only holds onto it for the duration of this function call. Parcel will be
// deleted before the 'transactionData' object.
- data.ipcSetDataReference(transaction->data,
- transactionData.size() - offsetof(RpcWireTransaction, data),
- nullptr /*object*/, 0 /*objectCount*/,
- do_nothing_to_transact_data);
- data.markForRpc(session);
- if (target) {
- bool origAllowNested = connection->allowNested;
- connection->allowNested = !oneway;
+ replyStatus =
+ data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size,
+ objectTableSpan.data, objectTableSpan.size,
+ std::move(ancillaryFds), do_nothing_to_transact_data);
+ // Reset to avoid spurious use-after-move warning from clang-tidy.
+ ancillaryFds = std::remove_reference<decltype(ancillaryFds)>::type();
- replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
+ if (replyStatus == OK) {
+ if (target) {
+ bool origAllowNested = connection->allowNested;
+ connection->allowNested = !oneway;
- connection->allowNested = origAllowNested;
- } else {
- LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
+ replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
- switch (transaction->code) {
- case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
- replyStatus = reply.writeInt32(session->getMaxIncomingThreads());
- break;
- }
- case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
- // for client connections, this should always report the value
- // originally returned from the server, so this is asserting
- // that it exists
- replyStatus = reply.writeByteVector(session->mId);
- break;
- }
- default: {
- sp<RpcServer> server = session->server();
- if (server) {
- switch (transaction->code) {
- case RPC_SPECIAL_TRANSACT_GET_ROOT: {
- sp<IBinder> root = session->mSessionSpecificRootObject
- ?: server->getRootObject();
- replyStatus = reply.writeStrongBinder(root);
- break;
+ connection->allowNested = origAllowNested;
+ } else {
+ LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
+
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+ replyStatus = reply.writeInt32(session->getMaxIncomingThreads());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+ // for client connections, this should always report the value
+ // originally returned from the server, so this is asserting
+ // that it exists
+ replyStatus = reply.writeByteVector(session->mId);
+ break;
+ }
+ default: {
+ sp<RpcServer> server = session->server();
+ if (server) {
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+ sp<IBinder> root = session->mSessionSpecificRootObject
+ ?: server->getRootObject();
+ replyStatus = reply.writeStrongBinder(root);
+ break;
+ }
+ default: {
+ replyStatus = UNKNOWN_TRANSACTION;
+ }
}
- default: {
- replyStatus = UNKNOWN_TRANSACTION;
- }
+ } else {
+ ALOGE("Special command sent, but no server object attached.");
}
- } else {
- ALOGE("Special command sent, but no server object attached.");
}
}
}
@@ -897,7 +984,7 @@
// downside: asynchronous transactions may drown out synchronous
// transactions.
{
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
// last refcount dropped after this transaction happened
if (it == mNodeForAddress.end()) return OK;
@@ -943,49 +1030,69 @@
replyStatus = flushExcessBinderRefs(session, addr, target);
}
- LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) -
- sizeof(RpcWireReply) <
- reply.dataSize(),
- "Too much data for reply %zu", reply.dataSize());
+ std::string errorMsg;
+ if (status_t status = validateParcel(session, reply, &errorMsg); status != OK) {
+ ALOGE("Reply Parcel failed validation: %s", errorMsg.c_str());
+ // Forward the error to the client of the transaction.
+ reply.freeData();
+ reply.markForRpc(session);
+ replyStatus = status;
+ }
+ auto* rpcFields = reply.maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+
+ const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value());
+
+ Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(),
+ rpcFields->mObjectPositions.size()};
+
+ uint32_t bodySize;
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) ||
+ __builtin_add_overflow(objectTableSpan.byteSize(), bodySize,
+ &bodySize),
+ "Too much data for reply %zu", reply.dataSize());
RpcWireHeader cmdReply{
.command = RPC_COMMAND_REPLY,
- .bodySize = static_cast<uint32_t>(sizeof(RpcWireReply) + reply.dataSize()),
+ .bodySize = bodySize,
};
RpcWireReply rpcReply{
.status = replyStatus,
+ // NOTE: Not necessarily written to socket depending on session
+ // version.
+ // NOTE: bodySize didn't overflow => this cast is safe
+ .parcelDataSize = static_cast<uint32_t>(reply.dataSize()),
+ .reserved = {0, 0, 0},
};
-
iovec iovs[]{
{&cmdReply, sizeof(RpcWireHeader)},
- {&rpcReply, sizeof(RpcWireReply)},
+ {&rpcReply, rpcReplyWireSize},
{const_cast<uint8_t*>(reply.data()), reply.dataSize()},
+ objectTableSpan.toIovec(),
};
- return rpcSend(connection, session, "reply", iovs, arraysize(iovs));
+ return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt,
+ rpcFields->mFds.get());
}
status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
- CommandData commandData(command.bodySize);
- if (!commandData.valid()) {
- return NO_MEMORY;
- }
- iovec iov{commandData.data(), commandData.size()};
- if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK)
- return status;
-
if (command.bodySize != sizeof(RpcDecStrong)) {
ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcDecStrong. Terminating!",
sizeof(RpcDecStrong), command.bodySize);
(void)session->shutdownAndWait(false);
return BAD_VALUE;
}
- RpcDecStrong* body = reinterpret_cast<RpcDecStrong*>(commandData.data());
- uint64_t addr = RpcWireAddress::toRaw(body->address);
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcDecStrong body;
+ iovec iov{&body, sizeof(RpcDecStrong)};
+ if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1, nullptr);
+ status != OK)
+ return status;
+
+ uint64_t addr = RpcWireAddress::toRaw(body.address);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr);
@@ -1002,19 +1109,19 @@
return BAD_VALUE;
}
- if (it->second.timesSent < body->amount) {
+ if (it->second.timesSent < body.amount) {
ALOGE("Record of sending binder %zu times, but requested decStrong for %" PRIu64 " of %u",
- it->second.timesSent, addr, body->amount);
+ it->second.timesSent, addr, body.amount);
return OK;
}
LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64,
addr);
- LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body->amount,
+ LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body.amount,
it->second.timesSent);
- it->second.timesSent -= body->amount;
+ it->second.timesSent -= body.amount;
sp<IBinder> tempHold = tryEraseNode(it);
_l.unlock();
tempHold = nullptr; // destructor may make binder calls on this session
@@ -1022,6 +1129,50 @@
return OK;
}
+status_t RpcState::validateParcel(const sp<RpcSession>& session, const Parcel& parcel,
+ std::string* errorMsg) {
+ auto* rpcFields = parcel.maybeRpcFields();
+ if (rpcFields == nullptr) {
+ *errorMsg = "Parcel not crafted for RPC call";
+ return BAD_TYPE;
+ }
+
+ if (rpcFields->mSession != session) {
+ *errorMsg = "Parcel's session doesn't match";
+ return BAD_TYPE;
+ }
+
+ uint32_t protocolVersion = session->getProtocolVersion().value();
+ if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE &&
+ !rpcFields->mObjectPositions.empty()) {
+ *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version "
+ "(%" PRIu32 ") is too old, must be at least %" PRIu32,
+ protocolVersion,
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE);
+ return BAD_VALUE;
+ }
+
+ if (rpcFields->mFds && !rpcFields->mFds->empty()) {
+ switch (session->getFileDescriptorTransportMode()) {
+ case RpcSession::FileDescriptorTransportMode::NONE:
+ *errorMsg =
+ "Parcel has file descriptors, but no file descriptor transport is enabled";
+ return FDS_NOT_ALLOWED;
+ case RpcSession::FileDescriptorTransportMode::UNIX: {
+ constexpr size_t kMaxFdsPerMsg = 253;
+ if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
+ *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix "
+ "domain socket: %zu (max is %zu)",
+ rpcFields->mFds->size(), kMaxFdsPerMsg);
+ return BAD_VALUE;
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
sp<IBinder> ref;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index f4a0894..6fb2e4a 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -19,6 +19,7 @@
#include <binder/IBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
#include <map>
#include <optional>
@@ -178,28 +179,39 @@
size_t mSize;
};
- [[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs,
- int niovs, const std::function<status_t()>& altPoll = nullptr);
- [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs,
- int niovs);
+ [[nodiscard]] status_t rpcSend(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds =
+ nullptr);
+ [[nodiscard]] status_t rpcRec(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr);
[[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply);
- [[nodiscard]] status_t processCommand(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- const RpcWireHeader& command, CommandType type);
- [[nodiscard]] status_t processTransact(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- const RpcWireHeader& command);
- [[nodiscard]] status_t processTransactInternal(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- CommandData transactionData);
+ [[nodiscard]] status_t processCommand(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command, CommandType type,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ [[nodiscard]] status_t processTransact(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ [[nodiscard]] status_t processTransactInternal(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ CommandData transactionData,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
[[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session,
const RpcWireHeader& command);
+ // Whether `parcel` is compatible with `session`.
+ [[nodiscard]] static status_t validateParcel(const sp<RpcSession>& session,
+ const Parcel& parcel, std::string* errorMsg);
+
struct BinderNode {
// Two cases:
// A - local binder we are serving
@@ -257,7 +269,7 @@
// false - session shutdown, halt
[[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
- std::mutex mNodeMutex;
+ RpcMutex mNodeMutex;
bool mTerminated = false;
uint32_t mNextId = 0;
// binders known by both sides of a session
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 7cfc780..7cc58cd 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -18,6 +18,7 @@
#include <log/log.h>
#include <poll.h>
+#include <stddef.h>
#include <binder/RpcTransportRaw.h>
@@ -28,30 +29,37 @@
namespace {
+// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
+constexpr size_t kMaxFdsPerMsg = 253;
+
// RpcTransport with TLS disabled.
class RpcTransportRaw : public RpcTransport {
public:
explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
- status_t peek(void* buf, size_t size, size_t* out_size) override {
- ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
+ status_t pollRead(void) override {
+ uint8_t buf;
+ ssize_t ret = TEMP_FAILURE_RETRY(
+ ::recv(mSocket.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT));
if (ret < 0) {
int savedErrno = errno;
if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
return WOULD_BLOCK;
}
- LOG_RPC_DETAIL("RpcTransport peek(): %s", strerror(savedErrno));
+ LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno));
return -savedErrno;
+ } else if (ret == 0) {
+ return DEAD_OBJECT;
}
- *out_size = static_cast<size_t>(ret);
return OK;
}
template <typename SendOrReceive>
- status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- SendOrReceive sendOrReceiveFun, const char* funName,
- int16_t event, const std::function<status_t()>& altPoll) {
+ status_t interruptableReadOrWrite(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs, SendOrReceive sendOrReceiveFun,
+ const char* funName, int16_t event,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll) {
MAYBE_WAIT_IN_FLAKE_MODE;
if (niovs < 0) {
@@ -81,15 +89,7 @@
bool havePolled = false;
while (true) {
- msghdr msg{
- .msg_iov = iovs,
- // posix uses int, glibc uses size_t. niovs is a
- // non-negative int and can be cast to either.
- .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
- };
- ssize_t processSize =
- TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL));
-
+ ssize_t processSize = sendOrReceiveFun(iovs, niovs);
if (processSize < 0) {
int savedErrno = errno;
@@ -126,7 +126,7 @@
}
if (altPoll) {
- if (status_t status = altPoll(); status != OK) return status;
+ if (status_t status = (*altPoll)(); status != OK) return status;
if (fdTrigger->isTriggered()) {
return DEAD_OBJECT;
}
@@ -139,16 +139,123 @@
}
}
- status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) override {
- return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT,
- altPoll);
+ status_t interruptableWriteFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+ override {
+ bool sentFds = false;
+ auto send = [&](iovec* iovs, int niovs) -> ssize_t {
+ if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) {
+ if (ancillaryFds->size() > kMaxFdsPerMsg) {
+ // This shouldn't happen because we check the FD count in RpcState.
+ ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). "
+ "Aborting session.",
+ ancillaryFds->size(), kMaxFdsPerMsg);
+ errno = EINVAL;
+ return -1;
+ }
+
+ // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
+ // use memcpy.
+ int fds[kMaxFdsPerMsg];
+ for (size_t i = 0; i < ancillaryFds->size(); i++) {
+ fds[i] = std::visit([](const auto& fd) { return fd.get(); },
+ ancillaryFds->at(i));
+ }
+ const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
+
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
+ memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
+
+ msg.msg_controllen = CMSG_SPACE(fdsByteSize);
+
+ ssize_t processedSize = TEMP_FAILURE_RETRY(
+ sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
+ if (processedSize > 0) {
+ sentFds = true;
+ }
+ return processedSize;
+ }
+
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ return TEMP_FAILURE_RETRY(sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+ };
+ return interruptableReadOrWrite(fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT, altPoll);
}
- status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) override {
- return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN,
- altPoll);
+ status_t interruptableReadFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
+ auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
+ if (ancillaryFds != nullptr) {
+ int fdBuffer[kMaxFdsPerMsg];
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+ ssize_t processSize =
+ TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+ if (processSize < 0) {
+ return -1;
+ }
+
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
+ // application devs to memcpy the data to ensure memory alignment.
+ size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
+ LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check
+ memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
+ size_t fdCount = dataLen / sizeof(int);
+ ancillaryFds->reserve(ancillaryFds->size() + fdCount);
+ for (size_t i = 0; i < fdCount; i++) {
+ ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
+ }
+ break;
+ }
+ }
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ ALOGE("msg was truncated. Aborting session.");
+ errno = EPIPE;
+ return -1;
+ }
+
+ return processSize;
+ }
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ return TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+ };
+ return interruptableReadOrWrite(fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll);
}
private:
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index bc68c37..09b5c17 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -181,9 +181,10 @@
// |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,
- const std::function<status_t()>& altPoll) {
+ status_t pollForSslError(
+ android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger, const char* fnString,
+ int additionalEvent,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll) {
switch (sslError) {
case SSL_ERROR_WANT_READ:
return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
@@ -198,10 +199,11 @@
bool mHandled = false;
status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger,
- const char* fnString, const std::function<status_t()>& altPoll) {
+ const char* fnString,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll) {
status_t ret;
if (altPoll) {
- ret = altPoll();
+ ret = (*altPoll)();
if (fdTrigger->isTriggered()) ret = DEAD_OBJECT;
} else {
ret = fdTrigger->triggerablePoll(fd, event);
@@ -277,11 +279,16 @@
public:
RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
: mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
- status_t peek(void* buf, size_t size, size_t* out_size) override;
- status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) override;
- status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) override;
+ status_t pollRead(void) override;
+ status_t interruptableWriteFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+ override;
+ status_t interruptableReadFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
private:
android::base::unique_fd mSocket;
@@ -289,9 +296,9 @@
};
// Error code is errno.
-status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_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));
+status_t RpcTransportTls::pollRead(void) {
+ uint8_t buf;
+ auto [ret, errorQueue] = mSsl.call(SSL_peek, &buf, sizeof(buf));
if (ret < 0) {
int err = mSsl.getError(ret);
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
@@ -304,12 +311,15 @@
}
errorQueue.clear();
LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret);
- *out_size = static_cast<size_t>(ret);
return OK;
}
-status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) {
+status_t RpcTransportTls::interruptableWriteFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ (void)ancillaryFds;
+
MAYBE_WAIT_IN_FLAKE_MODE;
if (niovs < 0) return BAD_VALUE;
@@ -350,8 +360,12 @@
return OK;
}
-status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) {
+status_t RpcTransportTls::interruptableReadFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ (void)ancillaryFds;
+
MAYBE_WAIT_IN_FLAKE_MODE;
if (niovs < 0) return BAD_VALUE;
@@ -416,8 +430,8 @@
return false;
}
int sslError = ssl->getError(ret);
- status_t pollStatus =
- errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake", 0, {});
+ status_t pollStatus = errorQueue.pollForSslError(fd, sslError, fdTrigger,
+ "SSL_do_handshake", 0, std::nullopt);
if (pollStatus != OK) return false;
}
}
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 171550e..ff1b01a 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -45,7 +45,8 @@
struct RpcConnectionHeader {
uint32_t version; // maximum supported by caller
uint8_t options;
- uint8_t reservered[9];
+ uint8_t fileDescriptorTransportMode;
+ uint8_t reservered[8];
// Follows is sessionIdSize bytes.
// if size is 0, this is requesting a new session.
uint16_t sessionIdSize;
@@ -108,6 +109,10 @@
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
+//
+// When file descriptors are included in out-of-band data (e.g. in unix domain
+// sockets), they are always paired with the RpcWireHeader bytes of the
+// transaction or reply the file descriptors belong to.
struct RpcWireHeader {
uint32_t command; // RPC_COMMAND_*
@@ -131,7 +136,10 @@
uint64_t asyncNumber;
- uint32_t reserved[4];
+ // The size of the Parcel data directly following RpcWireTransaction.
+ uint32_t parcelDataSize;
+
+ uint32_t reserved[3];
uint8_t data[];
};
@@ -139,9 +147,23 @@
struct RpcWireReply {
int32_t status; // transact return
- uint8_t data[];
+
+ // -- Fields below only transmitted starting at protocol version 1 --
+
+ // The size of the Parcel data directly following RpcWireReply.
+ uint32_t parcelDataSize;
+
+ uint32_t reserved[3];
+
+ // Byte size of RpcWireReply in the wire protocol.
+ static size_t wireSize(uint32_t protocolVersion) {
+ if (protocolVersion == 0) {
+ return sizeof(int32_t);
+ }
+ return sizeof(RpcWireReply);
+ }
};
-static_assert(sizeof(RpcWireReply) == 4);
+static_assert(sizeof(RpcWireReply) == 20);
#pragma clang diagnostic pop
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 83b97d0..dba6587 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -139,6 +139,9 @@
mMessage = String8(message.value_or(String16()));
// Skip over the remote stack trace data
+ const size_t remote_start = parcel.dataPosition();
+ // Get available size before reading more
+ const size_t remote_avail = parcel.dataAvail();
int32_t remote_stack_trace_header_size;
status = parcel.readInt32(&remote_stack_trace_header_size);
if (status != OK) {
@@ -146,13 +149,16 @@
return status;
}
if (remote_stack_trace_header_size < 0 ||
- static_cast<size_t>(remote_stack_trace_header_size) > parcel.dataAvail()) {
+ static_cast<size_t>(remote_stack_trace_header_size) > remote_avail) {
android_errorWriteLog(0x534e4554, "132650049");
setFromStatusT(UNKNOWN_ERROR);
return UNKNOWN_ERROR;
}
- parcel.setDataPosition(parcel.dataPosition() + remote_stack_trace_header_size);
+
+ if (remote_stack_trace_header_size != 0) {
+ parcel.setDataPosition(remote_start + remote_stack_trace_header_size);
+ }
if (mException == EX_SERVICE_SPECIFIC) {
status = parcel.readInt32(&mErrorCode);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index ebb0d27..c91d56c 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -28,9 +28,6 @@
"name": "binderLibTest"
},
{
- "name": "binderRpcTest"
- },
- {
"name": "binderStabilityTest"
},
{
@@ -83,5 +80,24 @@
{
"name": "rustBinderSerializationTest"
}
+ ],
+ "presubmit-large": [
+ {
+ "name": "binderRpcTest"
+ },
+ {
+ "name": "binderRpcTestNoKernel"
+ },
+ {
+ "name": "binderRpcTestSingleThreaded"
+ },
+ {
+ "name": "binderRpcTestSingleThreadedNoKernel"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "binderLibTest"
+ }
]
}
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index d2a5be1..0314b0f 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -18,24 +18,10 @@
#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);
}
-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 ff2fad8..7c6d6f1 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-#include <cstdint>
#include <stddef.h>
+#include <sys/uio.h>
+#include <cstdint>
+#include <optional>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
#include <log/log.h>
+#include <utils/Errors.h>
#define TEST_AND_RETURN(value, expr) \
do { \
@@ -34,6 +35,37 @@
// avoid optimizations
void zeroMemory(uint8_t* data, size_t size);
-android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+// View of contiguous sequence. Similar to std::span.
+template <typename T>
+struct Span {
+ T* data = nullptr;
+ size_t size = 0;
+
+ size_t byteSize() { return size * sizeof(T); }
+
+ iovec toIovec() { return {const_cast<std::remove_const_t<T>*>(data), byteSize()}; }
+
+ // Truncates `this` to a length of `offset` and returns a span with the
+ // remainder.
+ //
+ // Aborts if offset > size.
+ Span<T> splitOff(size_t offset) {
+ LOG_ALWAYS_FATAL_IF(offset > size);
+ Span<T> rest = {data + offset, size - offset};
+ size = offset;
+ return rest;
+ }
+
+ // Returns nullopt if the byte size of `this` isn't evenly divisible by sizeof(U).
+ template <typename U>
+ std::optional<Span<U>> reinterpret() const {
+ // Only allow casting from bytes for simplicity.
+ static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>);
+ if (size % sizeof(U) != 0) {
+ return std::nullopt;
+ }
+ return Span<U>{reinterpret_cast<U*>(data), size / sizeof(U)};
+ }
+};
} // namespace android
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..7574c29 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,23 @@
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+#ifndef BINDER_GET_EXTENDED_ERROR
+/* struct binder_extened_error - extended error information
+ * @id: identifier for the failed operation
+ * @command: command as defined by binder_driver_return_protocol
+ * @param: parameter holding a negative errno value
+ *
+ * Used with BINDER_GET_EXTENDED_ERROR. This extends the error information
+ * returned by the driver upon a failed operation. Userspace can pull this
+ * data to properly handle specific error scenarios.
+ */
+struct binder_extended_error {
+ __u32 id;
+ __u32 command;
+ __s32 param;
+};
+
+#define BINDER_GET_EXTENDED_ERROR _IOWR('b', 17, struct binder_extended_error)
+#endif // BINDER_GET_EXTENDED_ERROR
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7067830..9f7e2c8 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -72,9 +72,9 @@
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual const String16& getInterfaceDescriptor() const;
+ typedef INTERFACE BaseInterface;
protected:
- typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
@@ -85,9 +85,9 @@
{
public:
explicit BpInterface(const sp<IBinder>& remote);
+ typedef INTERFACE BaseInterface;
protected:
- typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index bf02099..8ce3bc9 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -28,6 +28,10 @@
// ---------------------------------------------------------------------------
namespace android {
+/**
+ * Kernel binder thread state. All operations here refer to kernel binder. This
+ * object is allocated per-thread.
+ */
class IPCThreadState
{
public:
@@ -216,6 +220,7 @@
static void freeBuffer(Parcel* parcel,
const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsSize);
+ static void logExtendedError();
const sp<ProcessState> mProcess;
Vector<BBinder*> mPendingStrongDerefs;
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 15dd28f..c7177bd 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -26,9 +26,10 @@
// ---------------------------------------------------------------------------
-class MemoryHeapBase : public virtual BnMemoryHeap
+class MemoryHeapBase : public BnMemoryHeap
{
public:
+ static constexpr auto MEMFD_ALLOW_SEALING_FLAG = 0x00000800;
enum {
READ_ONLY = IMemoryHeap::READ_ONLY,
// memory won't be mapped locally, but will be mapped in the remote
@@ -48,7 +49,7 @@
// Clients of shared files can seal at anytime via syscall, leading to
// TOC/TOU issues if additional seals prevent access from the creating
// process. Alternatively, seccomp fcntl().
- MEMFD_ALLOW_SEALING = 0x00000800
+ MEMFD_ALLOW_SEALING = FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG
};
/*
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index e2b2c51..91febbd 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -20,6 +20,7 @@
#include <map> // for legacy reasons
#include <string>
#include <type_traits>
+#include <variant>
#include <vector>
#include <android-base/unique_fd.h>
@@ -76,6 +77,11 @@
size_t dataCapacity() const;
status_t setDataSize(size_t size);
+
+ // this must only be used to set a data position that was previously returned from
+ // dataPosition(). If writes are made, the exact same types of writes must be made (e.g.
+ // auto i = p.dataPosition(); p.writeInt32(0); p.setDataPosition(i); p.writeInt32(1);).
+ // Writing over objects, such as file descriptors and binders, is not supported.
void setDataPosition(size_t pos) const;
status_t setDataCapacity(size_t size);
@@ -600,16 +606,25 @@
size_t ipcDataSize() const;
uintptr_t ipcObjects() const;
size_t ipcObjectsCount() const;
- void ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount,
- release_func relFunc);
+ void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
+ size_t objectsCount, release_func relFunc);
+ // Takes ownership even when an error is returned.
+ status_t rpcSetDataReference(
+ const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
+ const uint32_t* objectTable, size_t objectTableSize,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds,
+ release_func relFunc);
status_t finishWrite(size_t len);
void releaseObjects();
void acquireObjects();
status_t growData(size_t len);
+ // Clear the Parcel and set the capacity to `desired`.
+ // Doesn't reset the RPC session association.
status_t restartWrite(size_t desired);
+ // Set the capacity to `desired`, truncating the Parcel if necessary.
status_t continueWrite(size_t desired);
+ status_t truncateRpcObjects(size_t newObjectsSize);
status_t writePointer(uintptr_t val);
status_t readPointer(uintptr_t *pArg) const;
uintptr_t readPointer() const;
@@ -1169,10 +1184,20 @@
c->clear(); // must clear before resizing/reserving otherwise move ctors may be called.
if constexpr (is_pointer_equivalent_array_v<T>) {
// could consider POD without gaps and alignment of 4.
- auto data = reinterpret_cast<const T*>(
- readInplace(static_cast<size_t>(size) * sizeof(T)));
+ size_t dataLen;
+ if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) {
+ return -EOVERFLOW;
+ }
+ auto data = reinterpret_cast<const T*>(readInplace(dataLen));
if (data == nullptr) return BAD_VALUE;
- c->insert(c->begin(), data, data + size); // insert should do a reserve().
+ // std::vector::insert and similar methods will require type-dependent
+ // byte alignment when inserting from a const iterator such as `data`,
+ // e.g. 8 byte alignment for int64_t, and so will not work if `data`
+ // is 4 byte aligned (which is all Parcel guarantees). Copying
+ // the contents into the vector directly, where possible, circumvents
+ // this.
+ c->resize(size);
+ memcpy(c->data(), data, dataLen);
} else if constexpr (std::is_same_v<T, bool>
|| std::is_same_v<T, char16_t>) {
c->reserve(size); // avoids default initialization
@@ -1247,19 +1272,57 @@
uint8_t* mData;
size_t mDataSize;
size_t mDataCapacity;
- mutable size_t mDataPos;
- binder_size_t* mObjects;
- size_t mObjectsSize;
- size_t mObjectsCapacity;
- mutable size_t mNextObjectHint;
- mutable bool mObjectsSorted;
+ mutable size_t mDataPos;
- mutable bool mRequestHeaderPresent;
+ // Fields only needed when parcelling for "kernel Binder".
+ struct KernelFields {
+ binder_size_t* mObjects = nullptr;
+ size_t mObjectsSize = 0;
+ size_t mObjectsCapacity = 0;
+ mutable size_t mNextObjectHint = 0;
- mutable size_t mWorkSourceRequestHeaderPosition;
+ mutable size_t mWorkSourceRequestHeaderPosition = 0;
+ mutable bool mRequestHeaderPresent = false;
- mutable bool mFdsKnown;
- mutable bool mHasFds;
+ mutable bool mObjectsSorted = false;
+ mutable bool mFdsKnown = true;
+ mutable bool mHasFds = false;
+ };
+ // Fields only needed when parcelling for RPC Binder.
+ struct RpcFields {
+ RpcFields(const sp<RpcSession>& session);
+
+ // Should always be non-null.
+ const sp<RpcSession> mSession;
+
+ enum ObjectType : int32_t {
+ TYPE_BINDER_NULL = 0,
+ TYPE_BINDER = 1,
+ // FD to be passed via native transport (Trusty IPC or UNIX domain socket).
+ TYPE_NATIVE_FILE_DESCRIPTOR = 2,
+ };
+
+ // Sorted.
+ std::vector<uint32_t> mObjectPositions;
+
+ // File descriptors referenced by the parcel data. Should be indexed
+ // using the offsets in the parcel data. Don't assume the list is in the
+ // same order as `mObjectPositions`.
+ //
+ // Boxed to save space. Lazy allocated.
+ std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds;
+ };
+ std::variant<KernelFields, RpcFields> mVariantFields;
+
+ // Pointer to KernelFields in mVariantFields if present.
+ KernelFields* maybeKernelFields() { return std::get_if<KernelFields>(&mVariantFields); }
+ const KernelFields* maybeKernelFields() const {
+ return std::get_if<KernelFields>(&mVariantFields);
+ }
+ // Pointer to RpcFields in mVariantFields if present.
+ RpcFields* maybeRpcFields() { return std::get_if<RpcFields>(&mVariantFields); }
+ const RpcFields* maybeRpcFields() const { return std::get_if<RpcFields>(&mVariantFields); }
+
bool mAllowFds;
// if this parcelable is involved in a secure transaction, force the
@@ -1268,7 +1331,6 @@
release_func mOwner;
- sp<RpcSession> mSession;
size_t mReserved;
class Blob {
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 675585e..882dfbf 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -29,6 +29,10 @@
class IPCThreadState;
+/**
+ * Kernel binder process state. All operations here refer to kernel binder. This
+ * object is allocated per process.
+ */
class ProcessState : public virtual RefBase {
public:
static sp<ProcessState> self();
@@ -84,14 +88,15 @@
void setCallRestriction(CallRestriction restriction);
/**
- * Get the max number of threads that the kernel can start.
- *
- * Note: this is the lower bound. Additional threads may be started.
+ * Get the max number of threads that have joined the thread pool.
+ * This includes kernel started threads, user joined threads and polling
+ * threads if used.
*/
- size_t getThreadPoolMaxThreadCount() const;
+ size_t getThreadPoolMaxTotalThreadCount() const;
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
+ EXTENDED_ERROR,
};
// Determine whether a feature is supported by the binder driver.
static bool isDriverFeatureEnabled(const DriverFeature feature);
@@ -132,8 +137,12 @@
size_t mExecutingThreadsCount;
// Number of threads calling IPCThreadState::blockUntilThreadAvailable()
size_t mWaitingForThreads;
- // Maximum number for binder threads allowed for this process.
+ // Maximum number of lazy threads to be started in the threadpool by the kernel.
size_t mMaxThreads;
+ // Current number of threads inside the thread pool.
+ size_t mCurrentThreads;
+ // Current number of pooled threads inside the thread pool.
+ size_t mKernelStartedThreads;
// Time when thread pool was emptied
int64_t mStarvationStartTimeMs;
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 6b31812..9318c27 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -18,6 +18,7 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -114,6 +115,15 @@
void setProtocolVersion(uint32_t version);
/**
+ * Set the supported transports for sending and receiving file descriptors.
+ *
+ * Clients will propose a mode when connecting. If the mode is not in the
+ * provided list, the connection will be rejected.
+ */
+ void setSupportedFileDescriptorTransportModes(
+ const std::vector<RpcSession::FileDescriptorTransportMode>& modes);
+
+ /**
* The root object can be retrieved by any client, without any
* authentication. TODO(b/183988761)
*
@@ -125,9 +135,17 @@
*/
void setRootObjectWeak(const wp<IBinder>& binder);
/**
- * Allows a root object to be created for each session
+ * Allows a root object to be created for each session.
+ *
+ * Takes one argument: a callable that is invoked once per new session.
+ * The callable takes two arguments: a type-erased pointer to an OS- and
+ * transport-specific address structure, e.g., sockaddr_vm for vsock, and
+ * an integer representing the size in bytes of that structure. The
+ * callable should validate the size, then cast the type-erased pointer
+ * to a pointer to the actual type of the address, e.g., const void* to
+ * const sockaddr_vm*.
*/
- void setPerSessionRootObject(std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& object);
+ void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object);
sp<IBinder> getRootObject();
/**
@@ -177,25 +195,30 @@
void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
+ static constexpr size_t kRpcAddressSize = 128;
static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
- const sockaddr_storage addr, socklen_t addrLen);
+ std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen);
[[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
const std::unique_ptr<RpcTransportCtx> mCtx;
size_t mMaxThreads = 1;
std::optional<uint32_t> mProtocolVersion;
+ // A mode is supported if the N'th bit is on, where N is the mode enum's value.
+ std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set(
+ static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE));
base::unique_fd mServer; // socket we are accepting sessions on
- std::mutex mLock; // for below
- std::unique_ptr<std::thread> mJoinThread;
+ RpcMutex mLock; // for below
+ std::unique_ptr<RpcMaybeThread> mJoinThread;
bool mJoinThreadRunning = false;
- std::map<std::thread::id, std::thread> mConnectingThreads;
+ std::map<RpcMaybeThread::id, RpcMaybeThread> mConnectingThreads;
+
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
- std::function<sp<IBinder>(const sockaddr*, socklen_t)> mRootObjectFactory;
+ std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
- std::condition_variable mShutdownCv;
+ RpcConditionVariable mShutdownCv;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a579442..a2b28db 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -15,15 +15,16 @@
*/
#pragma once
+#include <android-base/threads.h>
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
+#include <binder/RpcThreads.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <map>
#include <optional>
-#include <thread>
#include <vector>
namespace android {
@@ -37,7 +38,13 @@
constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1;
constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 0;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+
+// Starting with this version:
+//
+// * RpcWireReply is larger (4 bytes -> 20).
+// * RpcWireTransaction and RpcWireReplyV1 include the parcel data size.
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE = 1;
/**
* This represents a session (group of connections) between a client
@@ -88,6 +95,18 @@
[[nodiscard]] bool setProtocolVersion(uint32_t version);
std::optional<uint32_t> getProtocolVersion();
+ enum class FileDescriptorTransportMode : uint8_t {
+ NONE = 0,
+ // Send file descriptors via unix domain socket ancillary data.
+ UNIX = 1,
+ };
+
+ /**
+ * Set the transport for sending and receiving file descriptors.
+ */
+ void setFileDescriptorTransportMode(FileDescriptorTransportMode mode);
+ FileDescriptorTransportMode getFileDescriptorTransportMode();
+
/**
* This should be called once per thread, matching 'join' in the remote
* process.
@@ -199,10 +218,10 @@
public:
void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
- void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session);
+ void waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session);
private:
- std::condition_variable mCv;
+ RpcConditionVariable mCv;
};
friend WaitForShutdownListener;
@@ -211,7 +230,7 @@
// whether this or another thread is currently using this fd to make
// or receive transactions.
- std::optional<pid_t> exclusiveTid;
+ std::optional<uint64_t> exclusiveTid;
bool allowNested = false;
};
@@ -225,7 +244,7 @@
//
// transfer ownership of thread (usually done while a lock is taken on the
// structure which originally owns the thread)
- void preJoinThreadOwnership(std::thread thread);
+ void preJoinThreadOwnership(RpcMaybeThread thread);
// pass FD to thread and read initial connection information
struct PreJoinSetupResult {
// Server connection object associated with this
@@ -257,6 +276,7 @@
sp<RpcConnection> assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
+ void clearConnectionTid(const sp<RpcConnection>& connection);
[[nodiscard]] status_t initShutdownTrigger();
@@ -276,7 +296,7 @@
const sp<RpcConnection>& get() { return mConnection; }
private:
- static void findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ static void findConnection(uint64_t tid, sp<RpcConnection>* exclusive,
sp<RpcConnection>* available,
std::vector<sp<RpcConnection>>& sockets,
size_t socketsIndexHint);
@@ -306,7 +326,7 @@
// For a more complicated case, the client might itself open up a thread to
// serve calls to the server at all times (e.g. if it hosts a callback)
- wp<RpcServer> mForServer; // maybe null, for client sessions
+ wp<RpcServer> mForServer; // maybe null, for client sessions
sp<WaitForShutdownListener> mShutdownListener; // used for client sessions
wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client
@@ -320,13 +340,14 @@
std::unique_ptr<RpcState> mRpcBinderState;
- std::mutex mMutex; // for all below
+ RpcMutex mMutex; // for all below
size_t mMaxIncomingThreads = 0;
size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
std::optional<uint32_t> mProtocolVersion;
+ FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
- std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
+ RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads
struct ThreadState {
size_t mWaitingThreads = 0;
@@ -335,7 +356,7 @@
std::vector<sp<RpcConnection>> mOutgoing;
size_t mMaxIncoming = 0;
std::vector<sp<RpcConnection>> mIncoming;
- std::map<std::thread::id, std::thread> mThreads;
+ std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads;
} mConnections;
};
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
new file mode 100644
index 0000000..8abf04e
--- /dev/null
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <pthread.h>
+
+#include <android-base/threads.h>
+
+#include <functional>
+#include <memory>
+#include <thread>
+
+namespace android {
+
+#ifdef BINDER_RPC_SINGLE_THREADED
+class RpcMutex {
+public:
+ void lock() {}
+ void unlock() {}
+};
+
+class RpcMutexUniqueLock {
+public:
+ RpcMutexUniqueLock(RpcMutex&) {}
+ void unlock() {}
+};
+
+class RpcMutexLockGuard {
+public:
+ RpcMutexLockGuard(RpcMutex&) {}
+};
+
+class RpcConditionVariable {
+public:
+ void notify_one() {}
+ void notify_all() {}
+
+ void wait(RpcMutexUniqueLock&) {}
+
+ template <typename Predicate>
+ void wait(RpcMutexUniqueLock&, Predicate stop_waiting) {
+ LOG_ALWAYS_FATAL_IF(!stop_waiting(), "RpcConditionVariable::wait condition not met");
+ }
+
+ template <typename Duration>
+ std::cv_status wait_for(RpcMutexUniqueLock&, const Duration&) {
+ return std::cv_status::no_timeout;
+ }
+
+ template <typename Duration, typename Predicate>
+ bool wait_for(RpcMutexUniqueLock&, const Duration&, Predicate stop_waiting) {
+ return stop_waiting();
+ }
+};
+
+class RpcMaybeThread {
+public:
+ RpcMaybeThread() = default;
+
+ template <typename Function, typename... Args>
+ RpcMaybeThread(Function&& f, Args&&... args) {
+ // std::function requires a copy-constructible closure,
+ // so we need to wrap both the function and its arguments
+ // in a shared pointer that std::function can copy internally
+ struct Vars {
+ std::decay_t<Function> f;
+ std::tuple<std::decay_t<Args>...> args;
+
+ explicit Vars(Function&& f, Args&&... args)
+ : f(std::move(f)), args(std::move(args)...) {}
+ };
+ auto vars = std::make_shared<Vars>(std::forward<Function>(f), std::forward<Args>(args)...);
+ mFunc = [vars]() { std::apply(std::move(vars->f), std::move(vars->args)); };
+ }
+
+ void join() {
+ if (mFunc) {
+ // Move mFunc into a temporary so we can clear mFunc before
+ // executing the callback. This avoids infinite recursion if
+ // the callee then calls join() again directly or indirectly.
+ decltype(mFunc) func = nullptr;
+ mFunc.swap(func);
+ func();
+ }
+ }
+ void detach() { join(); }
+
+ class id {
+ public:
+ bool operator==(const id&) const { return true; }
+ bool operator!=(const id&) const { return false; }
+ bool operator<(const id&) const { return false; }
+ bool operator<=(const id&) const { return true; }
+ bool operator>(const id&) const { return false; }
+ bool operator>=(const id&) const { return true; }
+ };
+
+ id get_id() const { return id(); }
+
+private:
+ std::function<void(void)> mFunc;
+};
+
+namespace rpc_this_thread {
+static inline RpcMaybeThread::id get_id() {
+ return RpcMaybeThread::id();
+}
+} // namespace rpc_this_thread
+
+static inline uint64_t rpcGetThreadId() {
+ return 0;
+}
+
+static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) {
+ t.join();
+}
+#else // BINDER_RPC_SINGLE_THREADED
+using RpcMutex = std::mutex;
+using RpcMutexUniqueLock = std::unique_lock<std::mutex>;
+using RpcMutexLockGuard = std::lock_guard<std::mutex>;
+using RpcConditionVariable = std::condition_variable;
+using RpcMaybeThread = std::thread;
+namespace rpc_this_thread = std::this_thread;
+
+static inline uint64_t rpcGetThreadId() {
+ return base::GetThreadId();
+}
+
+static inline void rpcJoinIfSingleThreaded(RpcMaybeThread&) {}
+#endif // BINDER_RPC_SINGLE_THREADED
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 751c4f9..5197ef9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -20,8 +20,12 @@
#include <functional>
#include <memory>
+#include <optional>
#include <string>
+#include <variant>
+#include <vector>
+#include <android-base/function_ref.h>
#include <android-base/unique_fd.h>
#include <utils/Errors.h>
@@ -39,8 +43,15 @@
public:
virtual ~RpcTransport() = default;
- // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
- [[nodiscard]] virtual status_t peek(void *buf, size_t size, size_t *out_size) = 0;
+ /**
+ * Poll the transport to check whether there is any data ready to read.
+ *
+ * Return:
+ * OK - There is data available on this transport
+ * WOULDBLOCK - No data is available
+ * error - any other error
+ */
+ [[nodiscard]] virtual status_t pollRead(void) = 0;
/**
* Read (or write), but allow to be interrupted by a trigger.
@@ -52,16 +63,23 @@
* to read/write data. If this returns an error, that error is returned from
* this function.
*
+ * ancillaryFds - FDs to be sent via UNIX domain dockets or Trusty IPC. When
+ * reading, if `ancillaryFds` is null, any received FDs will be silently
+ * dropped and closed (by the OS). Appended values will always be unique_fd,
+ * the variant type is used to avoid extra copies elsewhere.
+ *
* Return:
* OK - succeeded in completely processing 'size'
* error - interrupted (failure or trigger)
*/
[[nodiscard]] virtual status_t interruptableWriteFully(
FdTrigger *fdTrigger, iovec *iovs, int niovs,
- const std::function<status_t()> &altPoll) = 0;
+ const std::optional<android::base::function_ref<status_t()>> &altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
[[nodiscard]] virtual status_t interruptableReadFully(
FdTrigger *fdTrigger, iovec *iovs, int niovs,
- const std::function<status_t()> &altPoll) = 0;
+ const std::optional<android::base::function_ref<status_t()>> &altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
protected:
RpcTransport() = default;
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index bf2b25b..a3d42b7 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -38,10 +38,10 @@
<< " error: " << statusToString(status).c_str();
return false;
}
- server->setPerSessionRootObject([=](const sockaddr* addr, socklen_t addrlen) {
- LOG_ALWAYS_FATAL_IF(addr->sa_family != AF_VSOCK, "address is not a vsock");
+ server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+ LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
});
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 79c8c8f..32e018d 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -113,6 +113,7 @@
"abseil-*",
"android-*",
"bugprone-*",
+ "-bugprone-branch-clone", // b/155034972
"cert-*",
"clang-analyzer-*",
"-clang-analyzer-core.CallAndMessage",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 28e3ff4..b21a7e9 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -36,6 +36,7 @@
using ::android::Parcel;
using ::android::sp;
using ::android::status_t;
+using ::android::statusToString;
using ::android::String16;
using ::android::String8;
using ::android::wp;
@@ -133,7 +134,8 @@
} else {
// b/155793159
LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
- << "' to dead binder.";
+ << "' to dead binder with cached descriptor '" << SanitizeString(descriptor)
+ << "'.";
}
return false;
}
@@ -458,7 +460,8 @@
status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/);
if (status != ::android::OK) {
LOG(ERROR) << __func__
- << ": removed reference to death recipient but unlink failed.";
+ << ": removed reference to death recipient but unlink failed: "
+ << statusToString(status);
}
return PruneStatusT(status);
}
@@ -539,7 +542,8 @@
binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) {
if (binder == nullptr || recipient == nullptr) {
- LOG(ERROR) << __func__ << ": Must provide binder and recipient.";
+ LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient ("
+ << recipient << ")";
return STATUS_UNEXPECTED_NULL;
}
@@ -550,7 +554,8 @@
binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) {
if (binder == nullptr || recipient == nullptr) {
- LOG(ERROR) << __func__ << ": Must provide binder and recipient.";
+ LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient ("
+ << recipient << ")";
return STATUS_UNEXPECTED_NULL;
}
@@ -625,7 +630,8 @@
binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) {
if (binder == nullptr || in == nullptr) {
- LOG(ERROR) << __func__ << ": requires non-null parameters.";
+ LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder
+ << ") and in (" << in << ").";
return STATUS_UNEXPECTED_NULL;
}
const AIBinder_Class* clazz = binder->getClass();
@@ -671,7 +677,9 @@
AutoParcelDestroyer forIn(in, DestroyParcel);
if (!isUserCommand(code)) {
- LOG(ERROR) << __func__ << ": Only user-defined transactions can be made from the NDK.";
+ LOG(ERROR) << __func__
+ << ": Only user-defined transactions can be made from the NDK, but requested: "
+ << code;
return STATUS_UNKNOWN_TRANSACTION;
}
@@ -682,7 +690,8 @@
}
if (binder == nullptr || *in == nullptr || out == nullptr) {
- LOG(ERROR) << __func__ << ": requires non-null parameters.";
+ LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in ("
+ << in << "), and out (" << out << ").";
return STATUS_UNEXPECTED_NULL;
}
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 c8e78fc..78bcb43 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -43,10 +43,12 @@
namespace ndk {
/**
- * analog using std::shared_ptr for internally held refcount
+ * Binder analog to using std::shared_ptr for an internally held refcount.
*
* ref must be called at least one time during the lifetime of this object. The recommended way to
* construct this object is with SharedRefBase::make.
+ *
+ * If you need a "this" shared reference analogous to shared_from_this, use this->ref().
*/
class SharedRefBase {
public:
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
index 6880d86..8e288b3 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -56,6 +56,12 @@
* If the binder is null, null is returned. If this binder object was originally an IBinder object,
* the original java object will be returned.
*
+ * WARNING: this function returns global and local references. This can be
+ * figured out using GetObjectRefType. Though, when this function is called
+ * from within a Java context, the local ref will automatically be cleaned
+ * up. If this is called outside of a Java frame,
+ * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup.
+ *
* Available since API level 29.
*
* \param env Java environment. Must not be null.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 8457581..f68612c 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -59,6 +59,11 @@
/**
* Sets the position within the parcel.
*
+ * This must be called with a position that has been previously returned from
+ * AParcel_getDataPosition. If writes are made after setting the data position, they must
+ * be made in the exact same sequence used before resetting data position. Writing over
+ * objects such as binders or file descriptors is not supported.
+ *
* Available since API level 29.
*
* \param parcel The parcel of which to set the position.
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index d0cd11f..683a433 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -98,9 +98,9 @@
* This interface has system<->vendor stability
*/
// b/227835797 - can't use __INTRODUCED_IN(30) because old targets load this code
-#if __ANDROID_MIN_SDK_VERSION__ < 30
+#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30
__attribute__((weak))
-#endif // __ANDROID_MIN_SDK_VERSION__ < 30
+#endif // defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30
void AIBinder_markVintfStability(AIBinder* binder);
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 3824a1b..f3f2886 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -146,8 +146,8 @@
AIBinder_Class_disableInterfaceTokenHeader;
AIBinder_DeathRecipient_setOnUnlinked;
AIBinder_isHandlingTransaction;
- AIBinder_setInheritRt; # llndk
- AIBinder_setMinSchedulerPolicy; # llndk
+ AIBinder_setInheritRt; # apex llndk
+ AIBinder_setMinSchedulerPolicy; # apex llndk
AParcel_marshal;
AParcel_unmarshal;
};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index b7c7ae4..3edeebf 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -335,11 +335,16 @@
// We don't own this file, so we need to be careful not to drop it.
let file = ManuallyDrop::new(File::from_raw_fd(fd));
- if args.is_null() {
+ if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
- let args = slice::from_raw_parts(args, num_args as usize);
- let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect();
+
+ let args = if args.is_null() || num_args == 0 {
+ vec![]
+ } else {
+ slice::from_raw_parts(args, num_args as usize)
+ .iter().map(|s| CStr::from_ptr(*s)).collect()
+ };
let object = sys::AIBinder_getUserData(binder);
let binder: &T = &*(object as *const T);
diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp
index ec780f2..3f59dab 100644
--- a/libs/binder/rust/tests/serialization.cpp
+++ b/libs/binder/rust/tests/serialization.cpp
@@ -381,7 +381,7 @@
string expected = "TestingFileDescriptors";
vector<char> buf(expected.length());
base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size());
- ASSERT_EQ(expected, string(buf.data()));
+ ASSERT_EQ(expected, string(buf.data(), expected.length()));
}
TEST_F(SerializationTest, SerializeIBinder) {
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index b62da7b..f6bdf5c 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -117,8 +117,8 @@
) -> Result<(), StatusCode> {
match code {
bindings::Transaction_TEST_BOOL => {
- assert_eq!(parcel.read::<bool>()?, true);
- assert_eq!(parcel.read::<bool>()?, false);
+ assert!(parcel.read::<bool>()?);
+ assert!(!parcel.read::<bool>()?);
assert_eq!(parcel.read::<Vec<bool>>()?, unsafe {
bindings::TESTDATA_BOOL
});
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a3533d8..d7c6d49 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -141,6 +141,7 @@
unstable: true,
srcs: [
"BinderRpcTestClientInfo.aidl",
+ "BinderRpcTestServerConfig.aidl",
"BinderRpcTestServerInfo.aidl",
"IBinderRpcCallback.aidl",
"IBinderRpcSession.aidl",
@@ -167,7 +168,6 @@
"libbinder_tls_shared_deps",
],
shared_libs: [
- "libbinder",
"libbase",
"liblog",
],
@@ -185,25 +185,72 @@
],
}
-cc_test {
- name: "binderRpcTest",
+cc_defaults {
+ name: "binderRpcTest_common_defaults",
host_supported: true,
target: {
darwin: {
enabled: false,
},
+ },
+ defaults: [
+ "binder_test_defaults",
+ ],
+
+ static_libs: [
+ "libbinder_tls_static",
+ "libbinder_tls_test_utils",
+ "binderRpcTestIface-cpp",
+ "binderRpcTestIface-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "binderRpcTest_service_defaults",
+ defaults: [
+ "binderRpcTest_common_defaults",
+ ],
+ gtest: false,
+ auto_gen_config: false,
+ srcs: [
+ "binderRpcTestCommon.cpp",
+ "binderRpcTestService.cpp",
+ ],
+}
+
+cc_defaults {
+ name: "binderRpcTest_defaults",
+ target: {
android: {
test_suites: ["vts"],
},
},
defaults: [
- "binder_test_defaults",
- "libbinder_tls_shared_deps",
+ "binderRpcTest_common_defaults",
],
srcs: [
"binderRpcTest.cpp",
+ "binderRpcTestCommon.cpp",
],
+
+ test_suites: ["general-tests"],
+ require_root: true,
+
+ data_bins: [
+ "binder_rpc_test_service",
+ "binder_rpc_test_service_no_kernel",
+ "binder_rpc_test_service_single_threaded",
+ "binder_rpc_test_service_single_threaded_no_kernel",
+ ],
+}
+
+cc_defaults {
+ name: "binderRpcTest_shared_defaults",
+ cflags: [
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+
shared_libs: [
"libbinder",
"libbinder_ndk",
@@ -212,14 +259,128 @@
"libcutils",
"liblog",
],
- static_libs: [
- "libbinder_tls_static",
- "libbinder_tls_test_utils",
- "binderRpcTestIface-cpp",
- "binderRpcTestIface-ndk",
+}
+
+cc_defaults {
+ name: "binderRpcTest_static_defaults",
+
+ shared_libs: [
+ "libutils",
+ // libcrypto_static is not visible to this module
+ "libcrypto",
],
- test_suites: ["general-tests"],
- require_root: true,
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libssl",
+ ],
+
+ cflags: [
+ // Disable tests that require shared libraries,
+ // e.g., libbinder.so or libbinder_ndk.so
+ "-DBINDER_TEST_NO_SHARED_LIBS",
+ ],
+}
+
+cc_test {
+ // The module name cannot start with "binderRpcTest" because
+ // then atest tries to execute it as part of binderRpcTest
+ name: "binder_rpc_test_service",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_shared_defaults",
+ "libbinder_tls_shared_deps",
+ ],
+}
+
+cc_test {
+ name: "binder_rpc_test_service_no_kernel",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ static_libs: [
+ "libbinder_rpc_no_kernel",
+ ],
+}
+
+cc_test {
+ name: "binder_rpc_test_service_single_threaded",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded",
+ ],
+}
+
+cc_test {
+ name: "binder_rpc_test_service_single_threaded_no_kernel",
+ defaults: [
+ "binderRpcTest_service_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded_no_kernel",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTest",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_shared_defaults",
+ "libbinder_tls_shared_deps",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTestNoKernel",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ static_libs: [
+ "libbinder_rpc_no_kernel",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTestSingleThreaded",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ "-DBINDER_WITH_KERNEL_IPC",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded",
+ ],
+}
+
+cc_test {
+ name: "binderRpcTestSingleThreadedNoKernel",
+ defaults: [
+ "binderRpcTest_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+ cflags: [
+ "-DBINDER_RPC_SINGLE_THREADED",
+ ],
+ static_libs: [
+ "libbinder_rpc_single_threaded_no_kernel",
+ ],
}
cc_test {
@@ -319,7 +480,6 @@
"libbinder",
"libutils",
],
- clang: true,
cflags: [
"-g",
"-Wno-missing-field-initializers",
diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl
new file mode 100644
index 0000000..34d74be
--- /dev/null
+++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcelable BinderRpcTestServerConfig {
+ int numThreads;
+ int[] serverSupportedFileDescriptorTransportModes;
+ int socketType;
+ int rpcSecurity;
+ int serverVersion;
+ int vsockPort;
+ @utf8InCpp String addr;
+}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index fdd02a4..b15a225 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -24,6 +24,9 @@
// number of known RPC binders to process, RpcState::countBinders by session
int[] countBinders();
+ // Return a null binder with a non-nullable return type.
+ IBinder getNullBinder();
+
// Caller sends server, callee pings caller's server and returns error code.
int pingMe(IBinder binder);
@nullable IBinder repeatBinder(@nullable IBinder binder);
@@ -64,4 +67,8 @@
void scheduleShutdown();
void useKernelBinderCallingId();
+
+ ParcelFileDescriptor echoAsFile(@utf8InCpp String content);
+
+ ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files);
}
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index e1f5ed5..60b3c94 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -15,8 +15,11 @@
*/
#include <android-base/logging.h>
-#include <binder/Parcel.h>
+#include <binder/Binder.h>
#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <gtest/gtest.h>
#include <utils/CallStack.h>
@@ -124,12 +127,18 @@
});
}
-using android::IBinder;
-using android::Parcel;
-using android::String16;
+using android::BBinder;
using android::defaultServiceManager;
-using android::sp;
+using android::IBinder;
using android::IServiceManager;
+using android::OK;
+using android::Parcel;
+using android::RpcServer;
+using android::RpcSession;
+using android::sp;
+using android::status_t;
+using android::statusToString;
+using android::String16;
static sp<IBinder> GetRemoteBinder() {
// This gets binder representing the service manager
@@ -175,6 +184,36 @@
EXPECT_EQ(mallocs, 1);
}
+TEST(RpcBinderAllocation, SetupRpcServer) {
+ std::string tmp = getenv("TMPDIR") ?: "/tmp";
+ std::string addr = tmp + "/binderRpcBenchmark";
+ (void)unlink(addr.c_str());
+ auto server = RpcServer::make();
+ server->setRootObject(sp<BBinder>::make());
+
+ CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
+
+ std::thread([server]() { server->join(); }).detach();
+
+ status_t status;
+ auto session = RpcSession::make();
+ status = session->setupUnixDomainClient(addr.c_str());
+ CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
+
+ auto remoteBinder = session->getRootObject();
+
+ size_t mallocs = 0, totalBytes = 0;
+ {
+ const auto on_malloc = OnMalloc([&](size_t bytes) {
+ mallocs++;
+ totalBytes += bytes;
+ });
+ CHECK_EQ(OK, remoteBinder->pingBinder());
+ }
+ EXPECT_EQ(mallocs, 1);
+ EXPECT_EQ(totalBytes, 40);
+}
+
int main(int argc, char** argv) {
if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index b1e17b7..4ed3309 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -30,6 +30,7 @@
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <binder/Binder.h>
@@ -82,6 +83,7 @@
static constexpr int kSchedPolicy = SCHED_RR;
static constexpr int kSchedPriority = 7;
static constexpr int kSchedPriorityMore = 8;
+static constexpr int kKernelThreads = 15;
static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -115,6 +117,12 @@
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_OBJECTS,
BINDER_LIB_TEST_CAN_GET_SID,
+ BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
+ BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+ BINDER_LIB_TEST_LOCK_UNLOCK,
+ BINDER_LIB_TEST_PROCESS_LOCK,
+ BINDER_LIB_TEST_UNLOCK_AFTER_MS,
+ BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK
};
pid_t start_server_process(int arg2, bool usePoll = false)
@@ -247,13 +255,11 @@
{
int32_t id;
Parcel data, reply;
- sp<IBinder> binder;
EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR));
- EXPECT_FALSE(binder != nullptr);
- binder = reply.readStrongBinder();
- EXPECT_TRUE(binder != nullptr);
+ sp<IBinder> binder = reply.readStrongBinder();
+ EXPECT_NE(nullptr, binder);
EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
if (idPtr)
*idPtr = id;
@@ -442,6 +448,12 @@
EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork");
}
+TEST_F(BinderLibTest, AddManagerToManager) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = IInterface::asBinder(sm);
+ EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder));
+}
+
TEST_F(BinderLibTest, WasParceled) {
auto binder = sp<BBinder>::make();
EXPECT_FALSE(binder->wasParceled());
@@ -1221,6 +1233,53 @@
EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
}
+struct TooManyFdsFlattenable : Flattenable<TooManyFdsFlattenable> {
+ TooManyFdsFlattenable(size_t fdCount) : mFdCount(fdCount) {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const {
+ // Return a valid non-zero size here so we don't get an unintended
+ // BAD_VALUE from Parcel::write
+ return 16;
+ }
+ size_t getFdCount() const { return mFdCount; }
+ status_t flatten(void *& /*buffer*/, size_t & /*size*/, int *&fds, size_t &count) const {
+ for (size_t i = 0; i < count; i++) {
+ fds[i] = STDIN_FILENO;
+ }
+ return NO_ERROR;
+ }
+ status_t unflatten(void const *& /*buffer*/, size_t & /*size*/, int const *& /*fds*/,
+ size_t & /*count*/) {
+ /* This doesn't get called */
+ return NO_ERROR;
+ }
+
+ size_t mFdCount;
+};
+
+TEST_F(BinderLibTest, TooManyFdsFlattenable) {
+ rlimit origNofile;
+ int ret = getrlimit(RLIMIT_NOFILE, &origNofile);
+ ASSERT_EQ(0, ret);
+
+ // Restore the original file limits when the test finishes
+ base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); });
+
+ rlimit testNofile = {1024, 1024};
+ ret = setrlimit(RLIMIT_NOFILE, &testNofile);
+ ASSERT_EQ(0, ret);
+
+ Parcel parcel;
+ // Try to write more file descriptors than supported by the OS
+ TooManyFdsFlattenable tooManyFds1(1024);
+ EXPECT_THAT(parcel.write(tooManyFds1), StatusEq(-EMFILE));
+
+ // Try to write more file descriptors than the internal limit
+ TooManyFdsFlattenable tooManyFds2(1025);
+ EXPECT_THAT(parcel.write(tooManyFds2), StatusEq(BAD_VALUE));
+}
+
TEST(ServiceNotifications, Unregister) {
auto sm = defaultServiceManager();
using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
@@ -1234,6 +1293,80 @@
EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK);
}
+TEST_F(BinderLibTest, ThreadPoolAvailableThreads) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
+ StatusEq(NO_ERROR));
+ int32_t replyi = reply.readInt32();
+ // Expect 16 threads: kKernelThreads = 15 + Pool thread == 16
+ EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_LOCK, data, &reply), NO_ERROR);
+
+ /*
+ * This will use all threads in the pool expect the main pool thread.
+ * The service should run fine without locking, and the thread count should
+ * not exceed 16 (15 Max + pool thread).
+ */
+ std::vector<std::thread> ts;
+ for (size_t i = 0; i < kKernelThreads - 1; i++) {
+ ts.push_back(std::thread([&] {
+ Parcel local_reply;
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply),
+ NO_ERROR);
+ }));
+ }
+
+ data.writeInt32(1);
+ // Give a chance for all threads to be used
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
+
+ for (auto &t : ts) {
+ t.join();
+ }
+
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
+ StatusEq(NO_ERROR));
+ replyi = reply.readInt32();
+ // No more than 16 threads should exist.
+ EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1);
+}
+
+size_t epochMillis() {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::seconds;
+ using std::chrono::system_clock;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
+
+TEST_F(BinderLibTest, HangingServices) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+ int32_t delay = 1000; // ms
+ data.writeInt32(delay);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR);
+ std::vector<std::thread> ts;
+ size_t epochMsBefore = epochMillis();
+ for (size_t i = 0; i < kKernelThreads + 1; i++) {
+ ts.push_back(std::thread([&] {
+ Parcel local_reply;
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply),
+ NO_ERROR);
+ }));
+ }
+
+ for (auto &t : ts) {
+ t.join();
+ }
+ size_t epochMsAfter = epochMillis();
+
+ // deadlock occurred and threads only finished after 1s passed.
+ EXPECT_GE(epochMsAfter, epochMsBefore + delay);
+}
+
class BinderLibRpcTestBase : public BinderLibTest {
public:
void SetUp() override {
@@ -1640,11 +1773,42 @@
case BINDER_LIB_TEST_CAN_GET_SID: {
return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_MAX_THREAD_COUNT: {
+ reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_PROCESS_LOCK: {
+ m_blockMutex.lock();
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_LOCK_UNLOCK: {
+ std::lock_guard<std::mutex> _l(m_blockMutex);
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_UNLOCK_AFTER_MS: {
+ int32_t ms = data.readInt32();
+ return unlockInMs(ms);
+ }
+ case BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK: {
+ m_blockMutex.lock();
+ sp<BinderLibTestService> thisService = this;
+ int32_t value = data.readInt32();
+ // start local thread to unlock in 1s
+ std::thread t([=] { thisService->unlockInMs(value); });
+ t.detach();
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
}
+ status_t unlockInMs(int32_t ms) {
+ usleep(ms * 1000);
+ m_blockMutex.unlock();
+ return NO_ERROR;
+ }
+
private:
int32_t m_id;
int32_t m_nextServerId;
@@ -1655,6 +1819,7 @@
sp<IBinder> m_strongRef;
sp<IBinder> m_callback;
bool m_exitOnDestroy;
+ std::mutex m_blockMutex;
};
int run_server(int index, int readypipefd, bool usePoll)
@@ -1756,6 +1921,7 @@
}
}
} else {
+ ProcessState::self()->setThreadPoolMaxThreadCount(kKernelThreads);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
index 21cb70b..278dd2b 100644
--- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
+++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
@@ -23,6 +23,7 @@
#ifdef __BIONIC__
TEST(MemoryHeapBase, ForceMemfdRespected) {
auto mHeap = sp<MemoryHeapBase>::make(10, MemoryHeapBase::FORCE_MEMFD, "Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_FALSE(ashmem_valid(fd));
@@ -33,6 +34,7 @@
auto mHeap = sp<MemoryHeapBase>::make(8192,
MemoryHeapBase::FORCE_MEMFD,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL);
@@ -43,6 +45,7 @@
MemoryHeapBase::FORCE_MEMFD |
MemoryHeapBase::MEMFD_ALLOW_SEALING,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0);
@@ -53,6 +56,7 @@
MemoryHeapBase::FORCE_MEMFD |
MemoryHeapBase::READ_ONLY,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
@@ -64,6 +68,7 @@
MemoryHeapBase::READ_ONLY |
MemoryHeapBase::MEMFD_ALLOW_SEALING,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE);
@@ -74,6 +79,7 @@
auto mHeap = sp<MemoryHeapBase>::make(8192,
MemoryHeapBase::READ_ONLY,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
void* ptr = mHeap->getBase();
EXPECT_NE(ptr, MAP_FAILED);
@@ -87,6 +93,7 @@
MemoryHeapBase::READ_ONLY |
MemoryHeapBase::MEMFD_ALLOW_SEALING,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
void* ptr = mHeap->getBase();
EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index c2639e7..4e41d8e 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,29 +14,7 @@
* limitations under the License.
*/
-#include <BinderRpcTestClientInfo.h>
-#include <BinderRpcTestServerInfo.h>
-#include <BnBinderRpcCallback.h>
-#include <BnBinderRpcSession.h>
-#include <BnBinderRpcTest.h>
-#include <aidl/IBinderRpcTest.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android/binder_auto_utils.h>
-#include <android/binder_libbinder.h>
-#include <binder/Binder.h>
-#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <binder/RpcServer.h>
-#include <binder/RpcSession.h>
-#include <binder/RpcTlsTestUtils.h>
-#include <binder/RpcTlsUtils.h>
-#include <binder/RpcTransport.h>
-#include <binder/RpcTransportRaw.h>
-#include <binder/RpcTransportTls.h>
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -45,14 +23,12 @@
#include <thread>
#include <type_traits>
+#include <dlfcn.h>
#include <poll.h>
#include <sys/prctl.h>
-#include <unistd.h>
+#include <sys/socket.h>
-#include "../FdTrigger.h"
-#include "../RpcSocketAddress.h" // for testing preconnected clients
-#include "../RpcState.h" // for debugging
-#include "../vm_sockets.h" // for VMADDR_*
+#include "binderRpcTestCommon.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -62,35 +38,14 @@
namespace android {
+#ifdef BINDER_TEST_NO_SHARED_LIBS
+constexpr bool kEnableSharedLibs = false;
+#else
+constexpr bool kEnableSharedLibs = true;
+#endif
+
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, TLS };
-
-static inline std::vector<RpcSecurity> RpcSecurityValues() {
- return {RpcSecurity::RAW, RpcSecurity::TLS};
-}
-
-static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
- RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
- std::unique_ptr<RpcAuth> auth = nullptr) {
- switch (rpcSecurity) {
- case RpcSecurity::RAW:
- return RpcTransportCtxFactoryRaw::make();
- case RpcSecurity::TLS: {
- if (verifier == nullptr) {
- verifier = std::make_shared<RpcCertificateVerifierSimple>();
- }
- if (auth == nullptr) {
- auth = std::make_unique<RpcAuthSelfSigned>();
- }
- return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth));
- }
- default:
- LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
- }
-}
TEST(BinderRpcParcel, EntireParcelFormatted) {
Parcel p;
@@ -99,17 +54,19 @@
EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
}
-class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> {
+class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> {
public:
static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
- return newFactory(info.param)->toCString();
+ return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
+ std::to_string(std::get<1>(info.param));
}
};
-TEST_P(BinderRpcSimple, SetExternalServerTest) {
+TEST_P(BinderRpcServerOnly, SetExternalServerTest) {
base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
int sinkFd = sink.get();
- auto server = RpcServer::make(newFactory(GetParam()));
+ auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+ server->setProtocolVersion(std::get<1>(GetParam()));
ASSERT_FALSE(server->hasServer());
ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
ASSERT_TRUE(server->hasServer());
@@ -139,191 +96,15 @@
EXPECT_TRUE(stat.isOk()) << stat; \
} while (false)
-class MyBinderRpcSession : public BnBinderRpcSession {
-public:
- static std::atomic<int32_t> gNum;
-
- MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; }
- Status getName(std::string* name) override {
- *name = mName;
- return Status::ok();
+static std::string WaitStatusToString(int wstatus) {
+ if (WIFEXITED(wstatus)) {
+ return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
}
- ~MyBinderRpcSession() { gNum--; }
-
-private:
- std::string mName;
-};
-std::atomic<int32_t> MyBinderRpcSession::gNum;
-
-class MyBinderRpcCallback : public BnBinderRpcCallback {
- Status sendCallback(const std::string& value) {
- std::unique_lock _l(mMutex);
- mValues.push_back(value);
- _l.unlock();
- mCv.notify_one();
- return Status::ok();
+ if (WIFSIGNALED(wstatus)) {
+ return base::StringPrintf("term signal %d", WTERMSIG(wstatus));
}
- Status sendOnewayCallback(const std::string& value) { return sendCallback(value); }
-
-public:
- std::mutex mMutex;
- std::condition_variable mCv;
- std::vector<std::string> mValues;
-};
-
-class MyBinderRpcTest : public BnBinderRpcTest {
-public:
- wp<RpcServer> server;
- int port = 0;
-
- Status sendString(const std::string& str) override {
- (void)str;
- return Status::ok();
- }
- Status doubleString(const std::string& str, std::string* strstr) override {
- *strstr = str + str;
- return Status::ok();
- }
- Status getClientPort(int* out) override {
- *out = port;
- return Status::ok();
- }
- Status countBinders(std::vector<int32_t>* out) override {
- sp<RpcServer> spServer = server.promote();
- if (spServer == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
- out->clear();
- for (auto session : spServer->listSessions()) {
- size_t count = session->state()->countBinders();
- out->push_back(count);
- }
- return Status::ok();
- }
- Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
- if (binder == nullptr) {
- std::cout << "Received null binder!" << std::endl;
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
- *out = binder->pingBinder();
- return Status::ok();
- }
- Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
- *out = binder;
- return Status::ok();
- }
- static sp<IBinder> mHeldBinder;
- Status holdBinder(const sp<IBinder>& binder) override {
- mHeldBinder = binder;
- return Status::ok();
- }
- Status getHeldBinder(sp<IBinder>* held) override {
- *held = mHeldBinder;
- return Status::ok();
- }
- Status nestMe(const sp<IBinderRpcTest>& binder, int count) override {
- if (count <= 0) return Status::ok();
- return binder->nestMe(this, count - 1);
- }
- Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override {
- static sp<IBinder> binder = new BBinder;
- *out = binder;
- return Status::ok();
- }
- Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override {
- *out = new MyBinderRpcSession(name);
- return Status::ok();
- }
- Status getNumOpenSessions(int32_t* out) override {
- *out = MyBinderRpcSession::gNum;
- return Status::ok();
- }
-
- std::mutex blockMutex;
- Status lock() override {
- blockMutex.lock();
- return Status::ok();
- }
- Status unlockInMsAsync(int32_t ms) override {
- usleep(ms * 1000);
- blockMutex.unlock();
- return Status::ok();
- }
- Status lockUnlock() override {
- std::lock_guard<std::mutex> _l(blockMutex);
- return Status::ok();
- }
-
- Status sleepMs(int32_t ms) override {
- usleep(ms * 1000);
- return Status::ok();
- }
-
- Status sleepMsAsync(int32_t ms) override {
- // In-process binder calls are asynchronous, but the call to this method
- // is synchronous wrt its client. This in/out-process threading model
- // diffentiation is a classic binder leaky abstraction (for better or
- // worse) and is preserved here the way binder sockets plugs itself
- // into BpBinder, as nothing is changed at the higher levels
- // (IInterface) which result in this behavior.
- return sleepMs(ms);
- }
-
- Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
- const std::string& value) override {
- if (callback == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
-
- if (delayed) {
- std::thread([=]() {
- ALOGE("Executing delayed callback: '%s'", value.c_str());
- Status status = doCallback(callback, oneway, false, value);
- ALOGE("Delayed callback status: '%s'", status.toString8().c_str());
- }).detach();
- return Status::ok();
- }
-
- if (oneway) {
- return callback->sendOnewayCallback(value);
- }
-
- return callback->sendCallback(value);
- }
-
- Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
- const std::string& value) override {
- return doCallback(callback, oneway, delayed, value);
- }
-
- Status die(bool cleanup) override {
- if (cleanup) {
- exit(1);
- } else {
- _exit(1);
- }
- }
-
- Status scheduleShutdown() override {
- sp<RpcServer> strongServer = server.promote();
- if (strongServer == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
- }
- std::thread([=] {
- LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
- }).detach();
- return Status::ok();
- }
-
- Status useKernelBinderCallingId() override {
- // this is WRONG! It does not make sense when using RPC binder, and
- // because it is SO wrong, and so much code calls this, it should abort!
-
- (void)IPCThreadState::self()->getCallingPid();
- return Status::ok();
- }
-};
-sp<IBinder> MyBinderRpcTest::mHeldBinder;
+ return base::StringPrintf("unexpected state %d", wstatus);
+}
class Process {
public:
@@ -332,8 +113,8 @@
android::base::borrowed_fd /* readEnd */)>& f) {
android::base::unique_fd childWriteEnd;
android::base::unique_fd childReadEnd;
- CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno);
- CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno);
+ CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno);
+ CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno);
if (0 == (mPid = fork())) {
// racey: assume parent doesn't crash before this is set
prctl(PR_SET_PDEATHSIG, SIGHUP);
@@ -345,13 +126,28 @@
}
~Process() {
if (mPid != 0) {
- waitpid(mPid, nullptr, 0);
+ int wstatus;
+ waitpid(mPid, &wstatus, 0);
+ if (mCustomExitStatusCheck) {
+ mCustomExitStatusCheck(wstatus);
+ } else {
+ EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)
+ << "server process failed: " << WaitStatusToString(wstatus);
+ }
}
}
android::base::borrowed_fd readEnd() { return mReadEnd; }
android::base::borrowed_fd writeEnd() { return mWriteEnd; }
+ void setCustomExitStatusCheck(std::function<void(int wstatus)> f) {
+ mCustomExitStatusCheck = std::move(f);
+ }
+
+ // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
+ void terminate() { kill(mPid, SIGTERM); }
+
private:
+ std::function<void(int wstatus)> mCustomExitStatusCheck;
pid_t mPid = 0;
android::base::unique_fd mReadEnd;
android::base::unique_fd mWriteEnd;
@@ -366,7 +162,7 @@
};
static unsigned int allocateVsockPort() {
- static unsigned int vsockPort = 3456;
+ static unsigned int vsockPort = 34567;
return vsockPort++;
}
@@ -419,10 +215,10 @@
BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
~BinderRpcTestProcessSession() {
- EXPECT_NE(nullptr, rootIface);
- if (rootIface == nullptr) return;
-
if (!expectAlreadyShutdown) {
+ EXPECT_NE(nullptr, rootIface);
+ if (rootIface == nullptr) return;
+
std::vector<int32_t> remoteCounts;
// calling over any sessions counts across all sessions
EXPECT_OK(rootIface->countBinders(&remoteCounts));
@@ -443,28 +239,6 @@
}
};
-enum class SocketType {
- PRECONNECTED,
- UNIX,
- VSOCK,
- INET,
-};
-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:
- return "vm_socket";
- case SocketType::INET:
- return "inet_socket";
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- return "";
- }
-}
-
static base::unique_fd connectTo(const RpcSocketAddress& addr) {
base::unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
@@ -480,115 +254,79 @@
return serverFd;
}
-class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> {
+using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
+ android::base::borrowed_fd readEnd);
+
+class BinderRpc : public ::testing::TestWithParam<
+ std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
public:
- struct Options {
- size_t numThreads = 1;
- size_t numSessions = 1;
- size_t numIncomingConnections = 0;
- size_t numOutgoingConnections = SIZE_MAX;
- };
+ SocketType socketType() const { return std::get<0>(GetParam()); }
+ RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
+ uint32_t clientVersion() const { return std::get<2>(GetParam()); }
+ uint32_t serverVersion() const { return std::get<3>(GetParam()); }
+ bool singleThreaded() const { return std::get<4>(GetParam()); }
+ bool noKernel() const { return std::get<5>(GetParam()); }
+
+ // Whether the test params support sending FDs in parcels.
+ bool supportsFdTransport() const {
+ return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
+ (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX);
+ }
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security] = info.param;
- return PrintToString(type) + "_" + newFactory(security)->toCString();
- }
-
- static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
- uint64_t length = str.length();
- CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
- CHECK(android::base::WriteFully(fd, str.data(), str.length()));
- }
-
- static inline std::string readString(android::base::borrowed_fd fd) {
- uint64_t length;
- CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
- std::string ret(length, '\0');
- CHECK(android::base::ReadFully(fd, ret.data(), length));
+ auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+ auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
+ std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
+ if (singleThreaded) {
+ ret += "_single_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ }
return ret;
}
- static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
- Parcel parcel;
- CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
- writeString(fd,
- std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
- }
-
- template <typename T>
- static inline T readFromFd(android::base::borrowed_fd fd) {
- std::string data = readString(fd);
- Parcel parcel;
- CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
- T object;
- CHECK_EQ(OK, object.readFromParcel(&parcel));
- return object;
- }
-
// This creates a new process serving an interface on a certain number of
// threads.
- ProcessSession createRpcTestSocketServerProcess(
- const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) {
+ ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) {
CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
SocketType socketType = std::get<0>(GetParam());
RpcSecurity rpcSecurity = std::get<1>(GetParam());
+ uint32_t clientVersion = std::get<2>(GetParam());
+ uint32_t serverVersion = std::get<3>(GetParam());
+ bool singleThreaded = std::get<4>(GetParam());
+ bool noKernel = std::get<5>(GetParam());
- unsigned int vsockPort = allocateVsockPort();
- std::string addr = allocateSocketAddress();
+ std::string path = android::base::GetExecutableDirectory();
+ auto servicePath =
+ android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
+ singleThreaded ? "_single_threaded" : "",
+ noKernel ? "_no_kernel" : "");
auto ret = ProcessSession{
- .host = Process([&](android::base::borrowed_fd writeEnd,
+ .host = Process([=](android::base::borrowed_fd writeEnd,
android::base::borrowed_fd readEnd) {
- auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
- sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
-
- server->setMaxThreads(options.numThreads);
-
- unsigned int outPort = 0;
-
- switch (socketType) {
- case SocketType::PRECONNECTED:
- [[fallthrough]];
- case SocketType::UNIX:
- CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())) << addr;
- break;
- case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(vsockPort));
- break;
- case SocketType::INET: {
- CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
- CHECK_NE(0, outPort);
- break;
- }
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- }
-
- BinderRpcTestServerInfo serverInfo;
- serverInfo.port = static_cast<int64_t>(outPort);
- serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
- writeToFd(writeEnd, serverInfo);
- auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
-
- if (rpcSecurity == RpcSecurity::TLS) {
- for (const auto& clientCert : clientInfo.certs) {
- CHECK_EQ(OK,
- certVerifier
- ->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- clientCert.data));
- }
- }
-
- configure(server);
-
- server->join();
-
- // Another thread calls shutdown. Wait for it to complete.
- (void)server->shutdown();
+ auto writeFd = std::to_string(writeEnd.get());
+ auto readFd = std::to_string(readEnd.get());
+ execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
+ NULL);
}),
};
+ BinderRpcTestServerConfig serverConfig;
+ serverConfig.numThreads = options.numThreads;
+ serverConfig.socketType = static_cast<int32_t>(socketType);
+ serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity);
+ serverConfig.serverVersion = serverVersion;
+ serverConfig.vsockPort = allocateVsockPort();
+ serverConfig.addr = allocateSocketAddress();
+ for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
+ serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
+ static_cast<int32_t>(mode));
+ }
+ writeToFd(ret.host.writeEnd(), serverConfig);
+
std::vector<sp<RpcSession>> sessions;
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
for (size_t i = 0; i < options.numSessions; i++) {
@@ -618,20 +356,22 @@
status_t status;
for (const auto& session : sessions) {
+ CHECK(session->setProtocolVersion(clientVersion));
session->setMaxIncomingThreads(options.numIncomingConnections);
session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
switch (socketType) {
case SocketType::PRECONNECTED:
status = session->setupPreconnectedClient({}, [=]() {
- return connectTo(UnixSocketAddress(addr.c_str()));
+ return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
});
break;
case SocketType::UNIX:
- status = session->setupUnixDomainClient(addr.c_str());
+ status = session->setupUnixDomainClient(serverConfig.addr.c_str());
break;
case SocketType::VSOCK:
- status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
+ status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
break;
case SocketType::INET:
status = session->setupInetClient("127.0.0.1", serverInfo.port);
@@ -639,52 +379,22 @@
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
+ if (options.allowConnectFailure && status != OK) {
+ ret.sessions.clear();
+ break;
+ }
CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
ret.sessions.push_back({session, session->getRootObject()});
}
return ret;
}
- BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) {
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcess(
- options,
- [&](const sp<RpcServer>& server) {
- server->setPerSessionRootObject([&](const sockaddr* addr,
- socklen_t len) {
- sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make();
- switch (addr->sa_family) {
- case AF_UNIX:
- // nothing to save
- break;
- case AF_VSOCK:
- CHECK_EQ(len, sizeof(sockaddr_vm));
- service->port = reinterpret_cast<const sockaddr_vm*>(addr)
- ->svm_port;
- break;
- case AF_INET:
- CHECK_EQ(len, sizeof(sockaddr_in));
- service->port =
- ntohs(reinterpret_cast<const sockaddr_in*>(addr)
- ->sin_port);
- break;
- case AF_INET6:
- CHECK_EQ(len, sizeof(sockaddr_in));
- service->port =
- ntohs(reinterpret_cast<const sockaddr_in6*>(addr)
- ->sin6_port);
- break;
- default:
- LOG_ALWAYS_FATAL("Unrecognized address family %d",
- addr->sa_family);
- }
- service->server = server;
- return service;
- });
- }),
+ .proc = createRpcTestSocketServerProcessEtc(options),
};
- ret.rootBinder = ret.proc.sessions.at(0).root;
+ ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
@@ -694,6 +404,18 @@
size_t sleepMs = 500);
};
+// Test fixture for tests that start multiple threads.
+// This includes tests with one thread but multiple sessions,
+// since a server uses one thread per session.
+class BinderRpcThreads : public BinderRpc {
+public:
+ void SetUp() override {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+ }
+};
+
TEST_P(BinderRpc, Ping) {
auto proc = createRpcTestSocketServerProcess({});
ASSERT_NE(proc.rootBinder, nullptr);
@@ -706,7 +428,7 @@
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
-TEST_P(BinderRpc, MultipleSessions) {
+TEST_P(BinderRpcThreads, MultipleSessions) {
auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
for (auto session : proc.proc.sessions) {
ASSERT_NE(nullptr, session.root);
@@ -714,7 +436,7 @@
}
}
-TEST_P(BinderRpc, SeparateRootObject) {
+TEST_P(BinderRpcThreads, SeparateRootObject) {
SocketType type = std::get<0>(GetParam());
if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
// we can't get port numbers for unix sockets
@@ -752,7 +474,7 @@
p1.markForBinder(proc1.rootBinder);
p1.writeInt32(3);
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize()));
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
Parcel p2;
@@ -791,6 +513,13 @@
EXPECT_EQ(single + single, doubled);
}
+TEST_P(BinderRpc, InvalidNullBinderReturn) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
+}
+
TEST_P(BinderRpc, CallMeBack) {
auto proc = createRpcTestSocketServerProcess({});
@@ -890,7 +619,7 @@
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
-TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+TEST_P(BinderRpcThreads, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
sp<IBinder> outBinder;
@@ -900,6 +629,11 @@
}
TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
@@ -909,6 +643,11 @@
}
TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
// for historical reasons, IServiceManager interface only returns the
@@ -928,7 +667,13 @@
}
TEST_P(BinderRpc, NestedTransactions) {
- auto proc = createRpcTestSocketServerProcess({});
+ auto proc = createRpcTestSocketServerProcess({
+ // Enable FD support because it uses more stack space and so represents
+ // something closer to a worst case scenario.
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
auto nastyNester = sp<MyBinderRpcTest>::make();
EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
@@ -1030,7 +775,7 @@
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
-TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+TEST_P(BinderRpcThreads, ThreadPoolGreaterThanEqualRequested) {
constexpr size_t kNumThreads = 10;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
@@ -1081,14 +826,14 @@
EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
}
-TEST_P(BinderRpc, ThreadPoolOverSaturated) {
+TEST_P(BinderRpcThreads, ThreadPoolOverSaturated) {
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = kNumThreads + 3;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
}
-TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
+TEST_P(BinderRpcThreads, ThreadPoolLimitOutgoing) {
constexpr size_t kNumThreads = 20;
constexpr size_t kNumOutgoingConnections = 10;
constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
@@ -1097,7 +842,7 @@
testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
}
-TEST_P(BinderRpc, ThreadingStressTest) {
+TEST_P(BinderRpcThreads, ThreadingStressTest) {
constexpr size_t kNumClientThreads = 10;
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 100;
@@ -1126,7 +871,7 @@
for (auto& t : threads) t.join();
}
-TEST_P(BinderRpc, OnewayStressTest) {
+TEST_P(BinderRpcThreads, OnewayStressTest) {
constexpr size_t kNumClientThreads = 10;
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 1000;
@@ -1161,7 +906,7 @@
EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
}
-TEST_P(BinderRpc, OnewayCallQueueing) {
+TEST_P(BinderRpcThreads, OnewayCallQueueing) {
constexpr size_t kNumSleeps = 10;
constexpr size_t kNumExtraServerThreads = 4;
constexpr size_t kSleepMs = 50;
@@ -1190,7 +935,7 @@
saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
}
-TEST_P(BinderRpc, OnewayCallExhaustion) {
+TEST_P(BinderRpcThreads, OnewayCallExhaustion) {
constexpr size_t kNumClients = 2;
constexpr size_t kTooLongMs = 1000;
@@ -1233,11 +978,21 @@
TEST_P(BinderRpc, Callbacks) {
const static std::string kTestString = "good afternoon!";
+ bool bothSingleThreaded = !kEnableRpcThreads || singleThreaded();
+
for (bool callIsOneway : {true, false}) {
for (bool callbackIsOneway : {true, false}) {
for (bool delayed : {true, false}) {
+ if (bothSingleThreaded && (callIsOneway || callbackIsOneway || delayed)) {
+ // we have no incoming connections to receive the callback
+ continue;
+ }
+
+ size_t numIncomingConnections = bothSingleThreaded ? 0 : 1;
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+ {.numThreads = 1,
+ .numSessions = 1,
+ .numIncomingConnections = numIncomingConnections});
auto cb = sp<MyBinderRpcCallback>::make();
if (callIsOneway) {
@@ -1248,9 +1003,14 @@
proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
}
- using std::literals::chrono_literals::operator""s;
- std::unique_lock<std::mutex> _l(cb->mMutex);
- cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ // if both transactions are synchronous and the response is sent back on the
+ // same thread, everything should have happened in a nested call. Otherwise,
+ // the callback will be processed on another thread.
+ if (callIsOneway || callbackIsOneway || delayed) {
+ using std::literals::chrono_literals::operator""s;
+ RpcMutexUniqueLock _l(cb->mMutex);
+ cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ }
EXPECT_EQ(cb->mValues.size(), 1)
<< "callIsOneway: " << callIsOneway
@@ -1270,6 +1030,12 @@
// need to manually shut it down
EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ // Flaky. Sometimes gets SIGABRT.
+ EXPECT_TRUE((WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) ||
+ (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT))
+ << "server process failed: " << WaitStatusToString(wstatus);
+ });
proc.expectAlreadyShutdown = true;
}
}
@@ -1299,33 +1065,185 @@
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
proc.expectAlreadyShutdown = true;
}
}
TEST_P(BinderRpc, UseKernelBinderCallingId) {
- bool okToFork = ProcessState::selfOrNull() == nullptr;
+ // This test only works if the current process shared the internal state of
+ // ProcessState with the service across the call to fork(). Both the static
+ // libraries and libbinder.so have their own separate copies of all the
+ // globals, so the test only works when the test client and service both use
+ // libbinder.so (when using static libraries, even a client and service
+ // using the same kind of static library should have separate copies of the
+ // variables).
+ if (!kEnableSharedLibs || singleThreaded() || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
auto proc = createRpcTestSocketServerProcess({});
- // If this process has used ProcessState already, then the forked process
- // cannot use it at all. If this process hasn't used it (depending on the
- // order tests are run), then the forked process can use it, and we'll only
- // catch the invalid usage the second time. Such is the burden of global
- // state!
- if (okToFork) {
- // we can't allocate IPCThreadState so actually the first time should
- // succeed :(
- EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
- }
+ // we can't allocate IPCThreadState so actually the first time should
+ // succeed :(
+ EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
// second time! we catch the error :)
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
proc.expectAlreadyShutdown = true;
}
+TEST_P(BinderRpc, FileDescriptorTransportRejectNone) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ .allowConnectFailure = true,
+ });
+ EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
+ proc.proc.host.terminate();
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
+ proc.expectAlreadyShutdown = true;
+}
+
+TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::NONE},
+ .allowConnectFailure = true,
+ });
+ EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
+ proc.proc.host.terminate();
+ proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
+ << "server process failed incorrectly: " << WaitStatusToString(wstatus);
+ });
+ proc.expectAlreadyShutdown = true;
+}
+
+TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::NONE,
+ RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->echoAsFile("hello", &out);
+ EXPECT_EQ(status.transactionError(), FDS_NOT_ALLOWED) << status;
+}
+
+TEST_P(BinderRpc, ReceiveFile) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->echoAsFile("hello", &out);
+ if (!supportsFdTransport()) {
+ EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
+ return;
+ }
+ ASSERT_TRUE(status.isOk()) << status;
+
+ std::string result;
+ CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_EQ(result, "hello");
+}
+
+TEST_P(BinderRpc, SendFiles) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ std::vector<android::os::ParcelFileDescriptor> files;
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("123")));
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a")));
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("b")));
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("cd")));
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->concatFiles(files, &out);
+ if (!supportsFdTransport()) {
+ EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
+ return;
+ }
+ ASSERT_TRUE(status.isOk()) << status;
+
+ std::string result;
+ CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_EQ(result, "123abcd");
+}
+
+TEST_P(BinderRpc, SendMaxFiles) {
+ if (!supportsFdTransport()) {
+ GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ std::vector<android::os::ParcelFileDescriptor> files;
+ for (int i = 0; i < 253; i++) {
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a")));
+ }
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->concatFiles(files, &out);
+ ASSERT_TRUE(status.isOk()) << status;
+
+ std::string result;
+ CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_EQ(result, std::string(253, 'a'));
+}
+
+TEST_P(BinderRpc, SendTooManyFiles) {
+ if (!supportsFdTransport()) {
+ GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ std::vector<android::os::ParcelFileDescriptor> files;
+ for (int i = 0; i < 254; i++) {
+ files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a")));
+ }
+
+ android::os::ParcelFileDescriptor out;
+ auto status = proc.rootIface->concatFiles(files, &out);
+ EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
+}
+
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
+ if constexpr (!kEnableSharedLibs) {
+ GTEST_SKIP() << "Test disabled because Binder was built as a static library";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
@@ -1335,6 +1253,10 @@
}
TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
+ if constexpr (!kEnableSharedLibs) {
+ GTEST_SKIP() << "Test disabled because Binder was built as a static library";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
@@ -1359,7 +1281,7 @@
return ret;
}
-TEST_P(BinderRpc, Fds) {
+TEST_P(BinderRpcThreads, Fds) {
ssize_t beforeFds = countFds();
ASSERT_GE(beforeFds, 0);
{
@@ -1382,20 +1304,90 @@
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(RpcTransportCtxFactoryRaw::make());
- 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(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;
+ android::base::unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
+
+ sockaddr_vm serverAddr{
+ .svm_family = AF_VSOCK,
+ .svm_port = vsockPort,
+ .svm_cid = VMADDR_CID_ANY,
+ };
+ int ret = TEMP_FAILURE_RETRY(
+ bind(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr)));
+ LOG_ALWAYS_FATAL_IF(0 != ret, "Could not bind socket to port %u: %s", vsockPort,
+ strerror(errno));
+
+ ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/));
+ LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", vsockPort,
+ strerror(errno));
+
+ // Try to connect to the server using the VMADDR_CID_LOCAL cid
+ // to see if the kernel supports it. It's safe to use a blocking
+ // connect because vsock sockets have a 2 second connection timeout,
+ // and they return ETIMEDOUT after that.
+ android::base::unique_fd connectFd(
+ TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort,
+ strerror(errno));
+
+ bool success = false;
+ sockaddr_vm connectAddr{
+ .svm_family = AF_VSOCK,
+ .svm_port = vsockPort,
+ .svm_cid = VMADDR_CID_LOCAL,
+ };
+ ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr),
+ sizeof(connectAddr)));
+ if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) {
+ android::base::unique_fd acceptFd;
+ while (true) {
+ pollfd pfd[]{
+ {.fd = serverFd.get(), .events = POLLIN, .revents = 0},
+ {.fd = connectFd.get(), .events = POLLOUT, .revents = 0},
+ };
+ ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno));
+
+ if (pfd[0].revents & POLLIN) {
+ sockaddr_vm acceptAddr;
+ socklen_t acceptAddrLen = sizeof(acceptAddr);
+ ret = TEMP_FAILURE_RETRY(accept4(serverFd.get(),
+ reinterpret_cast<sockaddr*>(&acceptAddr),
+ &acceptAddrLen, SOCK_CLOEXEC));
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Could not accept4 socket: %s", strerror(errno));
+ LOG_ALWAYS_FATAL_IF(acceptAddrLen != static_cast<socklen_t>(sizeof(acceptAddr)),
+ "Truncated address");
+
+ // Store the fd in acceptFd so we keep the connection alive
+ // while polling connectFd
+ acceptFd.reset(ret);
+ }
+
+ if (pfd[1].revents & POLLOUT) {
+ // Connect either succeeded or timed out
+ int connectErrno;
+ socklen_t connectErrnoLen = sizeof(connectErrno);
+ int ret = getsockopt(connectFd.get(), SOL_SOCKET, SO_ERROR, &connectErrno,
+ &connectErrnoLen);
+ LOG_ALWAYS_FATAL_IF(ret == -1,
+ "Could not getsockopt() after connect() "
+ "on non-blocking socket: %s.",
+ strerror(errno));
+
+ // We're done, this is all we wanted
+ success = connectErrno == 0;
+ break;
+ }
+ }
+ } else {
+ success = ret == 0;
+ }
+
+ ALOGE("Detected vsock loopback supported: %s", success ? "yes" : "no");
+
+ return success;
}
static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
@@ -1412,9 +1404,31 @@
return ret;
}
+static std::vector<uint32_t> testVersions() {
+ std::vector<uint32_t> versions;
+ for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) {
+ versions.push_back(i);
+ }
+ versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+ return versions;
+}
+
INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
::testing::Combine(::testing::ValuesIn(testSocketTypes()),
- ::testing::ValuesIn(RpcSecurityValues())),
+ ::testing::ValuesIn(RpcSecurityValues()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(false, true),
+ ::testing::Values(false, true)),
+ BinderRpc::PrintParamInfo);
+
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpcThreads,
+ ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
+ ::testing::ValuesIn(RpcSecurityValues()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(false),
+ ::testing::Values(false, true)),
BinderRpc::PrintParamInfo);
class BinderRpcServerRootObject
@@ -1468,9 +1482,14 @@
bool mValue = false;
};
-TEST_P(BinderRpcSimple, Shutdown) {
+TEST_P(BinderRpcServerOnly, Shutdown) {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+
auto addr = allocateSocketAddress();
- auto server = RpcServer::make(newFactory(GetParam()));
+ auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+ server->setProtocolVersion(std::get<1>(GetParam()));
ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
auto joinEnds = std::make_shared<OneOffSignal>();
@@ -1499,6 +1518,11 @@
"createRpcDelegateServiceManager() with a device attached, such test belongs "
"to binderHostDeviceTest. Hence, just disable this test on host.";
#endif // !__ANDROID__
+ if constexpr (!kEnableKernelIpc) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
sp<IServiceManager> sm = defaultServiceManager();
ASSERT_NE(nullptr, sm);
// Any Java service with non-empty getInterfaceDescriptor() would do.
@@ -1540,12 +1564,17 @@
ASSERT_EQ(OK, rpcBinder->pingBinder());
}
-INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()),
- BinderRpcSimple::PrintTestParam);
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly,
+ ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()),
+ ::testing::ValuesIn(testVersions())),
+ BinderRpcServerOnly::PrintTestParam);
class RpcTransportTestUtils {
public:
- using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>;
+ // Only parameterized only server version because `RpcSession` is bypassed
+ // in the client half of the tests.
+ using Param =
+ std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>;
using ConnectToServer = std::function<base::unique_fd()>;
// A server that handles client socket connections.
@@ -1557,8 +1586,9 @@
[[nodiscard]] AssertionResult setUp(
const Param& param,
std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
- auto [socketType, rpcSecurity, certificateFormat] = param;
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
+ rpcServer->setProtocolVersion(serverVersion);
switch (socketType) {
case SocketType::PRECONNECTED: {
return AssertionFailure() << "Not supported by this test";
@@ -1676,7 +1706,8 @@
FdTrigger* fdTrigger) {
std::string message(kMessage);
iovec messageIov{message.data(), message.size()};
- auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
+ std::nullopt, nullptr);
if (status != OK) return AssertionFailure() << statusToString(status);
return AssertionSuccess();
}
@@ -1687,7 +1718,8 @@
explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {}
Client(Client&&) = default;
[[nodiscard]] AssertionResult setUp(const Param& param) {
- auto [socketType, rpcSecurity, certificateFormat] = param;
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
+ (void)serverVersion;
mFdTrigger = FdTrigger::make();
mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx();
if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
@@ -1708,8 +1740,9 @@
LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed");
std::string readMessage(expectedMessage.size(), '\0');
iovec readMessageIov{readMessage.data(), readMessage.size()};
- status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(),
- &readMessageIov, 1, {});
+ status_t readStatus =
+ mClientTransport->interruptableReadFully(mFdTrigger.get(), &readMessageIov, 1,
+ std::nullopt, nullptr);
if (readStatus != OK) {
return AssertionFailure() << statusToString(readStatus);
}
@@ -1757,23 +1790,28 @@
using Server = RpcTransportTestUtils::Server;
using Client = RpcTransportTestUtils::Client;
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [socketType, rpcSecurity, certificateFormat] = info.param;
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param;
auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat);
+ ret += "_serverV" + std::to_string(serverVersion);
return ret;
}
static std::vector<ParamType> getRpcTranportTestParams() {
std::vector<ParamType> ret;
- for (auto socketType : testSocketTypes(false /* hasPreconnected */)) {
- for (auto rpcSecurity : RpcSecurityValues()) {
- switch (rpcSecurity) {
- case RpcSecurity::RAW: {
- ret.emplace_back(socketType, rpcSecurity, std::nullopt);
- } break;
- case RpcSecurity::TLS: {
- ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM);
- ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER);
- } break;
+ for (auto serverVersion : testVersions()) {
+ for (auto socketType : testSocketTypes(false /* hasPreconnected */)) {
+ for (auto rpcSecurity : RpcSecurityValues()) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW: {
+ ret.emplace_back(socketType, rpcSecurity, std::nullopt, serverVersion);
+ } break;
+ case RpcSecurity::TLS: {
+ ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM,
+ serverVersion);
+ ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER,
+ serverVersion);
+ } break;
+ }
}
}
}
@@ -1781,9 +1819,15 @@
}
template <typename A, typename B>
status_t trust(const A& a, const B& b) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b);
}
+ void SetUp() override {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+ }
};
TEST_P(RpcTransportTest, GoodCertificate) {
@@ -1817,7 +1861,8 @@
}
TEST_P(RpcTransportTest, UntrustedServer) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
auto untrustedServer = std::make_unique<Server>();
ASSERT_TRUE(untrustedServer->setUp(GetParam()));
@@ -1835,7 +1880,9 @@
client.run(handshakeOk);
}
TEST_P(RpcTransportTest, MaliciousServer) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
+
auto validServer = std::make_unique<Server>();
ASSERT_TRUE(validServer->setUp(GetParam()));
@@ -1858,7 +1905,9 @@
}
TEST_P(RpcTransportTest, UntrustedClient) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
+
auto server = std::make_unique<Server>();
ASSERT_TRUE(server->setUp(GetParam()));
@@ -1877,7 +1926,9 @@
}
TEST_P(RpcTransportTest, MaliciousClient) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
+
auto server = std::make_unique<Server>();
ASSERT_TRUE(server->setUp(GetParam()));
@@ -1904,7 +1955,8 @@
auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
std::string message(RpcTransportTestUtils::kMessage);
iovec messageIov{message.data(), message.size()};
- auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
+ std::nullopt, nullptr);
if (status != OK) return AssertionFailure() << statusToString(status);
{
@@ -1915,7 +1967,8 @@
}
iovec msg2Iov{msg2.data(), msg2.size()};
- status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, {});
+ status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt,
+ nullptr);
if (status != DEAD_OBJECT)
return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
"should return DEAD_OBJECT, but it is "
@@ -1962,23 +2015,28 @@
RpcTransportTest::PrintParamInfo);
class RpcTransportTlsKeyTest
- : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> {
+ : public testing::TestWithParam<
+ std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> {
public:
template <typename A, typename B>
status_t trust(const A& a, const B& b) {
- auto [socketType, certificateFormat, keyFormat] = GetParam();
+ auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam();
+ (void)serverVersion;
return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b);
}
static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [socketType, certificateFormat, keyFormat] = info.param;
- auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) +
- "_key_" + PrintToString(keyFormat);
- return ret;
+ auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param;
+ return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) +
+ "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion);
};
};
TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) {
- auto [socketType, certificateFormat, keyFormat] = GetParam();
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+
+ auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam();
std::vector<uint8_t> pkeyData, certData;
{
@@ -1993,8 +2051,8 @@
auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat);
auto desCert = deserializeCertificate(certData, certificateFormat);
auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert));
- auto utilsParam =
- std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat));
+ auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS,
+ std::make_optional(certificateFormat), serverVersion);
auto server = std::make_unique<RpcTransportTestUtils::Server>();
ASSERT_TRUE(server->setUp(utilsParam, std::move(auth)));
@@ -2013,7 +2071,8 @@
BinderRpc, RpcTransportTlsKeyTest,
testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)),
testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER),
- testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)),
+ testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER),
+ testing::ValuesIn(testVersions())),
RpcTransportTlsKeyTest::PrintParamInfo);
} // namespace android
diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp
new file mode 100644
index 0000000..0d9aa95
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestCommon.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "binderRpcTestCommon.h"
+
+namespace android {
+
+std::atomic<int32_t> MyBinderRpcSession::gNum;
+sp<IBinder> MyBinderRpcTest::mHeldBinder;
+
+} // namespace android
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
new file mode 100644
index 0000000..4513d36
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <BinderRpcTestClientInfo.h>
+#include <BinderRpcTestServerConfig.h>
+#include <BinderRpcTestServerInfo.h>
+#include <BnBinderRpcCallback.h>
+#include <BnBinderRpcSession.h>
+#include <BnBinderRpcTest.h>
+#include <aidl/IBinderRpcTest.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
+#include <binder/RpcTlsTestUtils.h>
+#include <binder/RpcTlsUtils.h>
+#include <binder/RpcTransport.h>
+#include <binder/RpcTransportRaw.h>
+#include <binder/RpcTransportTls.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <signal.h>
+
+#include "../BuildFlags.h"
+#include "../FdTrigger.h"
+#include "../RpcSocketAddress.h" // for testing preconnected clients
+#include "../RpcState.h" // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
+#include "utils/Errors.h"
+
+namespace android {
+
+constexpr char kLocalInetAddress[] = "127.0.0.1";
+
+enum class RpcSecurity { RAW, TLS };
+
+static inline std::vector<RpcSecurity> RpcSecurityValues() {
+ return {RpcSecurity::RAW, RpcSecurity::TLS};
+}
+
+enum class SocketType {
+ PRECONNECTED,
+ UNIX,
+ VSOCK,
+ INET,
+};
+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:
+ return "vm_socket";
+ case SocketType::INET:
+ return "inet_socket";
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ return "";
+ }
+}
+
+struct BinderRpcOptions {
+ size_t numThreads = 1;
+ size_t numSessions = 1;
+ size_t numIncomingConnections = 0;
+ size_t numOutgoingConnections = SIZE_MAX;
+ RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode =
+ RpcSession::FileDescriptorTransportMode::NONE;
+ std::vector<RpcSession::FileDescriptorTransportMode>
+ serverSupportedFileDescriptorTransportModes = {
+ RpcSession::FileDescriptorTransportMode::NONE};
+
+ // If true, connection failures will result in `ProcessSession::sessions` being empty
+ // instead of a fatal error.
+ bool allowConnectFailure = false;
+};
+
+static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
+ uint64_t length = str.length();
+ CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
+ CHECK(android::base::WriteFully(fd, str.data(), str.length()));
+}
+
+static inline std::string readString(android::base::borrowed_fd fd) {
+ uint64_t length;
+ CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+ std::string ret(length, '\0');
+ CHECK(android::base::ReadFully(fd, ret.data(), length));
+ return ret;
+}
+
+static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
+ Parcel parcel;
+ CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
+ writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
+}
+
+template <typename T>
+static inline T readFromFd(android::base::borrowed_fd fd) {
+ std::string data = readString(fd);
+ Parcel parcel;
+ CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+ T object;
+ CHECK_EQ(OK, object.readFromParcel(&parcel));
+ return object;
+}
+
+static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
+ RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
+ std::unique_ptr<RpcAuth> auth = nullptr) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW:
+ return RpcTransportCtxFactoryRaw::make();
+ case RpcSecurity::TLS: {
+ if (verifier == nullptr) {
+ verifier = std::make_shared<RpcCertificateVerifierSimple>();
+ }
+ if (auth == nullptr) {
+ auth = std::make_unique<RpcAuthSelfSigned>();
+ }
+ return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth));
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
+ }
+}
+
+// Create an FD that returns `contents` when read.
+static inline base::unique_fd mockFileDescriptor(std::string contents) {
+ android::base::unique_fd readFd, writeFd;
+ CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno);
+ RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() {
+ signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write
+ if (!WriteStringToFd(contents, writeFd)) {
+ int savedErrno = errno;
+ LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s",
+ strerror(savedErrno));
+ }
+ }).detach();
+ return readFd;
+}
+
+using android::binder::Status;
+
+class MyBinderRpcSession : public BnBinderRpcSession {
+public:
+ static std::atomic<int32_t> gNum;
+
+ MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; }
+ Status getName(std::string* name) override {
+ *name = mName;
+ return Status::ok();
+ }
+ ~MyBinderRpcSession() { gNum--; }
+
+private:
+ std::string mName;
+};
+
+class MyBinderRpcCallback : public BnBinderRpcCallback {
+ Status sendCallback(const std::string& value) {
+ RpcMutexUniqueLock _l(mMutex);
+ mValues.push_back(value);
+ _l.unlock();
+ mCv.notify_one();
+ return Status::ok();
+ }
+ Status sendOnewayCallback(const std::string& value) { return sendCallback(value); }
+
+public:
+ RpcMutex mMutex;
+ RpcConditionVariable mCv;
+ std::vector<std::string> mValues;
+};
+
+class MyBinderRpcTest : public BnBinderRpcTest {
+public:
+ wp<RpcServer> server;
+ int port = 0;
+
+ Status sendString(const std::string& str) override {
+ (void)str;
+ return Status::ok();
+ }
+ Status doubleString(const std::string& str, std::string* strstr) override {
+ *strstr = str + str;
+ return Status::ok();
+ }
+ Status getClientPort(int* out) override {
+ *out = port;
+ return Status::ok();
+ }
+ Status countBinders(std::vector<int32_t>* out) override {
+ sp<RpcServer> spServer = server.promote();
+ if (spServer == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ out->clear();
+ for (auto session : spServer->listSessions()) {
+ size_t count = session->state()->countBinders();
+ out->push_back(count);
+ }
+ return Status::ok();
+ }
+ Status getNullBinder(sp<IBinder>* out) override {
+ out->clear();
+ return Status::ok();
+ }
+ Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
+ if (binder == nullptr) {
+ std::cout << "Received null binder!" << std::endl;
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ *out = binder->pingBinder();
+ return Status::ok();
+ }
+ Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
+ *out = binder;
+ return Status::ok();
+ }
+ static sp<IBinder> mHeldBinder;
+ Status holdBinder(const sp<IBinder>& binder) override {
+ mHeldBinder = binder;
+ return Status::ok();
+ }
+ Status getHeldBinder(sp<IBinder>* held) override {
+ *held = mHeldBinder;
+ return Status::ok();
+ }
+ Status nestMe(const sp<IBinderRpcTest>& binder, int count) override {
+ if (count <= 0) return Status::ok();
+ return binder->nestMe(this, count - 1);
+ }
+ Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override {
+ static sp<IBinder> binder = new BBinder;
+ *out = binder;
+ return Status::ok();
+ }
+ Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override {
+ *out = new MyBinderRpcSession(name);
+ return Status::ok();
+ }
+ Status getNumOpenSessions(int32_t* out) override {
+ *out = MyBinderRpcSession::gNum;
+ return Status::ok();
+ }
+
+ RpcMutex blockMutex;
+ Status lock() override {
+ blockMutex.lock();
+ return Status::ok();
+ }
+ Status unlockInMsAsync(int32_t ms) override {
+ usleep(ms * 1000);
+ blockMutex.unlock();
+ return Status::ok();
+ }
+ Status lockUnlock() override {
+ RpcMutexLockGuard _l(blockMutex);
+ return Status::ok();
+ }
+
+ Status sleepMs(int32_t ms) override {
+ usleep(ms * 1000);
+ return Status::ok();
+ }
+
+ Status sleepMsAsync(int32_t ms) override {
+ // In-process binder calls are asynchronous, but the call to this method
+ // is synchronous wrt its client. This in/out-process threading model
+ // diffentiation is a classic binder leaky abstraction (for better or
+ // worse) and is preserved here the way binder sockets plugs itself
+ // into BpBinder, as nothing is changed at the higher levels
+ // (IInterface) which result in this behavior.
+ return sleepMs(ms);
+ }
+
+ Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
+ const std::string& value) override {
+ if (callback == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+
+ if (delayed) {
+ RpcMaybeThread([=]() {
+ ALOGE("Executing delayed callback: '%s'", value.c_str());
+ Status status = doCallback(callback, oneway, false, value);
+ ALOGE("Delayed callback status: '%s'", status.toString8().c_str());
+ }).detach();
+ return Status::ok();
+ }
+
+ if (oneway) {
+ return callback->sendOnewayCallback(value);
+ }
+
+ return callback->sendCallback(value);
+ }
+
+ Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed,
+ const std::string& value) override {
+ return doCallback(callback, oneway, delayed, value);
+ }
+
+ Status die(bool cleanup) override {
+ if (cleanup) {
+ exit(1);
+ } else {
+ _exit(1);
+ }
+ }
+
+ Status scheduleShutdown() override {
+ sp<RpcServer> strongServer = server.promote();
+ if (strongServer == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ RpcMaybeThread([=] {
+ LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
+ }).detach();
+ return Status::ok();
+ }
+
+ Status useKernelBinderCallingId() override {
+ // this is WRONG! It does not make sense when using RPC binder, and
+ // because it is SO wrong, and so much code calls this, it should abort!
+
+ if constexpr (kEnableKernelIpc) {
+ (void)IPCThreadState::self()->getCallingPid();
+ }
+ return Status::ok();
+ }
+
+ Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override {
+ out->reset(mockFileDescriptor(content));
+ return Status::ok();
+ }
+
+ Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files,
+ android::os::ParcelFileDescriptor* out) override {
+ std::string acc;
+ for (const auto& file : files) {
+ std::string result;
+ CHECK(android::base::ReadFdToString(file.get(), &result));
+ acc.append(result);
+ }
+ out->reset(mockFileDescriptor(acc));
+ return Status::ok();
+ }
+};
+
+} // namespace android
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
new file mode 100644
index 0000000..31eb5da
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "binderRpcTestCommon.h"
+
+using namespace android;
+
+int main(int argc, const char* argv[]) {
+ LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
+ base::unique_fd writeEnd(atoi(argv[1]));
+ base::unique_fd readEnd(atoi(argv[2]));
+
+ auto serverConfig = readFromFd<BinderRpcTestServerConfig>(readEnd);
+ auto socketType = static_cast<SocketType>(serverConfig.socketType);
+ auto rpcSecurity = static_cast<RpcSecurity>(serverConfig.rpcSecurity);
+
+ std::vector<RpcSession::FileDescriptorTransportMode>
+ serverSupportedFileDescriptorTransportModes;
+ for (auto mode : serverConfig.serverSupportedFileDescriptorTransportModes) {
+ serverSupportedFileDescriptorTransportModes.push_back(
+ static_cast<RpcSession::FileDescriptorTransportMode>(mode));
+ }
+
+ auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+ sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
+
+ server->setProtocolVersion(serverConfig.serverVersion);
+ server->setMaxThreads(serverConfig.numThreads);
+ server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
+
+ unsigned int outPort = 0;
+
+ switch (socketType) {
+ case SocketType::PRECONNECTED:
+ [[fallthrough]];
+ case SocketType::UNIX:
+ CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
+ << serverConfig.addr;
+ break;
+ case SocketType::VSOCK:
+ CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
+ break;
+ case SocketType::INET: {
+ CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
+ CHECK_NE(0, outPort);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+
+ BinderRpcTestServerInfo serverInfo;
+ serverInfo.port = static_cast<int64_t>(outPort);
+ serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
+ writeToFd(writeEnd, serverInfo);
+ auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
+
+ if (rpcSecurity == RpcSecurity::TLS) {
+ for (const auto& clientCert : clientInfo.certs) {
+ CHECK_EQ(OK,
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+ clientCert.data));
+ }
+ }
+
+ server->setPerSessionRootObject([&](const void* addrPtr, size_t len) {
+ // UNIX sockets with abstract addresses return
+ // sizeof(sa_family_t)==2 in addrlen
+ CHECK_GE(len, sizeof(sa_family_t));
+ const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr);
+ sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make();
+ switch (addr->sa_family) {
+ case AF_UNIX:
+ // nothing to save
+ break;
+ case AF_VSOCK:
+ CHECK_EQ(len, sizeof(sockaddr_vm));
+ service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port;
+ break;
+ case AF_INET:
+ CHECK_EQ(len, sizeof(sockaddr_in));
+ service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
+ break;
+ case AF_INET6:
+ CHECK_EQ(len, sizeof(sockaddr_in));
+ service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unrecognized address family %d", addr->sa_family);
+ }
+ service->server = server;
+ return service;
+ });
+
+ server->join();
+
+ // Another thread calls shutdown. Wait for it to complete.
+ (void)server->shutdown();
+
+ return 0;
+}
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 4fcf42d..3dab2c7 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -233,11 +233,15 @@
"0100000025000000|03000000|00000000|ffffffff|03000000|00000000|00000000|"
"07000000020000003a0044000000000000000000|f8ffffff020000003a002f00000000000000000008000000";
+TEST(RpcWire, V0) {
+ checkRepr(kCurrentRepr, 0);
+}
+
TEST(RpcWire, CurrentVersion) {
checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION);
}
-static_assert(RPC_WIRE_PROTOCOL_VERSION == 0,
+static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL,
"If the binder wire protocol is updated, this test should test additional versions. "
"The binder wire protocol should only be updated on upstream AOSP.");
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index e5d32da..2ca6ebd 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -59,6 +59,7 @@
cc_library_static {
name: "libbinder_random_parcel",
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 47ec776..7059d30 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -73,20 +73,20 @@
uint8_t data[1337];
};
-#define PARCEL_READ_WITH_STATUS(T, FUN) \
- [] (const ::android::Parcel& p, uint8_t /*data*/) {\
- FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
- T t{};\
- status_t status = p.FUN(&t);\
- FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [](const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
+ T t{}; \
+ status_t status = p.FUN(&t); \
+ FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/; \
}
-#define PARCEL_READ_NO_STATUS(T, FUN) \
- [] (const ::android::Parcel& p, uint8_t /*data*/) {\
- FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
- T t = p.FUN();\
- (void) t;\
- FUZZ_LOG() << #T " done " /* << " value: " << t*/;\
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [](const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \
+ T t = p.FUN(); \
+ (void)t; \
+ FUZZ_LOG() << #T " done " /* << " value: " << t*/; \
}
#define PARCEL_READ_OPT_STATUS(T, FUN) \
@@ -102,7 +102,9 @@
PARCEL_READ_NO_STATUS(size_t, dataPosition),
PARCEL_READ_NO_STATUS(size_t, dataCapacity),
PARCEL_READ_NO_STATUS(::android::binder::Status, enforceNoDataAvail),
- [] (const ::android::Parcel& p, uint8_t pos) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ // aborts on larger values
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX);
FUZZ_LOG() << "about to setDataPosition: " << pos;
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
@@ -111,13 +113,13 @@
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors),
- [] (const ::android::Parcel& p, uint8_t len) {
- std::string interface(len, 'a');
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ std::string interface = provider.ConsumeRandomLengthString();
FUZZ_LOG() << "about to enforceInterface: " << interface;
bool b = p.enforceInterface(::android::String16(interface.c_str()));
FUZZ_LOG() << "enforced interface: " << b;
},
- [] (const ::android::Parcel& p, uint8_t /*len*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to checkInterface";
android::sp<android::IBinder> aBinder = new android::BBinder();
bool b = p.checkInterface(aBinder.get());
@@ -125,13 +127,16 @@
},
PARCEL_READ_NO_STATUS(size_t, objectsCount),
PARCEL_READ_NO_STATUS(status_t, errorCheck),
- [] (const ::android::Parcel& p, uint8_t len) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ // Read at least a bit. Unbounded allocation would OOM.
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
FUZZ_LOG() << "about to read void*";
std::vector<uint8_t> data(len);
status_t status = p.read(data.data(), len);
FUZZ_LOG() << "read status: " << status;
},
- [] (const ::android::Parcel& p, uint8_t len) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ size_t len = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readInplace";
const void* r = p.readInplace(len);
FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << (r ? HexString(r, len) : "null");
@@ -149,13 +154,13 @@
PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16),
- [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read c-str";
const char* str = p.readCString();
FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>");
},
PARCEL_READ_OPT_STATUS(android::String8, readString8),
- [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readString8Inplace";
size_t outLen = 0;
const char* str = p.readString8Inplace(&outLen);
@@ -165,7 +170,7 @@
PARCEL_READ_OPT_STATUS(android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
- [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
const char16_t* str = p.readString16Inplace(&outLen);
@@ -263,13 +268,13 @@
PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray),
#undef COMMA
- [] (const android::Parcel& p, uint8_t /*len*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read flattenable";
ExampleFlattenable f;
status_t status = p.read(f);
FUZZ_LOG() << "read flattenable: " << status;
},
- [] (const android::Parcel& p, uint8_t /*len*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read lite flattenable";
ExampleLightFlattenable f;
status_t status = p.read(f);
@@ -284,7 +289,7 @@
PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<BigStruct>>, resizeOutVector),
PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
- [] (const android::Parcel& p, uint8_t /*len*/) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readNativeHandle";
native_handle_t* t = p.readNativeHandle();
FUZZ_LOG() << "readNativeHandle: " << t;
@@ -303,15 +308,16 @@
PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
- [] (const android::Parcel& p, uint8_t len) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ size_t len = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readBlob";
::android::Parcel::ReadableBlob blob;
status_t status = p.readBlob(len, &blob);
FUZZ_LOG() << "readBlob status: " << status;
},
- [] (const android::Parcel& p, uint8_t options) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
FUZZ_LOG() << "about to readObject";
- bool nullMetaData = options & 0x1;
+ bool nullMetaData = provider.ConsumeBool();
const void* obj = static_cast<const void*>(p.readObject(nullMetaData));
FUZZ_LOG() << "readObject: " << obj;
},
@@ -319,20 +325,19 @@
PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
// additional parcelable objects defined in libbinder
- [] (const ::android::Parcel& p, uint8_t data) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
using ::android::os::ParcelableHolder;
using ::android::Parcelable;
FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status";
- Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL;
- if ( (data & 1) == 1 ) {
- stability = Parcelable::Stability::STABILITY_VINTF;
- }
+ Parcelable::Stability stability = provider.ConsumeBool()
+ ? Parcelable::Stability::STABILITY_LOCAL
+ : Parcelable::Stability::STABILITY_VINTF;
ParcelableHolder t = ParcelableHolder(stability);
status_t status = p.readParcelable(&t);
FUZZ_LOG() << "ParcelableHolder status: " << status;
},
PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
- [] (const ::android::Parcel& p, uint8_t /* data */) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call hasFileDescriptorsInRange() with status";
size_t offset = p.readUint32();
size_t length = p.readUint32();
@@ -340,7 +345,7 @@
status_t status = p.hasFileDescriptorsInRange(offset, length, &result);
FUZZ_LOG() << " status: " << status << " result: " << result;
},
- [] (const ::android::Parcel& p, uint8_t /* data */) {
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call compareDataInRange() with status";
size_t thisOffset = p.readUint32();
size_t otherOffset = p.readUint32();
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 5aeb5cc..26d6770 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -70,7 +70,7 @@
}
#define PARCEL_READ(T, FUN) \
- [](const NdkParcelAdapter& p, uint8_t /*data*/) { \
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { \
FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
T t{}; \
binder_status_t status = FUN(p.aParcel(), &t); \
@@ -80,32 +80,37 @@
// clang-format off
std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{
// methods from binder_parcel.h
- [](const NdkParcelAdapter& p, uint8_t pos) {
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) {
+ // aborts on larger values
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX);
FUZZ_LOG() << "about to set data position to " << pos;
binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos);
FUZZ_LOG() << "set data position: " << status;
},
- [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read status header";
ndk::ScopedAStatus t;
binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
FUZZ_LOG() << "read status header: " << status;
},
- [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to getDataSize the parcel";
AParcel_getDataSize(p.aParcel());
FUZZ_LOG() << "getDataSize done";
},
- [](const NdkParcelAdapter& p, uint8_t data) {
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) {
FUZZ_LOG() << "about to read a ParcelableHolder";
- ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF};
+ ndk::AParcelableHolder ph {provider.ConsumeBool() ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF};
binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph);
FUZZ_LOG() << "read the ParcelableHolder: " << status;
},
- [](const NdkParcelAdapter& p, uint8_t data) {
- FUZZ_LOG() << "about to appendFrom";
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) {
+ size_t offset = provider.ConsumeIntegral<size_t>();
+ size_t pos = provider.ConsumeIntegral<size_t>();
+ FUZZ_LOG() << "about to appendFrom " << pos;
+ // TODO: create random parcel
AParcel* parcel = AParcel_create();
- binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data);
+ binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, offset, pos);
AParcel_delete(parcel);
FUZZ_LOG() << "appendFrom: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
index cf24ab9..81e79b5 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -43,6 +43,10 @@
return aParcel()->get()->setData(buffer, len);
}
+ android::status_t appendFrom(const NdkParcelAdapter* parcel, int32_t start, int32_t len) {
+ return AParcel_appendFrom(parcel->aParcel(), aParcel(), start, len);
+ }
+
private:
ndk::ScopedAParcel mParcel;
};
diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
index ee9840f..438e8ae 100644
--- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
@@ -35,19 +35,19 @@
#define PARCEL_READ_OPT_STATUS(T, FUN) \
PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN)
-#define PARCEL_READ_NO_STATUS(T, FUN) \
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
- FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
- T t = p.FUN();\
- FUZZ_LOG() << #T " value: " << t;\
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \
+ T t = p.FUN(); \
+ FUZZ_LOG() << #T " value: " << t; \
}
-#define PARCEL_READ_WITH_STATUS(T, FUN) \
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
- FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
- T t;\
- status_t status = p.FUN(&t);\
- FUZZ_LOG() << #T " status: " << status << " value: " << t;\
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
+ T t; \
+ status_t status = p.FUN(&t); \
+ FUZZ_LOG() << #T " status: " << status << " value: " << t; \
}
// clang-format off
@@ -56,27 +56,30 @@
PARCEL_READ_NO_STATUS(size_t, dataAvail),
PARCEL_READ_NO_STATUS(size_t, dataPosition),
PARCEL_READ_NO_STATUS(size_t, dataCapacity),
- [] (const ::android::hardware::Parcel& p, uint8_t pos) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ // aborts on larger values
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX);
FUZZ_LOG() << "about to setDataPosition: " << pos;
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
- [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
FUZZ_LOG() << "about to enforceInterface";
- std::string interfaceName(length, 'a');
- bool okay = p.enforceInterface(interfaceName.c_str());
+ bool okay = p.enforceInterface(provider.ConsumeRandomLengthString().c_str());
FUZZ_LOG() << "enforceInterface status: " << okay;
},
PARCEL_READ_NO_STATUS(size_t, objectsCount),
- [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ // Read at least a bit. Unbounded allocation would OOM.
+ size_t length = provider.ConsumeIntegralInRange<size_t>(0, 1024);
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());
},
- [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t length = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to read";
- std::vector<uint8_t> data (length);
const void* inplace = p.readInplace(length);
FUZZ_LOG() << "read status: " << (inplace ? HexString(inplace, length) : "null");
},
@@ -91,14 +94,14 @@
PARCEL_READ_OPT_STATUS(float, readFloat),
PARCEL_READ_OPT_STATUS(double, readDouble),
PARCEL_READ_OPT_STATUS(bool, readBool),
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readCString";
const char* str = p.readCString();
FUZZ_LOG() << "readCString " << (str ? str : "<null>");
},
PARCEL_READ_OPT_STATUS(::android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16),
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readString16Inplace";
size_t outSize = 0;
const char16_t* str = p.readString16Inplace(&outSize);
@@ -106,7 +109,8 @@
},
PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readBuffer";
size_t handle = 0;
const void* data = nullptr;
@@ -116,7 +120,8 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readNullableBuffer";
size_t handle = 0;
const void* data = nullptr;
@@ -126,7 +131,8 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readEmbeddedBuffer";
size_t handle = 0;
size_t parent_buffer_handle = 0;
@@ -138,7 +144,8 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readNullableEmbeddedBuffer";
size_t handle = 0;
size_t parent_buffer_handle = 0;
@@ -150,7 +157,7 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readNativeHandleNoDup";
const native_handle_t* handle = nullptr;
status_t status = p.readNativeHandleNoDup(&handle);
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index f435dae..180a177 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -83,22 +83,36 @@
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];
- uint8_t readIdx = a % reads.size();
+ FuzzedDataProvider instructionsProvider(instructions.data(), instructions.size());
+ while (instructionsProvider.remaining_bytes() > 0) {
+ uint8_t idx = instructionsProvider.ConsumeIntegralInRange<uint8_t>(0, reads.size() - 1);
- uint8_t b = instructions[i + 1];
+ FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
+ << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
- FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
- << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
- << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
- << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
- << " cap: " << p.dataCapacity();
-
- reads[readIdx](p, b);
+ reads[idx](p, instructionsProvider);
}
}
+// Append two random parcels.
+template <typename P>
+void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) {
+ int32_t start = provider.ConsumeIntegral<int32_t>();
+ int32_t len = provider.ConsumeIntegral<int32_t>();
+
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+
+ P p0, p1;
+ fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()));
+ fillRandomParcel(&p1, std::move(provider));
+
+ FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "start: " << start << " len: " << len;
+
+ p0.appendFrom(&p1, start, len);
+}
+
void* NothingClass_onCreate(void* args) {
return args;
}
@@ -148,6 +162,12 @@
doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
std::move(provider));
},
+ [](FuzzedDataProvider&& provider) {
+ doAppendFuzz<::android::Parcel>("binder", std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider));
+ },
};
provider.PickValueInArray(fuzzBackend)(std::move(provider));
diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
index b68a8a9..765a93e 100644
--- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
+++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
@@ -15,5 +15,9 @@
*/
#pragma once
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <functional>
+
template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>;
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 7e9bb7d..d169043 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -300,11 +300,11 @@
}
std::vector<tis_val_t> vals(gNCpus);
- time_key_t key = {.uid = uid};
for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
- key.bucket = i;
+ const time_key_t key = {.uid = uid, .bucket = i};
if (findMapEntry(gTisMapFd, &key, vals.data())) {
- if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {};
+ time_key_t tmpKey;
+ if (errno != ENOENT || getFirstMapKey(gTisMapFd, &tmpKey)) return {};
continue;
}
@@ -412,10 +412,11 @@
concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)};
for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0);
std::vector<concurrent_val_t> vals(gNCpus);
- time_key_t key = {.uid = uid};
- for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
+ for (uint32_t i = 0; i <= (gNCpus - 1) / CPUS_PER_ENTRY; ++i) {
+ const time_key_t key = {.uid = uid, .bucket = i};
if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
- if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {};
+ time_key_t tmpKey;
+ if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &tmpKey)) return {};
continue;
}
auto offset = key.bucket * CPUS_PER_ENTRY;
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 3551a8f..a6585c5 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -85,8 +85,20 @@
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
+ "android.hardware.automotive.audiocontrol.IAudioControl",
+ "android.hardware.automotive.evs.IEvsEnumerator",
+ "android.hardware.biometrics.face.IBiometricsFace",
+ "android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
"android.hardware.camera.provider.ICameraProvider",
+ "android.hardware.drm.IDrmFactory",
+ "android.hardware.graphics.allocator.IAllocator",
+ "android.hardware.graphics.composer3.IComposer",
+ "android.hardware.health.IHealth",
"android.hardware.input.processor.IInputProcessor",
+ "android.hardware.neuralnetworks.IDevice",
+ "android.hardware.power.IPower",
+ "android.hardware.power.stats.IPowerStats",
+ "android.hardware.sensors.ISensors",
};
/* list of extra hal interfaces to dump containing process during native dumps */
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index 8c28464..347c4e0 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,4 +1,10 @@
+abdolrashidi@google.com
+cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
+ianelliott@google.com
+lfy@google.com
lpy@google.com
-timvp@google.com
+romanl@google.com
+vantablack@google.com
+yuxinhu@google.com
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d634c58..5a2b92b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -316,7 +316,6 @@
cc_defaults {
name: "libgui_bufferqueue-defaults",
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -337,7 +336,7 @@
},
whole_static_libs: [
- "LibGuiProperties",
+ "libLibGuiProperties",
],
shared_libs: [
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index e58543a..fc68ad2 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -15,7 +15,6 @@
name: "libgui_test",
test_suites: ["device-tests"],
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -75,7 +74,6 @@
name: "libgui_multilib_test",
test_suites: ["device-tests"],
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -102,7 +100,6 @@
name: "SurfaceParcelable_test",
test_suites: ["device-tests"],
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -131,7 +128,6 @@
cc_test {
name: "SamplingDemo",
- clang: true,
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5d7874a..b2fec79 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -58,8 +58,6 @@
"VirtualKeyMap.cpp",
],
- clang: true,
-
header_libs: ["jni_headers"],
export_header_lib_headers: ["jni_headers"],
@@ -67,6 +65,7 @@
"libbase",
"liblog",
"libcutils",
+ "libvintf",
],
static_libs: [
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 0bee1b6..a908969 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -53,33 +53,39 @@
}
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
- const InputDeviceIdentifier& deviceIdentifier,
- InputDeviceConfigurationFileType type) {
+ const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type,
+ const char* suffix) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
- std::string versionPath = getInputDeviceConfigurationFilePathByName(
- StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
- deviceIdentifier.vendor, deviceIdentifier.product,
- deviceIdentifier.version),
- type);
+ std::string versionPath =
+ getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%"
+ "04x_Version_%04x%s",
+ deviceIdentifier.vendor,
+ deviceIdentifier.product,
+ deviceIdentifier.version,
+ suffix),
+ type);
if (!versionPath.empty()) {
return versionPath;
}
}
// Try vendor product.
- std::string productPath = getInputDeviceConfigurationFilePathByName(
- StringPrintf("Vendor_%04x_Product_%04x",
- deviceIdentifier.vendor, deviceIdentifier.product),
- type);
+ std::string productPath =
+ getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%04x%s",
+ deviceIdentifier.vendor,
+ deviceIdentifier.product,
+ suffix),
+ type);
if (!productPath.empty()) {
return productPath;
}
}
// Try device name.
- return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
+ return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName() + suffix,
+ type);
}
std::string getInputDeviceConfigurationFilePathByName(
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 7c25cda..59cc7d1 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -21,24 +21,33 @@
#include <input/InputEventLabels.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
+#include <log/log.h>
#include <utils/Errors.h>
-#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
+#include <vintf/RuntimeInfo.h>
+#include <vintf/VintfObject.h>
#include <cstdlib>
#include <string_view>
#include <unordered_map>
-// Enables debug output for the parser.
-#define DEBUG_PARSER 0
+/**
+ * Log debug output for the parser.
+ * Enable this via "adb shell setprop log.tag.KeyLayoutMapParser DEBUG" (requires restart)
+ */
+const bool DEBUG_PARSER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Parser", ANDROID_LOG_INFO);
// Enables debug output for parser performance.
#define DEBUG_PARSER_PERFORMANCE 0
-// Enables debug output for mapping.
-#define DEBUG_MAPPING 0
-
+/**
+ * Log debug output for mapping.
+ * Enable this via "adb shell setprop log.tag.KeyLayoutMapMapping DEBUG" (requires restart)
+ */
+const bool DEBUG_MAPPING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Mapping", ANDROID_LOG_INFO);
namespace android {
namespace {
@@ -69,6 +78,29 @@
sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(),
sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
+bool kernelConfigsArePresent(const std::set<std::string>& configs) {
+ std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo =
+ android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
+ vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+ LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched");
+
+ const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs();
+ for (const std::string& requiredConfig : configs) {
+ const auto configIt = kernelConfigs.find(requiredConfig);
+ if (configIt == kernelConfigs.end()) {
+ ALOGI("Required kernel config %s is not found", requiredConfig.c_str());
+ return false;
+ }
+ const std::string& option = configIt->second;
+ if (option != "y" && option != "m") {
+ ALOGI("Required kernel config %s has option %s", requiredConfig.c_str(),
+ option.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
KeyLayoutMap::KeyLayoutMap() = default;
@@ -76,32 +108,34 @@
base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
const char* contents) {
- Tokenizer* tokenizer;
- status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
- if (status) {
- ALOGE("Error %d opening key layout map.", status);
- return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
- }
- std::unique_ptr<Tokenizer> t(tokenizer);
- auto ret = load(t.get());
- if (ret.ok()) {
- (*ret)->mLoadFileName = filename;
- }
- return ret;
+ return load(filename, contents);
}
-base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) {
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename,
+ const char* contents) {
Tokenizer* tokenizer;
- status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
+ status_t status;
+ if (contents == nullptr) {
+ status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
+ } else {
+ status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
+ }
if (status) {
ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
}
std::unique_ptr<Tokenizer> t(tokenizer);
auto ret = load(t.get());
- if (ret.ok()) {
- (*ret)->mLoadFileName = filename;
+ if (!ret.ok()) {
+ return ret;
}
+ const std::shared_ptr<KeyLayoutMap>& map = *ret;
+ LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error");
+ if (!kernelConfigsArePresent(map->mRequiredKernelConfigs)) {
+ ALOGI("Not loading %s because the required kernel configs are not set", filename.c_str());
+ return Errorf("Missing kernel config");
+ }
+ map->mLoadFileName = filename;
return ret;
}
@@ -134,9 +168,8 @@
int32_t* outKeyCode, uint32_t* outFlags) const {
const Key* key = getKey(scanCode, usageCode);
if (!key) {
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode,
+ usageCode);
*outKeyCode = AKEYCODE_UNKNOWN;
*outFlags = 0;
return NAME_NOT_FOUND;
@@ -145,10 +178,9 @@
*outKeyCode = key->keyCode;
*outFlags = key->flags;
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
- scanCode, usageCode, *outKeyCode, *outFlags);
-#endif
+ ALOGD_IF(DEBUG_MAPPING,
+ "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
+ scanCode, usageCode, *outKeyCode, *outFlags);
return NO_ERROR;
}
@@ -156,103 +188,79 @@
base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) {
auto it = mSensorsByAbsCode.find(absCode);
if (it == mSensorsByAbsCode.end()) {
-#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode);
return Errorf("Can't find abs code {}.", absCode);
}
const Sensor& sensor = it->second;
-
-#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
- ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
+ ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
}
const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- return &mKeysByUsageCode.valueAt(index);
+ auto it = mKeysByUsageCode.find(usageCode);
+ if (it != mKeysByUsageCode.end()) {
+ return &it->second;
}
}
if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- return &mKeysByScanCode.valueAt(index);
+ auto it = mKeysByScanCode.find(scanCode);
+ if (it != mKeysByScanCode.end()) {
+ return &it->second;
}
}
return nullptr;
}
-status_t KeyLayoutMap::findScanCodesForKey(
- int32_t keyCode, std::vector<int32_t>* outScanCodes) const {
- const size_t N = mKeysByScanCode.size();
- for (size_t i=0; i<N; i++) {
- if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
- outScanCodes->push_back(mKeysByScanCode.keyAt(i));
+std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const {
+ std::vector<int32_t> scanCodes;
+ for (const auto& [scanCode, key] : mKeysByScanCode) {
+ if (keyCode == key.keyCode) {
+ scanCodes.push_back(scanCode);
}
}
- return NO_ERROR;
+ return scanCodes;
}
-status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
- ssize_t index = mAxes.indexOfKey(scanCode);
- if (index < 0) {
-#if DEBUG_MAPPING
- ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
-#endif
- return NAME_NOT_FOUND;
+std::optional<AxisInfo> KeyLayoutMap::mapAxis(int32_t scanCode) const {
+ auto it = mAxes.find(scanCode);
+ if (it == mAxes.end()) {
+ ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode);
+ return std::nullopt;
}
- *outAxisInfo = mAxes.valueAt(index);
-
-#if DEBUG_MAPPING
- ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
- "splitValue=%d, flatOverride=%d.",
- scanCode,
- outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
- outAxisInfo->splitValue, outAxisInfo->flatOverride);
-#endif
- return NO_ERROR;
+ const AxisInfo& axisInfo = it->second;
+ ALOGD_IF(DEBUG_MAPPING,
+ "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
+ "splitValue=%d, flatOverride=%d.",
+ scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
+ axisInfo.flatOverride);
+ return axisInfo;
}
-status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const {
- const size_t N = mLedsByScanCode.size();
- for (size_t i = 0; i < N; i++) {
- if (mLedsByScanCode.valueAt(i).ledCode == ledCode) {
- *outScanCode = mLedsByScanCode.keyAt(i);
-#if DEBUG_MAPPING
- ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode);
-#endif
- return NO_ERROR;
+std::optional<int32_t> KeyLayoutMap::findScanCodeForLed(int32_t ledCode) const {
+ for (const auto& [scanCode, led] : mLedsByScanCode) {
+ if (led.ledCode == ledCode) {
+ ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, scanCode=%d.", __func__, ledCode, scanCode);
+ return scanCode;
}
}
-#if DEBUG_MAPPING
- ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
-#endif
- return NAME_NOT_FOUND;
+ ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode);
+ return std::nullopt;
}
-status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const {
- const size_t N = mLedsByUsageCode.size();
- for (size_t i = 0; i < N; i++) {
- if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) {
- *outUsageCode = mLedsByUsageCode.keyAt(i);
-#if DEBUG_MAPPING
- ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode);
-#endif
- return NO_ERROR;
+std::optional<int32_t> KeyLayoutMap::findUsageCodeForLed(int32_t ledCode) const {
+ for (const auto& [usageCode, led] : mLedsByUsageCode) {
+ if (led.ledCode == ledCode) {
+ ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, usageCode);
+ return usageCode;
}
}
-#if DEBUG_MAPPING
- ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode);
-#endif
- return NAME_NOT_FOUND;
+ ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode);
+ return std::nullopt;
}
-
// --- KeyLayoutMap::Parser ---
KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
@@ -264,10 +272,8 @@
status_t KeyLayoutMap::Parser::parse() {
while (!mTokenizer->isEof()) {
-#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
mTokenizer->skipDelimiters(WHITESPACE);
@@ -289,6 +295,10 @@
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseSensor();
if (status) return status;
+ } else if (keywordToken == "requires_kernel_config") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseRequiredKernelConfig();
+ if (status) return status;
} else {
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
@@ -325,8 +335,9 @@
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
- KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::unordered_map<int32_t, Key>& map =
+ mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+ if (map.find(code) != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -361,14 +372,13 @@
flags |= flag;
}
-#if DEBUG_PARSER
- ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
- mapUsage ? "usage" : "scan code", code, keyCode, flags);
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
+ mapUsage ? "usage" : "scan code", code, keyCode, flags);
+
Key key;
key.keyCode = keyCode;
key.flags = flags;
- map.add(code, key);
+ map.insert({code, key});
return NO_ERROR;
}
@@ -381,7 +391,7 @@
scanCodeToken.string());
return BAD_VALUE;
}
- if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
+ if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) {
ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
@@ -462,14 +472,12 @@
}
}
-#if DEBUG_PARSER
- ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
- "splitValue=%d, flatOverride=%d.",
- scanCode,
- axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
- axisInfo.splitValue, axisInfo.flatOverride);
-#endif
- mMap->mAxes.add(scanCode, axisInfo);
+ ALOGD_IF(DEBUG_PARSER,
+ "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
+ "splitValue=%d, flatOverride=%d.",
+ scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
+ axisInfo.flatOverride);
+ mMap->mAxes.insert({scanCode, axisInfo});
return NO_ERROR;
}
@@ -489,8 +497,9 @@
return BAD_VALUE;
}
- KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::unordered_map<int32_t, Led>& map =
+ mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
+ if (map.find(code) != map.end()) {
ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -505,14 +514,12 @@
return BAD_VALUE;
}
-#if DEBUG_PARSER
- ALOGD("Parsed led %s: code=%d, ledCode=%d.",
- mapUsage ? "usage" : "scan code", code, ledCode);
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code",
+ code, ledCode);
Led led;
led.ledCode = ledCode;
- map.add(code, led);
+ map.insert({code, led});
return NO_ERROR;
}
@@ -584,10 +591,8 @@
}
int32_t sensorDataIndex = indexOpt.value();
-#if DEBUG_PARSER
- ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
- ftl::enum_string(sensorType).c_str(), sensorDataIndex);
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+ ftl::enum_string(sensorType).c_str(), sensorDataIndex);
Sensor sensor;
sensor.sensorType = sensorType;
@@ -596,4 +601,23 @@
return NO_ERROR;
}
+// Parse the name of a required kernel config.
+// The layout won't be used if the specified kernel config is not present
+// Examples:
+// requires_kernel_config CONFIG_HID_PLAYSTATION
+status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() {
+ String8 codeToken = mTokenizer->nextToken(WHITESPACE);
+ std::string configName = codeToken.string();
+
+ const auto result = mMap->mRequiredKernelConfigs.emplace(configName);
+ if (!result.second) {
+ ALOGE("%s: Duplicate entry for required kernel config %s.",
+ mTokenizer->getLocation().string(), configName.c_str());
+ return BAD_VALUE;
+ }
+
+ ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str());
+ return NO_ERROR;
+}
+
} // namespace android
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index f0895b3..c3f5151 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -20,16 +20,23 @@
#include <unistd.h>
#include <limits.h>
-#include <input/Keyboard.h>
-#include <input/InputEventLabels.h>
-#include <input/KeyLayoutMap.h>
-#include <input/KeyCharacterMap.h>
#include <input/InputDevice.h>
+#include <input/InputEventLabels.h>
+#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/Keyboard.h>
+#include <log/log.h>
#include <utils/Errors.h>
-#include <utils/Log.h>
namespace android {
+static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name,
+ InputDeviceConfigurationFileType type) {
+ return name.empty()
+ ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
+ : getInputDeviceConfigurationFilePathByName(name, type);
+}
+
// --- KeyMap ---
KeyMap::KeyMap() {
@@ -111,11 +118,25 @@
}
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+ if (ret.ok()) {
+ keyLayoutMap = *ret;
+ keyLayoutFile = path;
+ return OK;
+ }
+
+ // Try to load fallback layout if the regular layout could not be loaded due to missing
+ // kernel modules
+ std::string fallbackPath(
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier,
+ InputDeviceConfigurationFileType::
+ KEY_LAYOUT,
+ "_fallback"));
+ ret = KeyLayoutMap::load(fallbackPath);
if (!ret.ok()) {
return ret.error().code();
}
keyLayoutMap = *ret;
- keyLayoutFile = path;
+ keyLayoutFile = fallbackPath;
return OK;
}
@@ -137,14 +158,6 @@
return OK;
}
-std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
- const std::string& name, InputDeviceConfigurationFileType type) {
- return name.empty()
- ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
- : getInputDeviceConfigurationFilePathByName(name, type);
-}
-
-
// --- Global functions ---
bool isKeyboardSpecialFunction(const PropertyMap* config) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 6ffe851..d947cd9 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,8 +36,9 @@
"liblog",
"libui",
"libutils",
+ "libvintf",
],
- data: ["data/*.kcm"],
+ data: ["data/*"],
test_suites: ["device-tests"],
}
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index 61e88df..e872fa4 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -64,13 +64,11 @@
mKeyMap.keyCharacterMapFile = path;
}
- virtual void SetUp() override {
+ void SetUp() override {
loadKeyLayout("Generic");
loadKeyCharacterMap("Generic");
}
- virtual void TearDown() override {}
-
KeyMap mKeyMap;
};
@@ -132,4 +130,20 @@
ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
}
+TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+ // We assert error message here because it's used by 'validatekeymaps' tool
+ ASSERT_EQ("Missing kernel config", ret.error().message());
+}
+
+TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath;
+ const std::shared_ptr<KeyLayoutMap>& map = *ret;
+ ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present";
+}
+
} // namespace android
diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl
new file mode 100644
index 0000000..2d0a507
--- /dev/null
+++ b/libs/input/tests/data/kl_with_required_fake_config.kl
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This KL should not be loaded unless the below config is present in the kernel
+# This config will never exist, and therefore this KL should never be loaded
+requires_kernel_config CONFIG_HID_FAKEMODULE
+
+# An arbitrary mapping taken from another file
+key 0x130 BUTTON_X
\ No newline at end of file
diff --git a/libs/input/tests/data/kl_with_required_real_config.kl b/libs/input/tests/data/kl_with_required_real_config.kl
new file mode 100644
index 0000000..303b23e
--- /dev/null
+++ b/libs/input/tests/data/kl_with_required_real_config.kl
@@ -0,0 +1,21 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This KL should not be loaded unless the below config is present in the kernel
+# The CONFIG_UHID option has been required for a while, and therefore it's safe
+# to assume that this will always be loaded
+requires_kernel_config CONFIG_UHID
+
+# An arbitrary mapping taken from another file
+key 0x130 BUTTON_X
\ No newline at end of file
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index ed728dc..4659b96 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -33,7 +33,7 @@
cc_library_headers {
name: "libnativedisplay_headers",
- export_include_dirs: ["include",],
+ export_include_dirs: ["include"],
}
cc_library_shared {
@@ -43,8 +43,6 @@
"include-private",
],
- clang: true,
-
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index d30efa1..dd07319 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -77,8 +77,6 @@
"include-private",
],
- clang: true,
-
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index cb92df3..f6f57dd 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -111,7 +111,6 @@
name: "librenderengine",
defaults: ["librenderengine_defaults"],
double_loadable: true,
- clang: true,
cflags: [
"-fvisibility=hidden",
"-Werror=format",
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index edd453a..2b93c6e 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -24,7 +24,6 @@
cc_library_shared {
name: "libsensor",
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -53,5 +52,9 @@
export_include_dirs: ["include"],
- export_shared_lib_headers: ["libbinder", "libpermission", "libhardware"],
+ export_shared_lib_headers: [
+ "libbinder",
+ "libpermission",
+ "libhardware",
+ ],
}
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 8fdb003..ac4be44 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -24,9 +24,10 @@
cc_test {
name: "libsensor_test",
- clang: true,
-
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
srcs: [
"Sensor_test.cpp",
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d138495..0f771a9 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -36,7 +36,6 @@
cc_defaults {
name: "libui-defaults",
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -111,7 +110,6 @@
},
double_loadable: true,
- clang: true,
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp
index c28c303..5d6070c 100644
--- a/libs/ui/tools/Android.bp
+++ b/libs/ui/tools/Android.bp
@@ -25,7 +25,7 @@
cc_defaults {
name: "libui_tools_default",
- clang_cflags: [
+ cflags: [
"-Wall",
"-Wextra",
"-Werror",
diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp
index fa449ae..e72ca74 100644
--- a/libs/vr/libbroadcastring/Android.bp
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -26,7 +26,6 @@
cc_test {
name: "broadcast_ring_tests",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index c1f6da3..c95603b 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -16,7 +16,6 @@
cc_library_static {
name: "libpdx",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -42,7 +41,6 @@
cc_test {
name: "pdx_tests",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -72,7 +70,6 @@
// Code analysis target.
cc_test {
name: "pdx_encoder_performance_test",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
index cc32b18..ac831ce 100644
--- a/libs/vr/libpdx/fuzz/Android.bp
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -9,7 +9,6 @@
cc_fuzz {
name: "libpdx_service_dispatcher_fuzzer",
- clang: true,
srcs: [
"service_dispatcher_fuzzer.cpp",
],
@@ -24,13 +23,12 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils"
+ "libcutils",
],
}
cc_fuzz {
name: "libpdx_message_fuzzer",
- clang: true,
srcs: [
"message_fuzzer.cpp",
],
@@ -45,13 +43,12 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils"
+ "libcutils",
],
}
cc_fuzz {
name: "libpdx_serialization_fuzzer",
- clang: true,
srcs: [
"serialization_fuzzer.cpp",
],
@@ -66,6 +63,6 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils"
+ "libcutils",
],
}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 8046857..a5758b5 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -9,7 +9,6 @@
cc_defaults {
name: "pdx_default_transport_compiler_defaults",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -26,7 +25,10 @@
cc_defaults {
name: "pdx_use_transport_servicefs",
export_include_dirs: ["private/servicefs"],
- whole_static_libs: ["libpdx_servicefs", "libservicefs"],
+ whole_static_libs: [
+ "libpdx_servicefs",
+ "libservicefs",
+ ],
}
cc_defaults {
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 216ca9f..7f88daf 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -9,7 +9,6 @@
cc_library_static {
name: "libpdx_uds",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -41,7 +40,6 @@
cc_test {
name: "libpdx_uds_tests",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/opengl/OWNERS b/opengl/OWNERS
index a47fb9a..379f763 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -6,7 +6,6 @@
jessehall@google.com
lfy@google.com
lpy@google.com
-timvp@google.com
romanl@google.com
vantablack@google.com
yuxinhu@google.com
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index c9fce8a..c1e935a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -108,7 +108,6 @@
// In particular, DO NOT add libutils nor anything "above" libui
"libgraphicsenv",
"libnativewindow",
- "libbacktrace",
"libbase",
],
}
@@ -165,6 +164,7 @@
"libnativeloader_lazy",
"libutils",
"libSurfaceFlingerProp",
+ "libunwindstack",
],
static_libs: [
"libEGL_getProcAddress",
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
index b7fdf97..96437c3 100644
--- a/opengl/libs/EGL/CallStack.h
+++ b/opengl/libs/EGL/CallStack.h
@@ -16,8 +16,8 @@
#pragma once
-#include <backtrace/Backtrace.h>
#include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <memory>
@@ -26,12 +26,15 @@
// Create a callstack with the current thread's stack trace.
// Immediately dump it to logcat using the given logtag.
static void log(const char* logtag) noexcept {
- std::unique_ptr<Backtrace> backtrace(
- Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- if (backtrace->Unwind(2)) {
- for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (unwinder.Unwind(data)) {
+ for (size_t i = 2, c = data.frames.size(); i < c; i++) {
+ auto& frame = data.frames[i];
+ // Trim the first two frames.
+ frame.num -= 2;
__android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
- backtrace->FormatFrameData(i).c_str());
+ unwinder.FormatFrame(frame).c_str());
}
}
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index f4dbe49..7619a50 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -1453,7 +1453,11 @@
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ if (err != 0) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ } else if (!s->cnx->useAngle) {
+ return EGL_TRUE;
+ } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
if (attribute == EGL_TIMESTAMPS_ANDROID) {
@@ -1463,7 +1467,11 @@
return EGL_TRUE;
}
int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ if (err != 0) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ } else if (!s->cnx->useAngle) {
+ return EGL_TRUE;
+ } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
if (s->setSmpte2086Attribute(attribute, value)) {
diff --git a/opengl/tests/gl2_cameraeye/AndroidManifest.xml b/opengl/tests/gl2_cameraeye/AndroidManifest.xml
index c53f7be..a4674e1 100644
--- a/opengl/tests/gl2_cameraeye/AndroidManifest.xml
+++ b/opengl/tests/gl2_cameraeye/AndroidManifest.xml
@@ -26,7 +26,7 @@
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:glEsVersion="0x00020000" />
<application android:label="@string/gl2cameraeye_name">
- <activity android:name="GL2CameraEye">
+ <activity android:name="GL2CameraEye" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/opengl/tests/gl2_java/AndroidManifest.xml b/opengl/tests/gl2_java/AndroidManifest.xml
index 8bb6840..500adb5 100644
--- a/opengl/tests/gl2_java/AndroidManifest.xml
+++ b/opengl/tests/gl2_java/AndroidManifest.xml
@@ -22,7 +22,8 @@
<activity android:name="GL2JavaActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/opengl/tests/gl2_jni/AndroidManifest.xml b/opengl/tests/gl2_jni/AndroidManifest.xml
index 1827e5f..b4ce99b 100644
--- a/opengl/tests/gl2_jni/AndroidManifest.xml
+++ b/opengl/tests/gl2_jni/AndroidManifest.xml
@@ -21,7 +21,8 @@
<activity android:name="GL2JNIActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/opengl/tests/gl_jni/AndroidManifest.xml b/opengl/tests/gl_jni/AndroidManifest.xml
index 5d0ec96..bedab56 100644
--- a/opengl/tests/gl_jni/AndroidManifest.xml
+++ b/opengl/tests/gl_jni/AndroidManifest.xml
@@ -24,7 +24,8 @@
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
index 6c23d42..d766be9 100644
--- a/opengl/tests/lighting1709/AndroidManifest.xml
+++ b/opengl/tests/lighting1709/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.android.lightingtest">
<application>
- <activity android:name="ClearActivity" android:label="LightingTest">
+ <activity android:name="ClearActivity" android:label="LightingTest" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/opengl/tests/testPauseResume/AndroidManifest.xml b/opengl/tests/testPauseResume/AndroidManifest.xml
index 1879bc3..ae82a82 100644
--- a/opengl/tests/testPauseResume/AndroidManifest.xml
+++ b/opengl/tests/testPauseResume/AndroidManifest.xml
@@ -24,7 +24,8 @@
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 4fb0d2e..86f6c7f 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -35,6 +35,7 @@
header_libs: ["bpf_headers"],
shared_libs: [
"libbase",
+ "libbinder",
"libbpf_bcc",
"libcutils",
"libgfxstats",
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index e916221..36ae179 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "gpuservice_unittest"
#include <android-base/stringprintf.h>
+#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
#include <bpf/BpfMap.h>
#include <gmock/gmock.h>
#include <gpumem/GpuMem.h>
@@ -65,11 +66,11 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
mTestableGpuMem.setInitialized();
errno = 0;
- mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC);
+ mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
+ TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC));
EXPECT_EQ(0, errno);
- EXPECT_LE(0, mTestMap.getMap().get());
EXPECT_TRUE(mTestMap.isValid());
}
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
index d76f039..5c04210 100644
--- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "gpuservice_unittest"
+#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
#include <bpf/BpfMap.h>
#include <gpumem/GpuMem.h>
#include <gtest/gtest.h>
@@ -64,11 +65,11 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
errno = 0;
- mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC);
+ mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
+ TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC));
EXPECT_EQ(0, errno);
- EXPECT_LE(0, mTestMap.getMap().get());
EXPECT_TRUE(mTestMap.isValid());
}
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 20c8ccf..7ea2288 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -18,12 +18,14 @@
#define LOG_TAG "gpuservice_unittest"
#include <unistd.h>
+#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <gpustats/GpuStats.h>
#include <gtest/gtest.h>
#include <stats_pull_atom_callback.h>
#include <statslog.h>
+#include <utils/Looper.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -61,8 +63,9 @@
// clang-format on
class GpuStatsTest : public testing::Test {
+ sp<android::Looper> looper;
public:
- GpuStatsTest() {
+ GpuStatsTest() : looper(Looper::prepare(0 /* opts */)) {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
@@ -72,6 +75,16 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ // This is required for test due to GpuStats instance spawns binder transactions
+ // in its destructor. After the gtest destructor test exits immidiatelly.
+ // It results in binder thread not able to process above binder transactions and memory leak
+ // occures. Binder thread needs time to process callbacks transactions.
+ // It leads to GpuStats instance destructor needs to be called in advance.
+ mGpuStats.reset(nullptr);
+ // performs all pending callbacks until all data has been consumed
+ // gives time to process binder transactions by thread pool
+ looper->pollAll(1000);
}
std::string inputCommand(InputCommand cmd);
@@ -79,6 +92,10 @@
void SetUp() override {
mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
mGlesVersion = property_get_int32("ro.opengles.version", 0);
+
+ // start the thread pool
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
}
std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>();
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
index 82c6ee1..c88bfe9 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1,3 +1 @@
-lzye@google.com
-michaelwr@google.com
-svv@google.com
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3bd3275..01146a3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,7 +71,7 @@
"libstatslog",
"libui",
"libutils",
- "PlatformProperties",
+ "libPlatformProperties",
],
static_libs: [
"libc++fs",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index d6a6bd2..669d2e1 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -452,8 +452,7 @@
return false;
}
- std::vector<int32_t> scanCodes;
- keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
+ std::vector<int32_t> scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode);
const size_t N = scanCodes.size();
for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
int32_t sc = scanCodes[i];
@@ -548,10 +547,10 @@
return NAME_NOT_FOUND;
}
- int32_t scanCode;
- if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) {
- *outScanCode = scanCode;
+ std::optional<int32_t> scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led);
+ if (scanCode.has_value()) {
+ if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) {
+ *outScanCode = *scanCode;
return NO_ERROR;
}
}
@@ -865,8 +864,7 @@
Device* device = getDeviceLocked(deviceId);
if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
- std::vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
+ std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode);
if (scanCodes.size() != 0) {
if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
for (size_t i = 0; i < scanCodes.size(); i++) {
@@ -890,8 +888,8 @@
device->keyMap.keyLayoutMap == nullptr) {
return AKEYCODE_UNKNOWN;
}
- std::vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes);
+ std::vector<int32_t> scanCodes =
+ device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode);
if (scanCodes.empty()) {
ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input"
"device %d",
@@ -960,20 +958,16 @@
Device* device = getDeviceLocked(deviceId);
if (device != nullptr && device->keyMap.haveKeyLayout()) {
- std::vector<int32_t> scanCodes;
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
- scanCodes.clear();
+ std::vector<int32_t> scanCodes =
+ device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]);
- status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex],
- &scanCodes);
- if (!err) {
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (device->keyBitmask.test(scanCodes[sc])) {
- outFlags[codeIndex] = 1;
- break;
- }
+ // check the possible scan codes identified by the layout map against the
+ // map of codes actually emitted by the driver
+ for (size_t sc = 0; sc < scanCodes.size(); sc++) {
+ if (device->keyBitmask.test(scanCodes[sc])) {
+ outFlags[codeIndex] = 1;
+ break;
}
}
}
@@ -1027,14 +1021,15 @@
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
- if (err == NO_ERROR) {
- return NO_ERROR;
- }
+ if (device == nullptr || !device->keyMap.haveKeyLayout()) {
+ return NAME_NOT_FOUND;
}
-
- return NAME_NOT_FOUND;
+ std::optional<AxisInfo> info = device->keyMap.keyLayoutMap->mapAxis(scanCode);
+ if (!info.has_value()) {
+ return NAME_NOT_FOUND;
+ }
+ *outAxisInfo = *info;
+ return NO_ERROR;
}
base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 637b1cb..2acbb0a 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1053,6 +1053,10 @@
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
} else {
+ if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT &&
+ !mConfig.showTouches) {
+ mPointerController->clearSpots();
+ }
mPointerController.reset();
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a26a0bc..d51ce35 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -219,7 +219,7 @@
mSpotsByDisplay[displayId] = newSpots;
}
- void clearSpots() override {}
+ void clearSpots() override { mSpotsByDisplay.clear(); }
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
@@ -8578,7 +8578,8 @@
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -8605,6 +8606,13 @@
iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID);
ASSERT_TRUE(iter != fakePointerController->getSpots().end());
ASSERT_EQ(size_t(2), iter->second.size());
+
+ // Disable the show touches configuration and ensure the spots are cleared.
+ mFakePolicy->setShowTouches(false);
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+
+ ASSERT_TRUE(fakePointerController->getSpots().empty());
}
TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 000a2cb..cbb95f9 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -280,7 +280,7 @@
"liblog",
],
static_libs: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
@@ -288,6 +288,6 @@
"libui",
],
export_static_lib_headers: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index be16942..38662bd 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1586,8 +1586,10 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
+ const float parentShadowRadius =
+ newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius;
child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
- newParent->mEffectiveShadowRadius);
+ parentShadowRadius);
}
}
diff --git a/services/utils/tests/Android.bp b/services/utils/tests/Android.bp
index 54cf5b7..cfa8a08 100644
--- a/services/utils/tests/Android.bp
+++ b/services/utils/tests/Android.bp
@@ -34,5 +34,4 @@
"libgmock",
"libserviceutils",
],
- clang: true,
}
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 440c5b1..5719b5c 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -37,7 +37,6 @@
"vulkan_headers",
],
},
- clang: true,
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c4b1487..48d6fd0 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -939,8 +939,7 @@
// VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and
// VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR. We technically cannot
// know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a
- // surface, and that cannot be relied upon.
- present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
+ // surface, and that cannot be relied upon. Therefore, don't return it.
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
} else {
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
index 0daad9c..a6d540b 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -27,7 +27,6 @@
proprietary: true,
relative_install_path: "hw",
- clang: true,
cflags: [
"-fvisibility=hidden",
"-fstrict-aliasing",
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index fa0258b..b6d3a0b 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -37,7 +37,6 @@
cc_library_static {
name: "libvkjson_ndk",
- clang: true,
srcs: [
"vkjson.cc",
"vkjson_instance.cc",