diff --git a/.gitignore b/.gitignore index ed653c6b4ab9875ce7c93bbfbafe80ead8a5929e..1ad8a24a4b10e06aa044eadf8962d4b618ea50dd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ .idea/ .vscode/ *.code-workspace + +# Rust artifacts +**/target/ diff --git a/Android.bp b/Android.bp index 7f1ef671bc93339d60409b90b8cf773bc9c156ef..8c4dfbbea429a95aa2311cffe5911ac08491db80 100644 --- a/Android.bp +++ b/Android.bp @@ -97,6 +97,12 @@ filegroup { ], } +aidl_library { + name: "PersistableBundle_aidl", + hdrs: ["aidl/binder/android/os/PersistableBundle.aidl"], + strip_import_prefix: "aidl/binder", +} + cc_library_headers { name: "libandroid_headers_private", export_include_dirs: ["include/private"], diff --git a/TEST_MAPPING b/TEST_MAPPING index cd8f3cdcc24c52501bc12d978664ed56cc76b4fe..9c0116957d9eda52cbcedb17d981141989b32228 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -7,7 +7,8 @@ "include-filter": "*" }, { - "exclude-filter": "*ChildLayerTest#ChildrenSurviveParentDestruction" + // TODO(b/305717998): Deflake and re-enable + "exclude-filter": "*ChildLayerTest*" } ] }, diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl index 493ecb414c7454d20002dba5f9a04b93d6fb20df..248e9738df6943d244295dca81da19669f426624 100644 --- a/aidl/binder/android/os/PersistableBundle.aidl +++ b/aidl/binder/android/os/PersistableBundle.aidl @@ -17,4 +17,4 @@ package android.os; -@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h"; +@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h" ndk_header "android/persistable_bundle_aidl.h"; diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 81056262c35d2112916cc4b2edf16e1899f6c00c..5719a09a165fe9d2e0693ac4d1bac12dae12fb8e 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -692,7 +692,7 @@ static bool verifyKernelTraceFuncs(const char* funcs) while (func) { if (!strchr(func, '*')) { String8 fancyFunc = String8::format("\n%s\n", func); - bool found = funcList.find(fancyFunc.string(), 0) >= 0; + bool found = funcList.find(fancyFunc.c_str(), 0) >= 0; if (!found || func[0] == '\0') { fprintf(stderr, "error: \"%s\" is not a valid kernel function " "to trace.\n", func); @@ -796,11 +796,11 @@ static bool setCategoriesEnableFromFile(const char* categories_file) bool ok = true; while (!tokenizer->isEol()) { String8 token = tokenizer->nextToken(" "); - if (token.isEmpty()) { + if (token.empty()) { tokenizer->skipDelimiters(" "); continue; } - ok &= setCategoryEnable(token.string()); + ok &= setCategoryEnable(token.c_str()); } delete tokenizer; return ok; diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index f1d8c72d8559f15c5dff3227c12d25085dfd44e1..3e6d2e01f8e556224a26664adacdd762e6a22a37 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -93,6 +93,10 @@ on late-init chmod 0666 /sys/kernel/tracing/events/binder/binder_unlock/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_set_priority/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_set_priority/enable + chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_command/enable + chmod 0666 /sys/kernel/tracing/events/binder/binder_command/enable + chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_return/enable + chmod 0666 /sys/kernel/tracing/events/binder/binder_return/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/enable chmod 0666 /sys/kernel/tracing/events/i2c/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable @@ -228,10 +232,6 @@ on late-init chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable -# Tracing disabled by default - write /sys/kernel/debug/tracing/tracing_on 0 - write /sys/kernel/tracing/tracing_on 0 - # Read and truncate the kernel trace. chmod 0666 /sys/kernel/debug/tracing/trace chmod 0666 /sys/kernel/tracing/trace @@ -310,18 +310,9 @@ on late-init chmod 0666 /sys/kernel/tracing/events/synthetic/suspend_resume_minimal/enable chmod 0666 /sys/kernel/debug/tracing/events/synthetic/suspend_resume_minimal/enable -on late-init && property:ro.boot.fastboot.boottrace=enabled - setprop debug.atrace.tags.enableflags 802922 - setprop persist.traced.enable 0 - write /sys/kernel/tracing/events/binder/binder_transaction/enable 1 - write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 1 - write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 1 - write /sys/kernel/tracing/events/binder/binder_set_priority/enable 1 - write /sys/kernel/tracing/events/binder/binder_lock/enable 1 - write /sys/kernel/tracing/events/binder/binder_locked/enable 1 - write /sys/kernel/tracing/events/binder/binder_unlock/enable 1 - write /sys/kernel/debug/tracing/tracing_on 1 - write /sys/kernel/tracing/tracing_on 1 +on late-init && property:ro.boot.fastboot.boottrace= + write /sys/kernel/debug/tracing/tracing_on 0 + write /sys/kernel/tracing/tracing_on 0 # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created @@ -534,7 +525,6 @@ on late-init && property:ro.boot.hypervisor.vm.supported=1 chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/id chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/id - on property:persist.debug.atrace.boottrace=1 start boottrace @@ -543,17 +533,3 @@ service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categ user root disabled oneshot - -on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled - setprop debug.atrace.tags.enableflags 0 - setprop persist.traced.enable 1 - write /sys/kernel/tracing/events/binder/binder_transaction/enable 0 - write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 0 - write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 0 - write /sys/kernel/tracing/events/binder/binder_set_priority/enable 0 - write /sys/kernel/tracing/events/binder/binder_lock/enable 0 - write /sys/kernel/tracing/events/binder/binder_locked/enable 0 - write /sys/kernel/tracing/events/binder/binder_unlock/enable 0 - write /sys/kernel/debug/tracing/tracing_on 0 - write /sys/kernel/tracing/tracing_on 0 - diff --git a/cmds/cmd/Android.bp b/cmds/cmd/Android.bp index c3d260144420c653bf21fcd9b908aa00f54f9a3d..27ef78854c311cc0be5102def06de65885a87dfd 100644 --- a/cmds/cmd/Android.bp +++ b/cmds/cmd/Android.bp @@ -27,6 +27,9 @@ cc_library_static { "libselinux", "libbinder", ], + whole_static_libs: [ + "libc++fs", + ], cflags: [ "-Wall", diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 8f1c01ab2cf86bc1b6a8184437cb7730a05ce719..0ce7711574d37581fa7924f7cc4f1372e3620e3b 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -69,16 +70,14 @@ public: virtual int openFile(const String16& path, const String16& seLinuxContext, const String16& mode) { String8 path8(path); - char cwd[256]; - getcwd(cwd, 256); - String8 fullPath(cwd); - fullPath.appendPath(path8); + auto fullPath = std::filesystem::current_path(); + fullPath /= path8.c_str(); if (!mActive) { mErrorLog << "Open attempt after active for: " << fullPath << endl; return -EPERM; } #if DEBUG - ALOGD("openFile: %s, full=%s", path8.string(), fullPath.string()); + ALOGD("openFile: %s, full=%s", path8.c_str(), fullPath.c_str()); #endif int flags = 0; bool checkRead = false; @@ -96,10 +95,10 @@ public: flags = O_RDWR; checkRead = checkWrite = true; } else { - mErrorLog << "Invalid mode requested: " << mode.string() << endl; + mErrorLog << "Invalid mode requested: " << mode.c_str() << endl; return -EINVAL; } - int fd = open(fullPath.string(), flags, S_IRWXU|S_IRWXG); + int fd = open(fullPath.c_str(), flags, S_IRWXU|S_IRWXG); #if DEBUG ALOGD("openFile: fd=%d", fd); #endif @@ -109,29 +108,29 @@ public: if (is_selinux_enabled() && seLinuxContext.size() > 0) { String8 seLinuxContext8(seLinuxContext); char* tmp = nullptr; - getfilecon(fullPath.string(), &tmp); + getfilecon(fullPath.c_str(), &tmp); Unique_SecurityContext context(tmp); if (checkWrite) { - int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), + int accessGranted = selinux_check_access(seLinuxContext8.c_str(), context.get(), "file", "write", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux write check!"); #endif close(fd); - mErrorLog << "System server has no access to write file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl; + mErrorLog << "System server has no access to write file context " << context.get() << " (from path " << fullPath.c_str() << ", context " << seLinuxContext8.c_str() << ")" << endl; return -EPERM; } } if (checkRead) { - int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), + int accessGranted = selinux_check_access(seLinuxContext8.c_str(), context.get(), "file", "read", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux read check!"); #endif close(fd); - mErrorLog << "System server has no access to read file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl; + mErrorLog << "System server has no access to read file context " << context.get() << " (from path " << fullPath.c_str() << ", context " << seLinuxContext8.c_str() << ")" << endl; return -EPERM; } } diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 860a2d82e64a0502400e0d5a0bdd8e7386c83533..23f185e305e206aa817d142c9add36c28fced7b6 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -104,6 +104,8 @@ cc_defaults { "libvintf", "libbinderdebug", "packagemanager_aidl-cpp", + "server_configurable_flags", + "device_policy_aconfig_flags_c_lib", ], srcs: [ "DumpstateService.cpp", diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index a7bc018ff644e231b22368af63f0525e39a65217..ba0a38aad90fb2ec0f9ec23dd921b24826b72389 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -37,6 +37,8 @@ struct DumpstateInfo { Dumpstate* ds = nullptr; int32_t calling_uid = -1; std::string calling_package; + int32_t user_id = -1; + bool keep_bugreport_on_retrieval = false; }; static binder::Status exception(uint32_t code, const std::string& msg, @@ -60,7 +62,7 @@ static binder::Status exception(uint32_t code, const std::string& msg, [[noreturn]] static void* dumpstate_thread_retrieve(void* data) { std::unique_ptr ds_info(static_cast(data)); - ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package); + ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package, ds_info->keep_bugreport_on_retrieval); MYLOGD("Finished retrieving a bugreport. Exiting.\n"); exit(0); } @@ -141,6 +143,7 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WEAR && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_ONBOARDING && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) { MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); @@ -200,9 +203,10 @@ binder::Status DumpstateService::cancelBugreport(int32_t calling_uid, } binder::Status DumpstateService::retrieveBugreport( - int32_t calling_uid, const std::string& calling_package, + int32_t calling_uid, const std::string& calling_package, int32_t user_id, android::base::unique_fd bugreport_fd, const std::string& bugreport_file, + const bool keep_bugreport_on_retrieval, const sp& listener) { ds_ = &(Dumpstate::GetInstance()); @@ -210,6 +214,8 @@ binder::Status DumpstateService::retrieveBugreport( ds_info->ds = ds_; ds_info->calling_uid = calling_uid; ds_info->calling_package = calling_package; + ds_info->user_id = user_id; + ds_info->keep_bugreport_on_retrieval = keep_bugreport_on_retrieval; ds_->listener_ = listener; std::unique_ptr options = std::make_unique(); // Use a /dev/null FD when initializing options since none is provided. diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index dd7331932c61ef99f28da9976f51ecb36c16f198..7b76c363806d28c59328d363ab0420fe36ef9982 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h @@ -48,8 +48,10 @@ class DumpstateService : public BinderService, public BnDumpst binder::Status retrieveBugreport(int32_t calling_uid, const std::string& calling_package, + int32_t user_id, android::base::unique_fd bugreport_fd, const std::string& bugreport_file, + const bool keep_bugreport_on_retrieval, const sp& listener) override; diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 484231225d24b94a911178287180bd6e291043a7..615701ccc123780c0d42d10674a599505fc98294 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -207,9 +207,7 @@ std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -1; int PropertiesHelper::unroot_ = -1; int PropertiesHelper::parallel_run_ = -1; -#if !defined(__ANDROID_VNDK__) int PropertiesHelper::strict_run_ = -1; -#endif bool PropertiesHelper::IsUserBuild() { if (build_type_.empty()) { @@ -240,7 +238,6 @@ bool PropertiesHelper::IsParallelRun() { return parallel_run_ == 1; } -#if !defined(__ANDROID_VNDK__) bool PropertiesHelper::IsStrictRun() { if (strict_run_ == -1) { // Defaults to using stricter timeouts. @@ -248,7 +245,6 @@ bool PropertiesHelper::IsStrictRun() { } return strict_run_ == 1; } -#endif int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 6049e3e19d76d7d0f046ae48f9b7890b60ed78db..9e955e3c045932ccb57ab5315877868aef7c06ff 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -198,18 +198,14 @@ class PropertiesHelper { * will default to true. This results in shortened timeouts for flaky * sections. */ -#if !defined(__ANDROID_VNDK__) static bool IsStrictRun(); -#endif private: static std::string build_type_; static int dry_run_; static int unroot_; static int parallel_run_; -#if !defined(__ANDROID_VNDK__) static int strict_run_; -#endif }; /* diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 0dc8f5ad64600682abdc0fb5b8d13399e65c62be..97c470e08c741cdbda0b9cd209660b3ba28eddba 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -49,12 +49,18 @@ interface IDumpstate { // Default mode. const int BUGREPORT_MODE_DEFAULT = 6; + // Bugreport taken for onboarding related flows. + const int BUGREPORT_MODE_ONBOARDING = 7; + // Use pre-dumped data. const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 0x1; // Defer user consent. const int BUGREPORT_FLAG_DEFER_CONSENT = 0x2; + // Keep bugreport stored after retrieval. + const int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 0x4; + /** * Speculatively pre-dumps UI data for a bugreport request that might come later. * @@ -113,12 +119,16 @@ interface IDumpstate { * * @param callingUid UID of the original application that requested the report. * @param callingPackage package of the original application that requested the report. + * @param userId user Id of the original package that requested the report. * @param bugreportFd the file to which the zipped bugreport should be written * @param bugreportFile the path of the bugreport file + * @param keepBugreportOnRetrieval boolean to indicate if the bugreport should be kept in the + * platform after it has been retrieved by the caller. * @param listener callback for updates; optional */ - void retrieveBugreport(int callingUid, @utf8InCpp String callingPackage, + void retrieveBugreport(int callingUid, @utf8InCpp String callingPackage, int userId, FileDescriptor bugreportFd, @utf8InCpp String bugreportFile, + boolean keepBugreportOnRetrieval, IDumpstateListener listener); } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index e93b46c666f6a9bb474a4ace47296cf82e12c7d0..83b336c62f09d2b94e5d9281a6ff477a4d38ed53 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -59,6 +59,7 @@ #include #include +#include #include #include #include @@ -187,6 +188,7 @@ void add_mountinfo(); #define CGROUPFS_DIR "/sys/fs/cgroup" #define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk" #define DROPBOX_DIR "/data/system/dropbox" +#define PRINT_FLAGS "/system/bin/printflags" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -1033,8 +1035,6 @@ static void DoLogcat() { CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); DoRadioLogcat(); - RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); - /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */ RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}); @@ -1087,8 +1087,14 @@ static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted // by the early call to MaybeSnapshotSystemTrace(), if any background // tracing was happening. - if (!ds.has_system_trace_) { - // No background trace was happening at the time dumpstate was invoked. + bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0; + if (!system_trace_exists) { + // No background trace was happening at the time MaybeSnapshotSystemTrace() was invoked. + if (!PropertiesHelper::IsUserBuild()) { + MYLOGI( + "No system traces found. Check for previously uploaded traces by looking for " + "go/trace-uuid in logcat") + } return; } ds.AddZipEntry( @@ -1235,7 +1241,7 @@ static void DumpPacketStats() { static void DumpIpAddrAndRules() { /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */ - RunCommand("NETWORK INTERFACES", {"ip", "link"}); + RunCommand("NETWORK INTERFACES", {"ip", "-s", "link"}); RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"}); RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"}); RunCommand("IP RULES", {"ip", "rule", "show"}); @@ -1420,12 +1426,12 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { auto ret = sm->list([&](const auto& interfaces) { for (const std::string& interface : interfaces) { std::string cleanName = interface; - std::replace_if(cleanName.begin(), - cleanName.end(), - [](char c) { - return !isalnum(c) && - std::string("@-_:.").find(c) == std::string::npos; - }, '_'); + std::replace_if( + cleanName.begin(), cleanName.end(), + [](char c) { + return !isalnum(c) && std::string("@-_.").find(c) == std::string::npos; + }, + '_'); const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; bool empty = false; @@ -1518,7 +1524,7 @@ static void DumpExternalFragmentationInfo() { } static void DumpstateLimitedOnly() { - // Trimmed-down version of dumpstate to only include a whitelisted + // Trimmed-down version of dumpstate to only include a allowlisted // set of logs (system log, event log, and system server / system app // crashes, and networking logs). See b/136273873 and b/138459828 // for context. @@ -1648,8 +1654,6 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { dump_board = ds.dump_pool_->enqueueTaskWithFd( DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1); dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1); - post_process_ui_traces = ds.dump_pool_->enqueueTask( - POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds); } // Dump various things. Note that anything that takes "long" (i.e. several seconds) should @@ -1758,6 +1762,14 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { RunCommand("SYSTEM PROPERTIES", {"getprop"}); + DumpFile("SYSTEM BUILD-TIME RELEASE FLAGS", "/system/etc/build_flags.json"); + DumpFile("SYSTEM_EXT BUILD-TIME RELEASE FLAGS", "/system_ext/etc/build_flags.json"); + DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json"); + DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json"); + + RunCommand("ACONFIG FLAGS", {PRINT_FLAGS}, + CommandOptions::WithTimeout(10).Always().DropRoot().Build()); + RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); @@ -1851,12 +1863,6 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { DumpIncidentReport); } - if (ds.dump_pool_) { - WaitForTask(std::move(post_process_ui_traces)); - } else { - RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces); - } - MaybeAddUiTracesToZip(); return Dumpstate::RunStatus::OK; @@ -2162,6 +2168,11 @@ static void DumpstateWifiOnly() { printf("========================================================\n"); } +// Collects a lightweight dumpstate to be used for debugging onboarding related flows. +static void DumpstateOnboardingOnly() { + ds.AddDir(LOGPERSIST_DATA_DIR, false); +} + Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX"; const size_t buf_size = temp_file_pattern.length() + 1; @@ -2294,6 +2305,7 @@ static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl( return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return dumpstate_hal_hidl::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return dumpstate_hal_hidl::DumpstateMode::DEFAULT; } @@ -2315,6 +2327,7 @@ static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAi return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; } @@ -2798,6 +2811,8 @@ static inline const char* ModeToString(Dumpstate::BugreportMode mode) { return "BUGREPORT_TELEPHONY"; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return "BUGREPORT_WIFI"; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: + return "BUGREPORT_ONBOARDING"; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return "BUGREPORT_DEFAULT"; } @@ -2843,6 +2858,10 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt options->wifi_only = true; options->do_screenshot = false; break; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: + options->onboarding_only = true; + options->do_screenshot = false; + break; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: break; } @@ -2955,14 +2974,17 @@ Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& call return status; } -Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package) { - Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package); +Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package, + const bool keep_bugreport_on_retrieval) { + Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package, + keep_bugreport_on_retrieval); HandleRunStatus(status); return status; } Dumpstate::RunStatus Dumpstate::RetrieveInternal(int32_t calling_uid, - const std::string& calling_package) { + const std::string& calling_package, + const bool keep_bugreport_on_retrieval) { consent_callback_ = new ConsentCallback(); const String16 incidentcompanion("incidentcompanion"); sp ics( @@ -2997,9 +3019,12 @@ Dumpstate::RunStatus Dumpstate::RetrieveInternal(int32_t calling_uid, bool copy_succeeded = android::os::CopyFileToFd(path_, options_->bugreport_fd.get()); - if (copy_succeeded) { - android::os::UnlinkAndLogOnError(path_); + + if (copy_succeeded && (!android::app::admin::flags::onboarding_bugreport_v2_enabled() + || !keep_bugreport_on_retrieval)) { + android::os::UnlinkAndLogOnError(path_); } + return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR; } @@ -3049,6 +3074,7 @@ void Dumpstate::Cancel() { } void Dumpstate::PreDumpUiData() { + MaybeSnapshotSystemTrace(); MaybeSnapshotUiTraces(); } @@ -3235,25 +3261,23 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // duration is logged into MYLOG instead. PrintHeader(); - bool is_dumpstate_restricted = options_->telephony_only - || options_->wifi_only - || options_->limited_only; - if (!is_dumpstate_restricted) { - // Invoke critical dumpsys first to preserve system state, before doing anything else. - RunDumpsysCritical(); - } - MaybeTakeEarlyScreenshot(); - + bool is_dumpstate_restricted = + options_->telephony_only || options_->wifi_only || options_->limited_only; if (!is_dumpstate_restricted) { // Snapshot the system trace now (if running) to avoid that dumpstate's // own activity pushes out interesting data from the trace ring buffer. // The trace file is added to the zip by MaybeAddSystemTraceToZip(). MaybeSnapshotSystemTrace(); + // Invoke critical dumpsys to preserve system state, before doing anything else. + RunDumpsysCritical(); + // Snapshot the UI traces now (if running). // The trace files will be added to bugreport later. MaybeSnapshotUiTraces(); } + + MaybeTakeEarlyScreenshot(); onUiIntensiveBugreportDumpsFinished(calling_uid); MaybeCheckUserConsent(calling_uid, calling_package); if (options_->telephony_only) { @@ -3262,6 +3286,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, DumpstateWifiOnly(); } else if (options_->limited_only) { DumpstateLimitedOnly(); + } else if (options_->onboarding_only) { + DumpstateOnboardingOnly(); } else { // Dump state for the default case. This also drops root. RunStatus s = DumpstateDefaultAfterCritical(); @@ -3348,6 +3374,19 @@ void Dumpstate::MaybeTakeEarlyScreenshot() { } void Dumpstate::MaybeSnapshotSystemTrace() { + // When capturing traces via bugreport handler (BH), this function will be invoked twice: + // 1) When BH invokes IDumpstate::PreDumpUiData() + // 2) When BH invokes IDumpstate::startBugreport(flags = BUGREPORT_USE_PREDUMPED_UI_DATA) + // In this case we don't want to re-invoke perfetto in step 2. + // In all other standard invocation states, this function is invoked once + // without the flag BUGREPORT_USE_PREDUMPED_UI_DATA. + if (options_->use_predumped_ui_data) { + return; + } + + // If a stale file exists already, remove it. + unlink(SYSTEM_TRACE_SNAPSHOT); + // If a background system trace is happening and is marked as "suitable for // bugreport" (i.e. bugreport_score > 0 in the trace config), this command // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) @@ -3355,14 +3394,8 @@ void Dumpstate::MaybeSnapshotSystemTrace() { // Note: this should not be enqueued as we need to freeze the trace before // dumpstate starts. Otherwise the trace ring buffers will contain mostly // the dumpstate's own activity which is irrelevant. - int res = RunCommand( - "SERIALIZE PERFETTO TRACE", - {"perfetto", "--save-for-bugreport"}, - CommandOptions::WithTimeout(10) - .DropRoot() - .CloseAllFileDescriptorsOnExec() - .Build()); - has_system_trace_ = res == 0; + RunCommand("SERIALIZE PERFETTO TRACE", {"perfetto", "--save-for-bugreport"}, + CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build()); // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip // file in the later stages. } @@ -3389,33 +3422,6 @@ void Dumpstate::MaybeSnapshotUiTraces() { "", command, CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); } - - // This command needs to be run as root - static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector { - "service", "call", "SurfaceFlinger", "1042" - }; - // Empty name because it's not intended to be classified as a bugreport section. - // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport. - RunCommand( - "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES, - CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build()); -} - -void Dumpstate::MaybePostProcessUiTraces() { - if (PropertiesHelper::IsUserBuild()) { - return; - } - - RunCommand( - // Empty name because it's not intended to be classified as a bugreport section. - // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport. - "", { - "/system/xbin/su", "system", - "/system/bin/layertracegenerator", - "/data/misc/wmtrace/transactions_trace.winscope", - "/data/misc/wmtrace/layers_trace_from_transactions.winscope" - }, - CommandOptions::WithTimeout(120).Always().RedirectStderr().Build()); } void Dumpstate::MaybeAddUiTracesToZip() { diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 8a31c314d9dff8683a88525d60413276849f2699..c66fd1cce6b80b77610ae82e8dc0b48fc5eaee3d 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -201,6 +201,7 @@ class Dumpstate { BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR, BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY, BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI, + BUGREPORT_ONBOARDING = android::os::IDumpstate::BUGREPORT_MODE_ONBOARDING, BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT }; @@ -209,7 +210,9 @@ class Dumpstate { BUGREPORT_USE_PREDUMPED_UI_DATA = android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA, BUGREPORT_FLAG_DEFER_CONSENT = - android::os::IDumpstate::BUGREPORT_FLAG_DEFER_CONSENT + android::os::IDumpstate::BUGREPORT_FLAG_DEFER_CONSENT, + BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = + android::os::IDumpstate::BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL }; static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS; @@ -360,7 +363,8 @@ class Dumpstate { * * Initialize() dumpstate before calling this method. */ - RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package); + RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package, + const bool keep_bugreport_on_retrieval); @@ -412,6 +416,7 @@ class Dumpstate { bool show_header_only = false; bool telephony_only = false; bool wifi_only = false; + bool onboarding_only = false; // Trimmed-down version of dumpstate to only include whitelisted logs. bool limited_only = false; // Whether progress updates should be published. @@ -471,11 +476,6 @@ class Dumpstate { // Whether it should take an screenshot earlier in the process. bool do_early_screenshot_ = false; - // This is set to true when the trace snapshot request in the early call to - // MaybeSnapshotSystemTrace(). When this is true, the later stages of - // dumpstate will append the trace to the zip archive. - bool has_system_trace_ = false; - std::unique_ptr progress_; // When set, defines a socket file-descriptor use to report progress to bugreportz @@ -560,7 +560,8 @@ class Dumpstate { private: RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package); - RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package); + RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package, + const bool keep_bugreport_on_retrieval); RunStatus DumpstateDefaultAfterCritical(); RunStatus dumpstate(); @@ -568,7 +569,6 @@ class Dumpstate { void MaybeTakeEarlyScreenshot(); void MaybeSnapshotSystemTrace(); void MaybeSnapshotUiTraces(); - void MaybePostProcessUiTraces(); void MaybeAddUiTracesToZip(); void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid); diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index a417837ef9c38eb28a6e97836a12181f8404db9d..fc828864d5e0997716b27e2781d826272a9ec9c8 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -1000,7 +1000,6 @@ TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) { TEST_F(DumpstateTest, PreDumpUiData) { // These traces are always enabled, i.e. they are always pre-dumped const std::vector uiTraces = { - std::filesystem::path{"/data/misc/wmtrace/transactions_trace.winscope"}, std::filesystem::path{"/data/misc/wmtrace/wm_transition_trace.winscope"}, std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"}, }; diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 3d2bdf1d6f45166f8528a60e3b1a1a38b6db15ad..6c4e4b3bc308b218924285bb460518f67be9e13a 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -543,7 +543,7 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi if ((status == TIMED_OUT) && (!asProto)) { std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n", - String8(serviceName).string(), timeout.count()); + String8(serviceName).c_str(), timeout.count()); WriteStringToFd(msg, fd); } @@ -562,6 +562,6 @@ void Dumpsys::writeDumpFooter(int fd, const String16& serviceName, oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S"); std::string msg = StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n", - elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str()); + elapsedDuration.count(), String8(serviceName).c_str(), oss.str().c_str()); WriteStringToFd(msg, fd); } diff --git a/cmds/evemu-record/Android.bp b/cmds/evemu-record/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..1edacec384b4aefe7db4ec46dd447963ccda3b5a --- /dev/null +++ b/cmds/evemu-record/Android.bp @@ -0,0 +1,13 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_binary { + name: "evemu-record", + srcs: ["main.rs"], + rustlibs: [ + "libclap", + "liblibc", + "libnix", + ], +} diff --git a/cmds/evemu-record/OWNERS b/cmds/evemu-record/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..c88bfe97cab9d6c272648703390956510906d003 --- /dev/null +++ b/cmds/evemu-record/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/INPUT_OWNERS diff --git a/cmds/evemu-record/evdev.rs b/cmds/evemu-record/evdev.rs new file mode 100644 index 0000000000000000000000000000000000000000..35feea1930a9ecf20b048592e9029600ad1ea19f --- /dev/null +++ b/cmds/evemu-record/evdev.rs @@ -0,0 +1,299 @@ +/* + * Copyright 2023 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. + */ + +//! Wrappers for the Linux evdev APIs. + +use std::fs::File; +use std::io; +use std::mem; +use std::os::fd::{AsRawFd, OwnedFd}; +use std::path::Path; + +use libc::c_int; +use nix::sys::time::TimeVal; + +pub const SYN_CNT: usize = 0x10; +pub const KEY_CNT: usize = 0x300; +pub const REL_CNT: usize = 0x10; +pub const ABS_CNT: usize = 0x40; +pub const MSC_CNT: usize = 0x08; +pub const SW_CNT: usize = 0x11; +pub const LED_CNT: usize = 0x10; +pub const SND_CNT: usize = 0x08; +pub const REP_CNT: usize = 0x02; + +// Disable naming warnings, as these are supposed to match the EV_ constants in input-event-codes.h. +#[allow(non_camel_case_types)] +// Some of these types aren't referenced for evemu purposes, but are included for completeness. +#[allow(dead_code)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum EventType { + SYN = 0x00, + KEY = 0x01, + REL = 0x02, + ABS = 0x03, + MSC = 0x04, + SW = 0x05, + LED = 0x11, + SND = 0x12, + REP = 0x14, + FF = 0x15, + PWR = 0x16, + FF_STATUS = 0x17, +} + +impl EventType { + fn code_count(&self) -> usize { + match self { + EventType::SYN => SYN_CNT, + EventType::KEY => KEY_CNT, + EventType::REL => REL_CNT, + EventType::ABS => ABS_CNT, + EventType::MSC => MSC_CNT, + EventType::SW => SW_CNT, + EventType::LED => LED_CNT, + EventType::SND => SND_CNT, + EventType::REP => REP_CNT, + _ => { + panic!("Event type {self:?} does not have a defined code count."); + } + } + } +} + +pub const EVENT_TYPES_WITH_BITMAPS: [EventType; 7] = [ + EventType::KEY, + EventType::REL, + EventType::ABS, + EventType::MSC, + EventType::SW, + EventType::LED, + EventType::SND, +]; + +const INPUT_PROP_CNT: usize = 32; + +/// The `ioctl_*!` macros create public functions by default, so this module makes them private. +mod ioctl { + use nix::{ioctl_read, ioctl_read_buf}; + + ioctl_read!(eviocgid, b'E', 0x02, super::DeviceId); + ioctl_read_buf!(eviocgname, b'E', 0x06, u8); + ioctl_read_buf!(eviocgprop, b'E', 0x09, u8); +} + +#[derive(Default)] +#[repr(C)] +pub struct DeviceId { + pub bus_type: u16, + pub vendor: u16, + pub product: u16, + pub version: u16, +} + +#[derive(Default)] +#[repr(C)] +pub struct AbsoluteAxisInfo { + pub value: i32, + pub minimum: i32, + pub maximum: i32, + pub fuzz: i32, + pub flat: i32, + pub resolution: i32, +} + +#[repr(C)] +pub struct InputEvent { + pub time: TimeVal, + pub type_: u16, + pub code: u16, + pub value: i32, +} + +impl InputEvent { + pub fn offset_time_by(&self, offset: TimeVal) -> InputEvent { + InputEvent { time: self.time - offset, ..*self } + } +} + +impl Default for InputEvent { + fn default() -> Self { + InputEvent { time: TimeVal::new(0, 0), type_: 0, code: 0, value: 0 } + } +} + +/// An object representing an input device using Linux's evdev protocol. +pub struct Device { + fd: OwnedFd, +} + +/// # Safety +/// +/// `ioctl` must be safe to call with the given file descriptor and a pointer to a buffer of +/// `initial_buf_size` `u8`s. +unsafe fn buf_from_ioctl( + ioctl: unsafe fn(c_int, &mut [u8]) -> nix::Result, + fd: &OwnedFd, + initial_buf_size: usize, +) -> Result, nix::errno::Errno> { + let mut buf = vec![0; initial_buf_size]; + // SAFETY: + // Here we're relying on the safety guarantees for `ioctl` made by the caller. + match unsafe { ioctl(fd.as_raw_fd(), buf.as_mut_slice()) } { + Ok(len) if len < 0 => { + panic!("ioctl returned invalid length {len}"); + } + Ok(len) => { + buf.truncate(len as usize); + Ok(buf) + } + Err(err) => Err(err), + } +} + +impl Device { + /// Opens a device from the evdev node at the given path. + pub fn open(path: &Path) -> io::Result { + Ok(Device { fd: OwnedFd::from(File::open(path)?) }) + } + + /// Returns the name of the device, as set by the relevant kernel driver. + pub fn name(&self) -> Result { + // There's no official maximum length for evdev device names. The Linux HID driver + // currently supports names of at most 151 bytes (128 from the device plus a suffix of up + // to 23 bytes). 256 seems to be the buffer size most commonly used in evdev bindings, so + // we use it here. + // + // SAFETY: + // We know that fd is a valid file descriptor as it comes from a File that we have open. + // + // The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created + // by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's + // str_to_user function will truncate the string to that length. + let mut buf = unsafe { buf_from_ioctl(ioctl::eviocgname, &self.fd, 256)? }; + assert!(!buf.is_empty(), "buf is too short for an empty null-terminated string"); + assert_eq!(buf.pop().unwrap(), 0, "buf is not a null-terminated string"); + Ok(String::from_utf8_lossy(buf.as_slice()).into_owned()) + } + + pub fn ids(&self) -> Result { + let mut ids = DeviceId::default(); + // SAFETY: + // We know that fd is a valid file descriptor as it comes from a File that we have open. + // + // We know that the pointer to ids is valid because we just allocated it. + unsafe { ioctl::eviocgid(self.fd.as_raw_fd(), &mut ids) }.map(|_| ids) + } + + pub fn properties_bitmap(&self) -> Result, nix::errno::Errno> { + let buf_size = (INPUT_PROP_CNT + 7) / 8; + // SAFETY: + // We know that fd is a valid file descriptor as it comes from a File that we have open. + // + // The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created + // by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's + // str_to_user function will truncate the string to that length. + unsafe { buf_from_ioctl(ioctl::eviocgprop, &self.fd, buf_size) } + } + + pub fn bitmap_for_event_type(&self, event_type: EventType) -> nix::Result> { + let buf_size = (event_type.code_count() + 7) / 8; + let mut buf = vec![0; buf_size]; + + // The EVIOCGBIT ioctl can't be bound using ioctl_read_buf! like the others, since it uses + // part of its ioctl code as an additional parameter, for the event type. Hence this unsafe + // block is a manual expansion of ioctl_read_buf!. + // + // SAFETY: + // We know that fd is a valid file descriptor as it comes from a File that we have open. + // + // We prevent the retrieved data from overflowing buf by passing in the size of buf to the + // ioctl, meaning that the kernel's str_to_user function will truncate the string to that + // length. We also panic if the ioctl returns a length longer than buf, hopefully before the + // overflow can do any damage. + match nix::errno::Errno::result(unsafe { + libc::ioctl( + self.fd.as_raw_fd(), + nix::request_code_read!(b'E', 0x20 + event_type as u16, buf.len()) + as nix::sys::ioctl::ioctl_num_type, + buf.as_mut_ptr(), + ) + }) { + Ok(len) if len < 0 => { + panic!("EVIOCGBIT returned invalid length {len} for event type {event_type:?}"); + } + Ok(len) => { + buf.truncate(len as usize); + Ok(buf) + } + Err(err) => Err(err), + } + } + + pub fn supported_axes_of_type(&self, event_type: EventType) -> nix::Result> { + let mut axes = Vec::new(); + for (i, byte_ref) in self.bitmap_for_event_type(event_type)?.iter().enumerate() { + let mut byte = *byte_ref; + for j in 0..8 { + if byte & 1 == 1 { + axes.push((i * 8 + j) as u16); + } + byte >>= 1; + } + } + Ok(axes) + } + + pub fn absolute_axis_info(&self, axis: u16) -> nix::Result { + let mut info = AbsoluteAxisInfo::default(); + // The EVIOCGABS ioctl can't be bound using ioctl_read! since it uses part of its ioctl code + // as an additional parameter, for the axis code. Hence this unsafe block is a manual + // expansion of ioctl_read!. + // + // SAFETY: + // We know that fd is a valid file descriptor as it comes from a File that we have open. + // + // We know that the pointer to info is valid because we just allocated it. + nix::errno::Errno::result(unsafe { + nix::libc::ioctl( + self.fd.as_raw_fd(), + nix::request_code_read!(b'E', 0x40 + axis, mem::size_of::()), + &mut info, + ) + }) + .map(|_| info) + } + + pub fn read_event(&self) -> nix::Result { + let mut event = InputEvent::default(); + + // SAFETY: + // We know that fd is a valid file descriptor as it comes from a File that we have open. + // + // We know that the pointer to event is valid because we just allocated it, and that the + // data structures match up because InputEvent is repr(C) and all its members are repr(C) + // or primitives that support all representations without niches. + nix::errno::Errno::result(unsafe { + libc::read( + self.fd.as_raw_fd(), + &mut event as *mut _ as *mut std::ffi::c_void, + mem::size_of::(), + ) + }) + .map(|_| event) + } +} diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..c30c00fcd2504314f0b697e1f463d137bf96c973 --- /dev/null +++ b/cmds/evemu-record/main.rs @@ -0,0 +1,213 @@ +/* + * Copyright 2023 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. + */ + +//! A Rust implementation of the evemu-record command from the [FreeDesktop evemu suite][evemu] of +//! tools. +//! +//! [evemu]: https://gitlab.freedesktop.org/libevdev/evemu + +use std::cmp; +use std::error::Error; +use std::fs; +use std::io; +use std::io::{BufRead, Write}; +use std::path::PathBuf; + +use clap::{Parser, ValueEnum}; +use nix::sys::time::TimeVal; + +mod evdev; + +/// Records evdev events from an input device in a format compatible with the FreeDesktop evemu +/// library. +#[derive(Parser, Debug)] +struct Args { + /// The path to the input device to record. If omitted, offers a list of devices to choose from. + device: Option, + /// The file to save the recording to. Defaults to standard output. + output_file: Option, + + /// The base time that timestamps should be relative to (Android-specific extension) + #[arg(long, value_enum, default_value_t = TimestampBase::FirstEvent)] + timestamp_base: TimestampBase, +} + +#[derive(Clone, Debug, ValueEnum)] +enum TimestampBase { + /// The first event received from the device. + FirstEvent, + + /// The time when the system booted. + Boot, +} + +fn get_choice(max: u32) -> u32 { + fn read_u32() -> Result { + io::stdin().lock().lines().next().unwrap().unwrap().parse::() + } + let mut choice = read_u32(); + while choice.is_err() || choice.clone().unwrap() > max { + eprint!("Enter a number between 0 and {max} inclusive: "); + choice = read_u32(); + } + choice.unwrap() +} + +fn pick_input_device() -> Result { + eprintln!("Available devices:"); + let mut entries = + fs::read_dir("/dev/input")?.filter_map(|entry| entry.ok()).collect::>(); + entries.sort_by_key(|entry| entry.path()); + let mut highest_number = 0; + for entry in entries { + let path = entry.path(); + let file_name = path.file_name().unwrap().to_str().unwrap(); + if path.is_dir() || !file_name.starts_with("event") { + continue; + } + let number = file_name.strip_prefix("event").unwrap().parse::(); + if number.is_err() { + continue; + } + let number = number.unwrap(); + match evdev::Device::open(path.as_path()) { + Ok(dev) => { + highest_number = cmp::max(highest_number, number); + eprintln!( + "{}:\t{}", + path.display(), + dev.name().unwrap_or("[could not read name]".to_string()), + ); + } + Err(_) => { + eprintln!("Couldn't open {}", path.display()); + } + } + } + eprint!("Select the device event number [0-{highest_number}]: "); + let choice = get_choice(highest_number); + Ok(PathBuf::from(format!("/dev/input/event{choice}"))) +} + +fn print_device_description( + device: &evdev::Device, + output: &mut impl Write, +) -> Result<(), Box> { + // TODO(b/302297266): report LED and SW states, then bump the version to EVEMU 1.3. + writeln!(output, "# EVEMU 1.2")?; + writeln!(output, "N: {}", device.name()?)?; + + let ids = device.ids()?; + writeln!( + output, + "I: {:04x} {:04x} {:04x} {:04x}", + ids.bus_type, ids.vendor, ids.product, ids.version, + )?; + + fn print_in_8_byte_chunks( + output: &mut impl Write, + prefix: &str, + data: &Vec, + ) -> Result<(), io::Error> { + for (i, byte) in data.iter().enumerate() { + if i % 8 == 0 { + write!(output, "{prefix}")?; + } + write!(output, " {:02x}", byte)?; + if (i + 1) % 8 == 0 { + writeln!(output)?; + } + } + if data.len() % 8 != 0 { + for _ in (data.len() % 8)..8 { + write!(output, " 00")?; + } + writeln!(output)?; + } + Ok(()) + } + + let props = device.properties_bitmap()?; + print_in_8_byte_chunks(output, "P:", &props)?; + + // The SYN event type can't be queried through the EVIOCGBIT ioctl, so just hard-code it to + // SYN_REPORT, SYN_CONFIG, and SYN_DROPPED. + writeln!(output, "B: 00 0b 00 00 00 00 00 00 00")?; + for event_type in evdev::EVENT_TYPES_WITH_BITMAPS { + let bits = device.bitmap_for_event_type(event_type)?; + print_in_8_byte_chunks(output, format!("B: {:02x}", event_type as u16).as_str(), &bits)?; + } + + for axis in device.supported_axes_of_type(evdev::EventType::ABS)? { + let info = device.absolute_axis_info(axis)?; + writeln!( + output, + "A: {axis:02x} {} {} {} {} {}", + info.minimum, info.maximum, info.fuzz, info.flat, info.resolution + )?; + } + Ok(()) +} + +fn print_events( + device: &evdev::Device, + output: &mut impl Write, + timestamp_base: TimestampBase, +) -> Result<(), Box> { + fn print_event(output: &mut impl Write, event: &evdev::InputEvent) -> Result<(), io::Error> { + // TODO(b/302297266): Translate events into human-readable names and add those as comments. + writeln!( + output, + "E: {}.{:06} {:04x} {:04x} {:04}", + event.time.tv_sec(), + event.time.tv_usec(), + event.type_, + event.code, + event.value, + )?; + Ok(()) + } + let event = device.read_event()?; + let start_time = match timestamp_base { + // Due to a bug in the C implementation of evemu-play [0] that has since become part of the + // API, the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds, + // so offset it by 1µs. + // + // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d + TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1), + TimestampBase::Boot => TimeVal::new(0, 0), + }; + print_event(output, &event.offset_time_by(start_time))?; + loop { + let event = device.read_event()?; + print_event(output, &event.offset_time_by(start_time))?; + } +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + + let device_path = args.device.unwrap_or_else(|| pick_input_device().unwrap()); + + let device = evdev::Device::open(device_path.as_path())?; + let mut output = match args.output_file { + Some(path) => Box::new(fs::File::create(path)?) as Box, + None => Box::new(io::stdout().lock()), + }; + print_device_description(&device, &mut output)?; + print_events(&device, &mut output, args.timestamp_base)?; + Ok(()) +} diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index ac101ecb293b3ae7ac3b7154492ede2750f15aa7..334bae44e3ffa126f49585a4ef1f7a2dcd5b07e8 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -34,7 +34,6 @@ cc_defaults { "unique_file.cpp", "utils.cpp", "utils_default.cpp", - "view_compiler.cpp", ":installd_aidl", ], shared_libs: [ @@ -254,7 +253,6 @@ cc_binary { "unique_file.cpp", "utils.cpp", "utils_default.cpp", - "view_compiler.cpp", ], static_libs: [ diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp index b17cba15d5e39dc7fe72725de90c95a8686966c1..fd1df35aee73f692a7d8e09630cf01b7b0864639 100644 --- a/cmds/installd/CrateManager.cpp +++ b/cmds/installd/CrateManager.cpp @@ -29,9 +29,10 @@ #include #include +#include #include +#include #include -#include #include "utils.h" diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h index 1f30b5dc7948c1fa49af678c6adb07ac4e9da711..d9b590f7842b6afa4859f172bd5e64fb04563fe7 100644 --- a/cmds/installd/CrateManager.h +++ b/cmds/installd/CrateManager.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index bb6639e1a8e77d9655e713908fe8de3d3bf3a4ba..c8ab8c07e656bdeb1a4d67edeeef881b13ada7c0 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include @@ -51,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -67,7 +70,6 @@ #include "installd_deps.h" #include "otapreopt_utils.h" #include "utils.h" -#include "view_compiler.h" #include "CacheTracker.h" #include "CrateManager.h" @@ -84,6 +86,8 @@ using android::base::ParseUint; using android::base::Split; using android::base::StringPrintf; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; using std::endl; namespace android { @@ -229,6 +233,14 @@ binder::Status checkArgumentFileName(const std::string& path) { return ok(); } +binder::Status checkUidInAppRange(int32_t appUid) { + if (FIRST_APPLICATION_UID <= appUid && appUid <= LAST_APPLICATION_UID) { + return ok(); + } + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("UID %d is outside of the range", appUid)); +} + #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ @@ -236,6 +248,22 @@ binder::Status checkArgumentFileName(const std::string& path) { } \ } +// we could have tighter checks, but this is only to avoid hard errors. Negative values are defined +// in UserHandle.java and carry specific meanings that may not be handled by certain APIs here. +#define ENFORCE_VALID_USER(userId) \ + { \ + if (static_cast(userId) >= std::numeric_limits::max() / AID_USER_OFFSET) { \ + return error("userId invalid: " + std::to_string(userId)); \ + } \ + } + +#define ENFORCE_VALID_USER_OR_NULL(userId) \ + { \ + if (static_cast(userId) != USER_NULL) { \ + ENFORCE_VALID_USER(userId); \ + } \ + } + #define CHECK_ARGUMENT_UUID(uuid) { \ binder::Status status = checkArgumentUuid((uuid)); \ if (!status.isOk()) { \ @@ -273,6 +301,14 @@ binder::Status checkArgumentFileName(const std::string& path) { } \ } +#define CHECK_ARGUMENT_UID_IN_APP_RANGE(uid) \ + { \ + binder::Status status = checkUidInAppRange((uid)); \ + if (!status.isOk()) { \ + return status; \ + } \ + } + #ifdef GRANULAR_LOCKS /** @@ -373,6 +409,33 @@ using PackageLockGuard = std::lock_guard; } // namespace +binder::Status InstalldNativeService::FsveritySetupAuthToken::authenticate( + const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId) { + int open_flags = fcntl(authFd.get(), F_GETFL); + if (open_flags < 0) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "fcntl failed"); + } + if ((open_flags & O_ACCMODE) != O_WRONLY && (open_flags & O_ACCMODE) != O_RDWR) { + return exception(binder::Status::EX_SECURITY, "Received FD with unexpected open flag"); + } + if (fstat(authFd.get(), &this->mStatFromAuthFd) < 0) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "fstat failed"); + } + if (!S_ISREG(this->mStatFromAuthFd.st_mode)) { + return exception(binder::Status::EX_SECURITY, "Not a regular file"); + } + // Don't accept a file owned by a different app. + uid_t uid = multiuser_get_uid(userId, appUid); + if (this->mStatFromAuthFd.st_uid != uid) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by appUid"); + } + return ok(); +} + +bool InstalldNativeService::FsveritySetupAuthToken::isSameStat(const struct stat& st) const { + return memcmp(&st, &mStatFromAuthFd, sizeof(st)) == 0; +} + status_t InstalldNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService::publish(); @@ -409,6 +472,49 @@ status_t InstalldNativeService::dump(int fd, const Vector& /* args */) return NO_ERROR; } +constexpr const char kXattrRestoreconInProgress[] = "user.restorecon_in_progress"; + +static std::string lgetfilecon(const std::string& path) { + char* context; + if (::lgetfilecon(path.c_str(), &context) < 0) { + PLOG(ERROR) << "Failed to lgetfilecon for " << path; + return {}; + } + std::string result{context}; + free(context); + return result; +} + +static bool getRestoreconInProgress(const std::string& path) { + bool inProgress = false; + if (getxattr(path.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress)) != + sizeof(inProgress)) { + if (errno != ENODATA) { + PLOG(ERROR) << "Failed to check in-progress restorecon for " << path; + } + return false; + } + return inProgress; +} + +struct RestoreconInProgress { + explicit RestoreconInProgress(const std::string& path) : mPath(path) { + bool inProgress = true; + if (setxattr(mPath.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress), + 0) != 0) { + PLOG(ERROR) << "Failed to set in-progress restorecon for " << path; + } + } + ~RestoreconInProgress() { + if (removexattr(mPath.c_str(), kXattrRestoreconInProgress) < 0) { + PLOG(ERROR) << "Failed to clear in-progress restorecon for " << mPath; + } + } + +private: + const std::string& mPath; +}; + /** * Perform restorecon of the given path, but only perform recursive restorecon * if the label of that top-level file actually changed. This can save us @@ -416,54 +522,71 @@ status_t InstalldNativeService::dump(int fd, const Vector& /* args */) */ static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid, bool existing) { - int res = 0; - char* before = nullptr; - char* after = nullptr; + ScopedTrace tracer("restorecon-lazy"); if (!existing) { + ScopedTrace tracer("new-path"); if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { PLOG(ERROR) << "Failed recursive restorecon for " << path; - goto fail; + return -1; } - return res; + return 0; } - // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by - // libselinux. Not needed here. - if (lgetfilecon(path.c_str(), &before) < 0) { - PLOG(ERROR) << "Failed before getfilecon for " << path; - goto fail; - } - if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) { - PLOG(ERROR) << "Failed top-level restorecon for " << path; - goto fail; - } - if (lgetfilecon(path.c_str(), &after) < 0) { - PLOG(ERROR) << "Failed after getfilecon for " << path; - goto fail; + // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. + + // Check to see if there was an interrupted operation. + bool inProgress = getRestoreconInProgress(path); + std::string before, after; + if (!inProgress) { + if (before = lgetfilecon(path); before.empty()) { + PLOG(ERROR) << "Failed before getfilecon for " << path; + return -1; + } + if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) { + PLOG(ERROR) << "Failed top-level restorecon for " << path; + return -1; + } + if (after = lgetfilecon(path); after.empty()) { + PLOG(ERROR) << "Failed after getfilecon for " << path; + return -1; + } } // If the initial top-level restorecon above changed the label, then go // back and restorecon everything recursively - if (strcmp(before, after)) { + if (inProgress || before != after) { if (existing) { LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " - << path << "; running recursive restorecon"; + << path << "; running recursive restorecon"; } - if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, - SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { - PLOG(ERROR) << "Failed recursive restorecon for " << path; - goto fail; + + auto restorecon = [path, seInfo, uid]() { + ScopedTrace tracer("label-change"); + + // Temporary mark the folder as "in-progress" to resume in case of reboot/other failure. + RestoreconInProgress fence(path); + + if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, + SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { + PLOG(ERROR) << "Failed recursive restorecon for " << path; + return -1; + } + return 0; + }; + if (inProgress) { + // The previous restorecon was interrupted. It's either crashed (unlikely), or the phone + // was rebooted. Possibly because it took too much time. This time let's move it to a + // separate thread - so it won't block the rest of the OS. + std::thread(restorecon).detach(); + } else { + if (int result = restorecon(); result) { + return result; + } } } - goto done; -fail: - res = -1; -done: - free(before); - free(after); - return res; + return 0; } static bool internal_storage_has_project_id() { // The following path is populated in setFirstBoot, so if this file is present @@ -480,11 +603,15 @@ static bool internal_storage_has_project_id() { static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid, long project_id) { - if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { - PLOG(ERROR) << "Failed to prepare " << path; - return -1; + { + ScopedTrace tracer("prepare-dir"); + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << path; + return -1; + } } if (internal_storage_has_project_id()) { + ScopedTrace tracer("set-quota"); return set_quota_project_id(path, project_id, true); } return 0; @@ -493,14 +620,20 @@ static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t ui static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid, long project_id) { auto path = StringPrintf("%s/%s", parent.c_str(), name); - int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + int ret; + { + ScopedTrace tracer("prepare-cache-dir"); + ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + } if (ret == 0 && internal_storage_has_project_id()) { + ScopedTrace tracer("set-quota-cache-dir"); return set_quota_project_id(path, project_id, true); } return ret; } static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { + ScopedTrace tracer("prepare-app-profile"); int32_t uid = multiuser_get_uid(userId, appId); int shared_app_gid = multiuser_get_shared_gid(userId, appId); if (shared_app_gid == -1) { @@ -633,6 +766,7 @@ static binder::Status createAppDataDirs(const std::string& path, int32_t uid, in int32_t previousUid, int32_t cacheGid, const std::string& seInfo, mode_t targetMode, long projectIdApp, long projectIdCache) { + ScopedTrace tracer("create-dirs"); struct stat st{}; bool parent_dir_exists = (stat(path.c_str(), &st) == 0); @@ -680,8 +814,9 @@ static binder::Status createAppDataDirs(const std::string& path, int32_t uid, in binder::Status InstalldNativeService::createAppDataLocked( const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, - int32_t targetSdkVersion, int64_t* _aidl_return) { + int32_t targetSdkVersion, int64_t* ceDataInode, int64_t* deDataInode) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -689,7 +824,8 @@ binder::Status InstalldNativeService::createAppDataLocked( const char* pkgname = packageName.c_str(); // Assume invalid inode unless filled in below - if (_aidl_return != nullptr) *_aidl_return = -1; + if (ceDataInode != nullptr) *ceDataInode = -1; + if (deDataInode != nullptr) *deDataInode = -1; int32_t uid = multiuser_get_uid(userId, appId); @@ -709,6 +845,7 @@ binder::Status InstalldNativeService::createAppDataLocked( long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START); if (flags & FLAG_STORAGE_CE) { + ScopedTrace tracer("ce"); auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, @@ -726,15 +863,16 @@ binder::Status InstalldNativeService::createAppDataLocked( // And return the CE inode of the top-level data directory so we can // clear contents while CE storage is locked - if (_aidl_return != nullptr) { + if (ceDataInode != nullptr) { ino_t result; if (get_path_inode(path, &result) != 0) { return error("Failed to get_path_inode for " + path); } - *_aidl_return = static_cast(result); + *ceDataInode = static_cast(result); } } if (flags & FLAG_STORAGE_DE) { + ScopedTrace tracer("de"); auto path = create_data_user_de_package_path(uuid_, userId, pkgname); auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, @@ -749,16 +887,25 @@ binder::Status InstalldNativeService::createAppDataLocked( if (!prepare_app_profile_dir(packageName, appId, userId)) { return error("Failed to prepare profiles for " + packageName); } + + if (deDataInode != nullptr) { + ino_t result; + if (get_path_inode(path, &result) != 0) { + return error("Failed to get_path_inode for " + path); + } + *deDataInode = static_cast(result); + } } if (flags & FLAG_STORAGE_SDK) { + ScopedTrace tracer("sdk"); // Safe to ignore status since we can retry creating this by calling reconcileSdkData auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags); if (!ignore.isOk()) { PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName; } - } else { + ScopedTrace tracer("destroy-sdk"); // Package does not need sdk storage. Remove it. destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); } @@ -773,6 +920,8 @@ binder::Status InstalldNativeService::createAppDataLocked( binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t appId, int32_t flags) { + ENFORCE_VALID_USER(userId); + int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); if (sdkSandboxUid == -1) { // There no valid sdk sandbox process for this app. Skip creation of data directory @@ -809,25 +958,30 @@ binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( binder::Status InstalldNativeService::createAppData( const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, - int32_t targetSdkVersion, int64_t* _aidl_return) { + int32_t targetSdkVersion, int64_t* ceDataInode, int64_t* deDataInode) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo, - targetSdkVersion, _aidl_return); + targetSdkVersion, ceDataInode, deDataInode); } binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(args.userId); // Locking is performed depeer in the callstack. int64_t ceDataInode = -1; + int64_t deDataInode = -1; auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, - args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode); + args.previousAppId, args.seInfo, args.targetSdkVersion, + &ceDataInode, &deDataInode); _aidl_return->ceDataInode = ceDataInode; + _aidl_return->deDataInode = deDataInode; _aidl_return->exceptionCode = status.exceptionCode(); _aidl_return->exceptionMessage = status.exceptionMessage(); return ok(); @@ -837,6 +991,10 @@ binder::Status InstalldNativeService::createAppDataBatched( const std::vector& args, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + for (const auto& arg : args) { + ENFORCE_VALID_USER(arg.userId); + } + // Locking is performed depeer in the callstack. std::vector results; @@ -851,6 +1009,7 @@ binder::Status InstalldNativeService::createAppDataBatched( binder::Status InstalldNativeService::reconcileSdkData( const android::os::ReconcileSdkDataArgs& args) { + ENFORCE_VALID_USER(args.userId); // Locking is performed depeer in the callstack. return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId, @@ -874,6 +1033,7 @@ binder::Status InstalldNativeService::reconcileSdkData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1024,6 +1185,7 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag binder::Status InstalldNativeService::clearAppData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1115,6 +1277,7 @@ binder::Status InstalldNativeService::clearAppData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags) { + ENFORCE_VALID_USER(userId); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -1201,6 +1364,7 @@ binder::Status InstalldNativeService::deleteReferenceProfile(const std::string& binder::Status InstalldNativeService::destroyAppData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1271,6 +1435,8 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags) { + ENFORCE_VALID_USER(userId); + const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -1418,6 +1584,7 @@ binder::Status InstalldNativeService::snapshotAppData(const std::optional& volumeUuid, const int32_t userId, const std::vector& retainSnapshotIds) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); LOCK_USER(); @@ -1738,7 +1908,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional& uuid, int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); LOCK_USER(); + ScopedTrace tracer("create-user-data"); + const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_DE) { if (uuid_ == nullptr) { @@ -1865,6 +2039,7 @@ binder::Status InstalldNativeService::createUserData(const std::optional& uuid, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); LOCK_USER(); @@ -2355,11 +2530,15 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st p->fts_number = p->fts_parent->fts_number; switch (p->fts_info) { case FTS_D: - if (p->fts_level == 4 + if (p->fts_level == 3 + && !strcmp(p->fts_parent->fts_name, "obb") + && !strcmp(p->fts_parent->fts_parent->fts_name, "Android")) { + p->fts_number = 1; + } else if (p->fts_level == 4 && !strcmp(p->fts_name, "cache") && !strcmp(p->fts_parent->fts_parent->fts_name, "data") && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) { - p->fts_number = 1; + p->fts_number = 2; } [[fallthrough]]; // to count the directory case FTS_DEFAULT: @@ -2368,9 +2547,13 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st case FTS_SLNONE: int64_t size = (p->fts_statp->st_blocks * 512); if (p->fts_number == 1) { - stats->cacheSize += size; + stats->codeSize += size; + } else { + if (p->fts_number == 2) { + stats->cacheSize += size; + } + stats->dataSize += size; } - stats->dataSize += size; break; } } @@ -2644,6 +2827,7 @@ binder::Status InstalldNativeService::getUserSize(const std::optional& appIds, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. @@ -2716,11 +2900,6 @@ binder::Status InstalldNativeService::getUserSize(const std::optional& appIds, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. @@ -2903,6 +3084,7 @@ binder::Status InstalldNativeService::getAppCrates( const std::vector& packageNames, int32_t userId, std::optional>>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); for (const auto& packageName : packageNames) { CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -2952,6 +3134,7 @@ binder::Status InstalldNativeService::getUserCrates( const std::optional& uuid, int32_t userId, std::optional>>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); #ifdef ENABLE_STORAGE_CRATES LOCK_USER(); @@ -2995,6 +3178,7 @@ binder::Status InstalldNativeService::getUserCrates( binder::Status InstalldNativeService::setAppQuota(const std::optional& uuid, int32_t userId, int32_t appId, int64_t cacheQuota) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); std::lock_guard lock(mQuotasLock); @@ -3131,17 +3315,6 @@ binder::Status InstalldNativeService::controlDexOptBlocking(bool block) { return ok(); } -binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath, - const std::string& packageName, - const std ::string& outDexFile, int uid, - bool* _aidl_return) { - const char* apk_path = apkPath.c_str(); - const char* package_name = packageName.c_str(); - const char* out_dex_file = outDexFile.c_str(); - *_aidl_return = android::installd::view_compiler(apk_path, package_name, out_dex_file, uid); - return *_aidl_return ? ok() : error("viewcompiler failed"); -} - binder::Status InstalldNativeService::linkNativeLibraryDirectory( const std::optional& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId) { @@ -3168,7 +3341,7 @@ binder::Status InstalldNativeService::linkNativeLibraryDirectory( } char *con = nullptr; - if (lgetfilecon(pkgdir, &con) < 0) { + if (::lgetfilecon(pkgdir, &con) < 0) { return error("Failed to lgetfilecon " + _pkgdir); } @@ -3238,6 +3411,7 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -3279,6 +3454,7 @@ binder::Status InstalldNativeService::restoreconSdkDataLocked( const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -3555,22 +3731,22 @@ binder::Status InstalldNativeService::tryMountDataMirror( std::lock_guard lock(mMountsLock); std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); - if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE data mirror"); } std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_)); - if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create DE data mirror"); } std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_)); - if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE misc mirror"); } std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_)); - if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create DE misc mirror"); } @@ -3730,6 +3906,7 @@ binder::Status InstalldNativeService::prepareAppProfile(const std::string& packa int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath, const std::optional& dexMetadata, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER_OR_NULL(userId); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); LOCK_PACKAGE_USER(); @@ -3752,6 +3929,7 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { binder::Status InstalldNativeService::cleanupInvalidPackageDirs( const std::optional& uuid, int32_t userId, int32_t flags) { + ENFORCE_VALID_USER(userId); const char* uuid_cstr = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_CE) { @@ -3791,5 +3969,84 @@ binder::Status InstalldNativeService::getOdexVisibility( return *_aidl_return == -1 ? error() : ok(); } +// Creates an auth token to be used in enableFsverity. This token is really to store a proof that +// the caller can write to a file, represented by the authFd. Effectively, system_server as the +// attacker-in-the-middle cannot enable fs-verity on arbitrary app files. If the FD is not writable, +// return null. +// +// appUid and userId are passed for additional ownership check, such that one app can not be +// authenticated for another app's file. These parameters are assumed trusted for this purpose of +// consistency check. +// +// Notably, creating the token allows us to manage the writable FD easily during enableFsverity. +// Since enabling fs-verity to a file requires no outstanding writable FD, passing the authFd to the +// server allows the server to hold the only reference (as long as the client app doesn't). +binder::Status InstalldNativeService::createFsveritySetupAuthToken( + const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId, + sp* _aidl_return) { + CHECK_ARGUMENT_UID_IN_APP_RANGE(appUid); + ENFORCE_VALID_USER(userId); + + auto token = sp::make(); + binder::Status status = token->authenticate(authFd, appUid, userId); + if (!status.isOk()) { + return status; + } + *_aidl_return = token; + return ok(); +} + +// Enables fs-verity for filePath, which must be an absolute path and the same inode as in the auth +// token previously returned from createFsveritySetupAuthToken, and owned by the app uid. As +// installd is more privileged than its client / system server, we attempt to limit what a +// (compromised) client can do. +// +// The reason for this app request to go through installd is to avoid exposing a risky area (PKCS#7 +// signature verification) in the kernel to the app as an attack surface (it can't be system server +// because it can't override DAC and manipulate app files). Note that we should be able to drop +// these hops and simply the app calls the ioctl, once all upgrading devices run with a kernel +// without fs-verity built-in signature (https://r.android.com/2650402). +binder::Status InstalldNativeService::enableFsverity(const sp& authToken, + const std::string& filePath, + const std::string& packageName, + int32_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(filePath); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE(); + if (authToken == nullptr) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Received a null auth token"); + } + + // Authenticate to check the targeting file is the same inode as the authFd. + sp authTokenBinder = IInterface::asBinder(authToken)->localBinder(); + if (authTokenBinder == nullptr) { + return exception(binder::Status::EX_SECURITY, "Received a non-local auth token"); + } + auto authTokenInstance = sp::cast(authTokenBinder); + unique_fd rfd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + struct stat stFromPath; + if (fstat(rfd.get(), &stFromPath) < 0) { + *_aidl_return = errno; + return ok(); + } + if (!authTokenInstance->isSameStat(stFromPath)) { + LOG(DEBUG) << "FD authentication failed"; + *_aidl_return = EPERM; + return ok(); + } + + fsverity_enable_arg arg = {}; + arg.version = 1; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.block_size = 4096; + if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) { + *_aidl_return = errno; + } else { + *_aidl_return = 0; + } + return ok(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 521afc3f97a9bc8a63ceab8caadd06cd4864d833..1b56144061546227eed06706dcf1de4bccf66348 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -19,6 +19,7 @@ #define COMMANDS_H_ #include +#include #include #include @@ -35,8 +36,26 @@ namespace android { namespace installd { +using IFsveritySetupAuthToken = android::os::IInstalld::IFsveritySetupAuthToken; + class InstalldNativeService : public BinderService, public os::BnInstalld { public: + class FsveritySetupAuthToken : public os::IInstalld::BnFsveritySetupAuthToken { + public: + FsveritySetupAuthToken() : mStatFromAuthFd() {} + + binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t appUid, + int32_t userId); + bool isSameStat(const struct stat& st) const; + + private: + // Not copyable or movable + FsveritySetupAuthToken(const FsveritySetupAuthToken&) = delete; + FsveritySetupAuthToken& operator=(const FsveritySetupAuthToken&) = delete; + + struct stat mStatFromAuthFd; + }; + static status_t start(); static char const* getServiceName() { return "installd"; } virtual status_t dump(int fd, const Vector &args) override; @@ -49,7 +68,8 @@ public: binder::Status createAppData(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, - int32_t targetSdkVersion, int64_t* _aidl_return); + int32_t targetSdkVersion, int64_t* ceDataInode, + int64_t* deDataInode); binder::Status createAppData( const android::os::CreateAppDataArgs& args, @@ -126,9 +146,6 @@ public: binder::Status controlDexOptBlocking(bool block); - binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName, - const std::string& outDexFile, int uid, bool* _aidl_return); - binder::Status rmdex(const std::string& codePath, const std::string& instructionSet); binder::Status mergeProfiles(int32_t uid, const std::string& packageName, @@ -192,6 +209,13 @@ public: const std::optional& outputPath, int32_t* _aidl_return); + binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd, + int32_t appUid, int32_t userId, + android::sp* _aidl_return); + binder::Status enableFsverity(const android::sp& authToken, + const std::string& filePath, const std::string& packageName, + int32_t* _aidl_return); + private: std::recursive_mutex mLock; std::unordered_map> mUserIdLock; @@ -212,7 +236,7 @@ private: const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, - int64_t* _aidl_return); + int64_t* ceDataInode, int64_t* deDataInode); binder::Status restoreconAppDataLocked(const std::optional& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); diff --git a/cmds/installd/SysTrace.h b/cmds/installd/SysTrace.h index 18506a9258fd52acc55f034227b0f5ca66d1396c..0deaeb4341c3b63b1f4daf920d3bc17afd33260d 100644 --- a/cmds/installd/SysTrace.h +++ b/cmds/installd/SysTrace.h @@ -19,4 +19,16 @@ namespace android::installd { void atrace_pm_begin(const char*); void atrace_pm_end(); + +class ScopedTrace { +public: + explicit ScopedTrace(const char* label) { atrace_pm_begin(label); } + ~ScopedTrace() { atrace_pm_end(); } + +private: + ScopedTrace(const ScopedTrace&) = delete; + ScopedTrace& operator=(const ScopedTrace&) = delete; + ScopedTrace(ScopedTrace&&) = delete; + ScopedTrace& operator=(ScopedTrace&&) = delete; +}; } /* namespace android::installd */ diff --git a/cmds/installd/binder/android/os/CreateAppDataResult.aidl b/cmds/installd/binder/android/os/CreateAppDataResult.aidl index 3b8fa6b9f2df3446447851e6a7ceb288346d986e..463489e01e950bd080e6c7bf66d7386453d3b682 100644 --- a/cmds/installd/binder/android/os/CreateAppDataResult.aidl +++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl @@ -19,6 +19,7 @@ package android.os; /** {@hide} */ parcelable CreateAppDataResult { long ceDataInode; + long deDataInode; int exceptionCode; @utf8InCpp String exceptionMessage; } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 9ad853b1df8204bac1ab3c8a253c6281ab5b3264..f5a770977cefae323c5eac964b1b7dc0424cdd1d 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -70,8 +70,6 @@ interface IInstalld { // Blocks (when block is true) or unblock (when block is false) dexopt. // Blocking also invloves cancelling the currently running dexopt. void controlDexOptBlocking(boolean block); - boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName, - @utf8InCpp String outDexFile, int uid); void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet); @@ -134,6 +132,22 @@ interface IInstalld { int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath, @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + interface IFsveritySetupAuthToken { + // Using an interface here is an easy way to create and maintain an IBinder object across + // the processes. When installd creates this binder object, it stores the file stat + // privately for later authentication, and only returns the reference to the caller process. + // Once the binder object has no reference count, it gets destructed automatically + // (alternatively, installd can maintain an internal mapping, but it is more error prone + // because the app may crash and not finish the fs-verity setup, keeping the memory unused + // forever). + // + // We don't necessarily need a method here, so it's left blank intentionally. + } + IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int appUid, + int userId); + int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath, + @utf8InCpp String packageName); + const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 5cf402c54c177cb7d331ef484440f12609f32f71..df02588a228cd431883a0a12e86bbe9220291f4e 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -18,6 +18,7 @@ #define DEXOPT_H_ #include "installd_constants.h" +#include "unique_file.h" #include @@ -156,6 +157,10 @@ const char* select_execution_binary( // artifacts. int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir); +UniqueFile maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path, + const char* profile_name, bool profile_guided, + bool is_public, int uid, bool is_secondary_dex); + } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 7cabdb09e1c95b4fa68519f463734b5c6c2cb2bc..8eb74583fe61833bf4bf63ad772f0c383c3386bc 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -14,20 +14,21 @@ ** limitations under the License. */ -#include #include -#include -#include -#include #include #include #include #include #include +#include #include #include -#include #include +#include +#include +#include +#include +#include #include #include @@ -47,6 +48,7 @@ #include "otapreopt_parameters.h" #include "otapreopt_utils.h" #include "system_properties.h" +#include "unique_file.h" #include "utils.h" #ifndef LOG_TAG @@ -87,6 +89,9 @@ static_assert(DEXOPT_GENERATE_APP_IMAGE == 1 << 12, "DEXOPT_GENERATE_APP_IMAGE u static_assert(DEXOPT_MASK == (0x3dfe | DEXOPT_IDLE_BACKGROUND_JOB), "DEXOPT_MASK unexpected."); +constexpr const char* kAotCompilerFilters[]{ + "space-profile", "space", "speed-profile", "speed", "everything-profile", "everything", +}; template static constexpr bool IsPowerOfTwo(T x) { @@ -415,6 +420,36 @@ private: return (strcmp(arg, "!") == 0) ? nullptr : arg; } + bool IsAotCompilation() const { + if (std::find(std::begin(kAotCompilerFilters), std::end(kAotCompilerFilters), + std::string_view(parameters_.compiler_filter)) == + std::end(kAotCompilerFilters)) { + return false; + } + + int dexopt_flags = parameters_.dexopt_flags; + bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0; + bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0; + bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0; + + if (profile_guided) { + UniqueFile reference_profile = + maybe_open_reference_profile(parameters_.pkgName, parameters_.apk_path, + parameters_.profile_name, profile_guided, + is_public, parameters_.uid, is_secondary_dex); + // `maybe_open_reference_profile` installs a hook that clears the profile on + // destruction. Disable it. + reference_profile.DisableCleanup(); + struct stat sbuf; + if (reference_profile.fd() == -1 || + (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) { + return false; + } + } + + return true; + } + bool ShouldSkipPreopt() const { // There's one thing we have to be careful about: we may/will be asked to compile an app // living in the system image. This may be a valid request - if the app wasn't compiled, @@ -439,9 +474,12 @@ private: // (This is ugly as it's the only thing where we need to understand the contents // of parameters_, but it beats postponing the decision or using the call- // backs to do weird things.) + + // In addition, no need to preopt for "verify". The existing vdex files in the OTA package + // and the /data partition will still be usable after the OTA update is applied. const char* apk_path = parameters_.apk_path; CHECK(apk_path != nullptr); - if (StartsWith(apk_path, android_root_)) { + if (StartsWith(apk_path, android_root_) || !IsAotCompilation()) { const char* last_slash = strrchr(apk_path, '/'); if (last_slash != nullptr) { std::string path(apk_path, last_slash - apk_path + 1); @@ -471,13 +509,20 @@ private: // TODO(calin): embed the profile name in the parameters. int Dexopt() { std::string error; + + int dexopt_flags = parameters_.dexopt_flags; + // Make sure dex2oat is run with background priority. + dexopt_flags |= DEXOPT_BOOTCOMPLETE | DEXOPT_IDLE_BACKGROUND_JOB; + + parameters_.compilation_reason = "ab-ota"; + int res = dexopt(parameters_.apk_path, parameters_.uid, parameters_.pkgName, parameters_.instruction_set, parameters_.dexopt_needed, parameters_.oat_dir, - parameters_.dexopt_flags, + dexopt_flags, parameters_.compiler_filter, parameters_.volume_uuid, parameters_.shared_libraries, @@ -521,61 +566,6 @@ private: return Dexopt(); } - //////////////////////////////////// - // Helpers, mostly taken from ART // - //////////////////////////////////// - - // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. - static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { - constexpr size_t kPageSize = PAGE_SIZE; - static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two"); - CHECK_EQ(min_delta % kPageSize, 0u); - CHECK_EQ(max_delta % kPageSize, 0u); - CHECK_LT(min_delta, max_delta); - - std::default_random_engine generator; - generator.seed(GetSeed()); - std::uniform_int_distribution distribution(min_delta, max_delta); - int32_t r = distribution(generator); - if (r % 2 == 0) { - r = RoundUp(r, kPageSize); - } else { - r = RoundDown(r, kPageSize); - } - CHECK_LE(min_delta, r); - CHECK_GE(max_delta, r); - CHECK_EQ(r % kPageSize, 0u); - return r; - } - - static uint64_t GetSeed() { -#ifdef __BIONIC__ - // Bionic exposes arc4random, use it. - uint64_t random_data; - arc4random_buf(&random_data, sizeof(random_data)); - return random_data; -#else -#error "This is only supposed to run with bionic. Otherwise, implement..." -#endif - } - - void AddCompilerOptionFromSystemProperty(const char* system_property, - const char* prefix, - bool runtime, - std::vector& out) const { - const std::string* value = system_properties_.GetProperty(system_property); - if (value != nullptr) { - if (runtime) { - out.push_back("--runtime-arg"); - } - if (prefix != nullptr) { - out.push_back(StringPrintf("%s%s", prefix, value->c_str())); - } else { - out.push_back(*value); - } - } - } - static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index c40caf56d8a1ca022b20e852d29ce709948015ce..c86adef11887a01a0b0c524601d5bb56197ef976 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -353,7 +353,7 @@ static int otapreopt_chroot(const int argc, char **arg) { // Now go on and read dexopt lines from stdin and pass them on to otapreopt. int count = 1; - for (std::array linebuf; + for (std::array linebuf; std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) { // Subtract one from gcount() since getline() counts the newline. std::string line(&linebuf[0], std::cin.gcount() - 1); diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index 4221a3a5936b08a96c8818b7e43cedc184a8a745..7648265f0f8dff4fc4954e0008ed2e9a9abc4240 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -208,36 +208,13 @@ void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex, } // Compute compiler filter. - { - std::string dex2oat_compiler_filter_arg; - { - // If we are booting without the real /data, don't spend time compiling. - std::string vold_decrypt = GetProperty("vold.decrypt", ""); - bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || - vold_decrypt == "1"; - - bool have_dex2oat_relocation_skip_flag = false; - if (skip_compilation) { - dex2oat_compiler_filter_arg = "--compiler-filter=extract"; - have_dex2oat_relocation_skip_flag = true; - } else if (compiler_filter != nullptr) { - dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", - compiler_filter); - } - if (have_dex2oat_relocation_skip_flag) { - AddRuntimeArg("-Xnorelocate"); - } - } - - if (dex2oat_compiler_filter_arg.empty()) { - dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", - "--compiler-filter=%s"); - } - AddArg(dex2oat_compiler_filter_arg); - - if (compilation_reason != nullptr) { - AddArg(std::string("--compilation-reason=") + compilation_reason); - } + if (compiler_filter != nullptr) { + AddArg(StringPrintf("--compiler-filter=%s", compiler_filter)); + } else { + AddArg(MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s")); + } + if (compilation_reason != nullptr) { + AddArg(std::string("--compilation-reason=") + compilation_reason); } AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 304ba7b04ff3df69db1a813c4f04adf30dd5c8dd..56f84a5e120b2063740720f6d0c75ff796f9de4d 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -441,24 +441,6 @@ TEST_F(RunDex2OatTest, Runtime) { VerifyExpectedFlags(); } -TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) { - setSystemProperty("vold.decrypt", "trigger_restart_min_framework"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--compiler-filter", "=extract"); - SetExpectedFlagUsed("-Xnorelocate", ""); - VerifyExpectedFlags(); -} - -TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) { - setSystemProperty("vold.decrypt", "1"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--compiler-filter", "=extract"); - SetExpectedFlagUsed("-Xnorelocate", ""); - VerifyExpectedFlags(); -} - TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) { setSystemProperty("dalvik.vm.dex2oat-filter", "speed"); auto args = RunDex2OatArgs::MakeDefaultTestArgs(); diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 07f73b9029dcb269966bd9766c3cbbba79ff1818..61fe3162254ef0c340724a62ce29aa48305fbfb0 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -77,10 +77,8 @@ cc_test { }, } -cc_test { - name: "installd_service_test", - test_suites: ["device-tests"], - srcs: ["installd_service_test.cpp"], +cc_defaults { + name: "installd_service_test_defaults", cflags: [ "-Wall", "-Werror", @@ -106,8 +104,6 @@ cc_test { "liblogwrap", "libc++fs", ], - test_config: "installd_service_test.xml", - product_variables: { arc: { exclude_srcs: [ @@ -124,6 +120,14 @@ cc_test { }, } +cc_test { + name: "installd_service_test", + test_suites: ["device-tests"], + srcs: ["installd_service_test.cpp"], + defaults: ["installd_service_test_defaults"], + test_config: "installd_service_test.xml", +} + cc_test { name: "installd_dexopt_test", test_suites: ["device-tests"], @@ -209,3 +213,19 @@ cc_test { "liblog", ], } + +cc_fuzz { + name: "installd_service_fuzzer", + defaults: [ + "service_fuzzer_defaults", + "fuzzer_disable_leaks", + "installd_service_test_defaults", + ], + srcs: ["fuzzers/InstalldServiceFuzzer.cpp"], + fuzz_config: { + cc: [ + "android-package-manager-team@google.com", + ], + triage_assignee: "waghpawan@google.com", + }, +} diff --git a/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1c6940207db318eb832bd530712a2969528c3b7 --- /dev/null +++ b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 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 + +#include "InstalldNativeService.h" +#include "dexopt.h" + +using ::android::fuzzService; +using ::android::sp; +using ::android::installd::InstalldNativeService; + +namespace android { +namespace installd { + +bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char* oat_dir, const char* apk_path, + const char* instruction_set) { + return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set); +} + +bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char* apk_path, + const char* instruction_set) { + return calculate_odex_file_path_default(path, apk_path, instruction_set); +} + +bool create_cache_path(char path[PKG_PATH_MAX], const char* src, const char* instruction_set) { + return create_cache_path_default(path, src, instruction_set); +} + +bool force_compile_without_image() { + return false; +} + +} // namespace installd +} // namespace android + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + auto service = sp::make(); + fuzzService(service, FuzzedDataProvider(data, size)); + return 0; +} \ No newline at end of file diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index c4071c60208f5f7da2cab238605ea977e67edba2..ee91d80a3b8c56dc7bad38ae8dad3d7aa32b3d47 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -197,6 +197,7 @@ protected: std::string app_oat_dir_; int64_t ce_data_inode_; + int64_t de_data_inode_; std::string secondary_dex_ce_; std::string secondary_dex_ce_link_; @@ -261,16 +262,10 @@ protected: } // Create the app user data. - binder::Status status = service_->createAppData( - volume_uuid_, - package_name_, - kTestUserId, - kAppDataFlags, - kTestAppUid, - 0 /* previousAppId */, - se_info_, - kOSdkVersion, - &ce_data_inode_); + binder::Status status = + service_->createAppData(volume_uuid_, package_name_, kTestUserId, kAppDataFlags, + kTestAppUid, 0 /* previousAppId */, se_info_, kOSdkVersion, + &ce_data_inode_, &de_data_inode_); if (!status.isOk()) { return ::testing::AssertionFailure() << "Could not create app data: " << status.toString8().c_str(); @@ -1350,16 +1345,10 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700)); // Run createAppData again which will offer to fix-up the profile directories. - ASSERT_BINDER_SUCCESS(service_->createAppData( - volume_uuid_, - package_name_, - kTestUserId, - kAppDataFlags, - kTestAppUid, - 0 /* previousAppId */, - se_info_, - kOSdkVersion, - &ce_data_inode_)); + ASSERT_BINDER_SUCCESS(service_->createAppData(volume_uuid_, package_name_, kTestUserId, + kAppDataFlags, kTestAppUid, 0 /* previousAppId */, + se_info_, kOSdkVersion, &ce_data_inode_, + &de_data_inode_)); // Check the file access. CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR); @@ -1492,18 +1481,13 @@ class BootProfileTest : public ProfileTest { void createAppProfilesForBootMerge(size_t number_of_profiles) { for (size_t i = 0; i < number_of_profiles; i++) { int64_t ce_data_inode; + int64_t de_data_inode; std::string package_name = "dummy_test_pkg" + std::to_string(i); LOG(INFO) << package_name; - ASSERT_BINDER_SUCCESS(service_->createAppData( - volume_uuid_, - package_name, - kTestUserId, - kAppDataFlags, - kTestAppUid, - 0 /* previousAppId */, - se_info_, - kOSdkVersion, - &ce_data_inode)); + ASSERT_BINDER_SUCCESS( + service_->createAppData(volume_uuid_, package_name, kTestUserId, kAppDataFlags, + kTestAppUid, 0 /* previousAppId */, se_info_, + kOSdkVersion, &ce_data_inode, &de_data_inode)); extra_apps_.push_back(package_name); extra_ce_data_inodes_.push_back(ce_data_inode); std::string profile = create_current_profile_path( diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 858a92cc65d956015fb4966e0bbb21922ad0424f..4bc92afa18aa739b0580bb7a4bfaf846565f700e 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -42,9 +42,12 @@ #include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" +#include "unique_file.h" #include "utils.h" using android::base::StringPrintf; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; using std::filesystem::is_empty; namespace android { @@ -136,6 +139,16 @@ static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode return fd; } +static void create_with_content(const std::string& path, uid_t owner, gid_t group, mode_t mode, + const std::string& content) { + int fd = ::open(path.c_str(), O_RDWR | O_CREAT, mode); + EXPECT_NE(fd, -1); + EXPECT_TRUE(android::base::WriteStringToFd(content, fd)); + EXPECT_EQ(::fchown(fd, owner, group), 0); + EXPECT_EQ(::fchmod(fd, mode), 0); + close(fd); +} + static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) { EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0); } @@ -527,6 +540,94 @@ TEST_F(ServiceTest, GetAppSizeWrongSizes) { externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize)); } + +class FsverityTest : public ServiceTest { +protected: + binder::Status createFsveritySetupAuthToken(const std::string& path, int open_mode, + sp* _aidl_return) { + unique_fd ufd(open(path.c_str(), open_mode)); + EXPECT_GE(ufd.get(), 0) << "open failed: " << strerror(errno); + ParcelFileDescriptor rfd(std::move(ufd)); + return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, kTestUserId, + _aidl_return); + } +}; + +TEST_F(FsverityTest, enableFsverity) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect to fs-verity setup to succeed + sp authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity auth token works to enable fs-verity + int32_t errno_local; + status = service->enableFsverity(authToken, path, "fake.package.name", &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(errno_local, 0); +} + +TEST_F(FsverityTest, enableFsverity_nullAuthToken) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Verity null auth token fails + sp authToken; + int32_t errno_local; + binder::Status status = + service->enableFsverity(authToken, path, "fake.package.name", &errno_local); + EXPECT_FALSE(status.isOk()); +} + +TEST_F(FsverityTest, enableFsverity_differentFile) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect to fs-verity setup to succeed + sp authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity auth token does not work for a different file + const std::string anotherPath = kTestPath + "/bar"; + ASSERT_TRUE(android::base::WriteStringToFile("content", anotherPath)); + UniqueFile raii2(/*fd=*/-1, anotherPath, [](const std::string& path) { unlink(path.c_str()); }); + int32_t errno_local; + status = service->enableFsverity(authToken, anotherPath, "fake.package.name", &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_NE(errno_local, 0); +} + +TEST_F(FsverityTest, createFsveritySetupAuthToken_ReadonlyFdDoesNotAuthenticate) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect the fs-verity setup to fail + sp authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDONLY, &authToken); + EXPECT_FALSE(status.isOk()); +} + +TEST_F(FsverityTest, createFsveritySetupAuthToken_UnownedFile) { + const std::string path = kTestPath + "/foo"; + // Simulate world-writable file owned by another app + create_with_content(path, kTestAppUid + 1, kTestAppUid + 1, 0666, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect the fs-verity setup to fail + sp authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_FALSE(status.isOk()); +} + static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index ecea1d2b1c571beebc8ebf431d479527721d455b..c43fdbd54798086bda9129606c4f20859ec2237b 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -18,6 +18,7 @@ #ifndef UTILS_H_ #define UTILS_H_ +#include #include #include diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp deleted file mode 100644 index 8c000a11c92386d314453cdabd5652f1a6333766..0000000000000000000000000000000000000000 --- a/cmds/installd/view_compiler.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "view_compiler.h" - -#include - -#include -#include -#include -#include -#include - -#include "utils.h" - -#include "android-base/logging.h" -#include "android-base/stringprintf.h" -#include "android-base/unique_fd.h" - -namespace android { -namespace installd { - -namespace { - -using ::android::base::unique_fd; - -constexpr int kTimeoutMs = 300000; - -} // namespace - -bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, - int uid) { - CHECK(apk_path != nullptr); - CHECK(package_name != nullptr); - CHECK(out_dex_file != nullptr); - - // viewcompiler won't have permission to open anything, so we have to open the files first - // and pass file descriptors. - - // Open input file - unique_fd infd{open(apk_path, O_RDONLY)}; // NOLINT(android-cloexec-open) - if (infd.get() < 0) { - PLOG(ERROR) << "Could not open input file: " << apk_path; - return false; - } - - // Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so - // we close stdout and open it towards the right output. - unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644)}; - if (outfd.get() < 0) { - PLOG(ERROR) << "Could not open output file: " << out_dex_file; - return false; - } - if (fchmod(outfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) { - PLOG(ERROR) << "Could not change output file permissions"; - return false; - } - if (dup2(outfd, STDOUT_FILENO) < 0) { - PLOG(ERROR) << "Could not duplicate output file descriptor"; - return false; - } - - // Prepare command line arguments for viewcompiler - std::string args[] = {"/system/bin/viewcompiler", - "--apk", - "--infd", - android::base::StringPrintf("%d", infd.get()), - "--dex", - "--package", - package_name}; - char* const argv[] = {const_cast(args[0].c_str()), const_cast(args[1].c_str()), - const_cast(args[2].c_str()), const_cast(args[3].c_str()), - const_cast(args[4].c_str()), const_cast(args[5].c_str()), - const_cast(args[6].c_str()), nullptr}; - - pid_t pid = fork(); - if (pid == 0) { - // Now that we've opened the files we need, drop privileges. - drop_capabilities(uid); - execv("/system/bin/viewcompiler", argv); - _exit(1); - } - - int return_code = wait_child_with_timeout(pid, kTimeoutMs); - if (!WIFEXITED(return_code)) { - LOG(WARNING) << "viewcompiler failed for " << package_name << ":" << apk_path; - if (WTERMSIG(return_code) == SIGKILL) { - // If the subprocess is killed while it's writing to the file, the file is likely - // corrupted, so we should remove it. - remove_file_at_fd(outfd.get()); - } - return false; - } - return WEXITSTATUS(return_code) == 0; -} - -} // namespace installd -} // namespace android diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c deleted file mode 100644 index 71f08375f7aa7dc3cb792b7a2d4603216c900d62..0000000000000000000000000000000000000000 --- a/cmds/ip-up-vpn/ip-up-vpn.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "ip-up-vpn" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DIR "/data/misc/vpn/" - -static const char *env(const char *name) { - const char *value = getenv(name); - return value ? value : ""; -} - -static int set_address(struct sockaddr *sa, const char *address) { - sa->sa_family = AF_INET; - errno = EINVAL; - return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr); -} - -/* - * The primary goal is to create a file with VPN parameters. Currently they - * are interface, addresses, routes, DNS servers, and search domains and VPN - * server address. Each parameter occupies one line in the file, and it can be - * an empty string or space-separated values. The order and the format must be - * consistent with com.android.server.connectivity.Vpn. Here is an example. - * - * ppp0 - * 192.168.1.100/24 - * 0.0.0.0/0 - * 192.168.1.1 192.168.1.2 - * example.org - * 192.0.2.1 - * - * The secondary goal is to unify the outcome of VPN. The current baseline - * is to have an interface configured with the given address and netmask - * and maybe add a host route to protect the tunnel. PPP-based VPN already - * does this, but others might not. Routes, DNS servers, and search domains - * are handled by the framework since they can be overridden by the users. - */ -int main(int argc, char **argv) -{ - FILE *state = fopen(DIR ".tmp", "wb"); - if (!state) { - ALOGE("Cannot create state: %s", strerror(errno)); - return 1; - } - - if (argc >= 6) { - /* Invoked by pppd. */ - fprintf(state, "%s\n", argv[1]); - fprintf(state, "%s/32\n", argv[4]); - fprintf(state, "0.0.0.0/0\n"); - fprintf(state, "%s %s\n", env("DNS1"), env("DNS2")); - fprintf(state, "\n"); - fprintf(state, "\n"); - } else if (argc == 2) { - /* Invoked by racoon. */ - const char *interface = env("INTERFACE"); - const char *address = env("INTERNAL_ADDR4"); - const char *routes = env("SPLIT_INCLUDE_CIDR"); - - int s = socket(AF_INET, SOCK_DGRAM, 0); - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - - /* Bring up the interface. */ - ifr.ifr_flags = IFF_UP; - strncpy(ifr.ifr_name, interface, IFNAMSIZ); - if (ioctl(s, SIOCSIFFLAGS, &ifr)) { - ALOGE("Cannot bring up %s: %s", interface, strerror(errno)); - fclose(state); - return 1; - } - - /* Set the address. */ - if (!set_address(&ifr.ifr_addr, address) || - ioctl(s, SIOCSIFADDR, &ifr)) { - ALOGE("Cannot set address: %s", strerror(errno)); - fclose(state); - return 1; - } - - /* Set the netmask. */ - if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) { - if (ioctl(s, SIOCSIFNETMASK, &ifr)) { - ALOGE("Cannot set netmask: %s", strerror(errno)); - fclose(state); - return 1; - } - } - - /* TODO: Send few packets to trigger phase 2? */ - - fprintf(state, "%s\n", interface); - fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4")); - fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0"); - fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST")); - fprintf(state, "%s\n", env("DEFAULT_DOMAIN")); - fprintf(state, "%s\n", env("REMOTE_ADDR")); - } else { - ALOGE("Cannot parse parameters"); - fclose(state); - return 1; - } - - fclose(state); - if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) { - ALOGE("Cannot write state: %s", strerror(errno)); - return 1; - } - return 0; -} diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp index af85666276c2bb1a7778500507672aef7b9a8b73..d0e4b74543caa225b9e5a4927e5460f234871e0d 100644 --- a/cmds/lshal/libprocpartition/Android.bp +++ b/cmds/lshal/libprocpartition/Android.bp @@ -37,4 +37,8 @@ cc_library_static { "include", ], min_sdk_version: "30", + apex_available: [ + "//apex_available:platform", + "com.android.neuralnetworks", + ], } diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index fb69513d2470b623b293da25b04a0693a55fdfd8..e00c2a2b5a3048807159b1b25988bc8c41c182d1 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -24,7 +24,6 @@ cc_defaults { shared_libs: [ "libbase", - "libbinder", // also contains servicemanager_interface "libvintf", "libcutils", "liblog", @@ -33,6 +32,21 @@ cc_defaults { ], target: { + android: { + shared_libs: [ + "libbinder", + "libutils", + ], + }, + host: { + static_libs: [ + "libbinder", + "libutils", + ], + }, + darwin: { + enabled: false, + }, vendor: { exclude_shared_libs: ["libvintf"], }, @@ -93,22 +107,9 @@ cc_fuzz { libfuzzer_options: [ "max_len=50000", ], - }, -} - -// Adding this new fuzzer to test the corpus generated by record_binder -cc_fuzz { - name: "servicemanager_test_fuzzer", - defaults: [ - "servicemanager_defaults", - "service_fuzzer_defaults", - ], - host_supported: true, - srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"], - fuzz_config: { - libfuzzer_options: [ - "max_len=50000", + cc: [ + "smoreland@google.com", + "waghpawan@google.com", ], }, - corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"], } diff --git a/cmds/servicemanager/OWNERS b/cmds/servicemanager/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..7f5a8115d18d667ba40131028694317495c656ac --- /dev/null +++ b/cmds/servicemanager/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 32456 + +smoreland@google.com diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 63f382170ce97c96e3ba5b128816d45b1d7dac55..a4018385c794cdeb09f7daf6d2747f686ac25c99 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -117,10 +118,26 @@ static bool isVintfDeclared(const std::string& name) { }); if (!found) { + std::set instances; + forEachManifest([&](const ManifestWithDescription& mwd) { + std::set res = mwd.manifest->getAidlInstances(aname.package, aname.iface); + instances.insert(res.begin(), res.end()); + return true; + }); + + std::string available; + if (instances.empty()) { + available = "No alternative instances declared in VINTF"; + } else { + // for logging only. We can't return this information to the client + // because they may not have permissions to find or list those + // instances + available = "VINTF declared instances: " + base::Join(instances, ", "); + } // Although it is tested, explicitly rebuilding qualified name, in case it // becomes something unexpected. - ALOGI("Could not find %s.%s/%s in the VINTF manifest.", aname.package.c_str(), - aname.iface.c_str(), aname.instance.c_str()); + ALOGI("Could not find %s.%s/%s in the VINTF manifest. %s.", aname.package.c_str(), + aname.iface.c_str(), aname.instance.c_str(), available.c_str()); } return found; @@ -291,6 +308,8 @@ sp ServiceManager::tryGetService(const std::string& name, bool startIfN service = &(it->second); if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) { + LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name + << "', but the service is not allowed for isolated apps."; return nullptr; } out = service->binder; @@ -301,7 +320,7 @@ sp ServiceManager::tryGetService(const std::string& name, bool startIfN } if (!out && startIfNotFound) { - tryStartService(name); + tryStartService(ctx, name); } if (out) { @@ -372,8 +391,10 @@ Status ServiceManager::addService(const std::string& name, const sp& bi } auto it = mNameToService.find(name); + bool prevClients = false; if (it != mNameToService.end()) { const Service& existing = it->second; + prevClients = existing.hasClients; // We could do better than this because if the other service dies, it // may not have an entry here. However, this case is unlikely. We are @@ -401,12 +422,14 @@ Status ServiceManager::addService(const std::string& name, const sp& bi .binder = binder, .allowIsolated = allowIsolated, .dumpPriority = dumpPriority, + .hasClients = prevClients, // see b/279898063, matters if existing callbacks + .guaranteeClient = false, .ctx = ctx, }; if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) { - // See also getService - handles case where client never gets the service, - // we want the service to quit. + // If someone is currently waiting on the service, notify the service that + // we're waiting and flush it to the service. mNameToService[name].guaranteeClient = true; CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); mNameToService[name].guaranteeClient = true; @@ -633,6 +656,14 @@ void ServiceManager::removeRegistrationCallback(const wp& who, void ServiceManager::binderDied(const wp& who) { for (auto it = mNameToService.begin(); it != mNameToService.end();) { if (who == it->second.binder) { + // TODO: currently, this entry contains the state also + // associated with mNameToClientCallback. If we allowed + // other processes to register client callbacks, we + // would have to preserve hasClients (perhaps moving + // that state into mNameToClientCallback, which is complicated + // because those callbacks are associated w/ particular binder + // objects, though they are indexed by name now, they may + // need to be indexed by binder at that point). it = mNameToService.erase(it); } else { ++it; @@ -648,10 +679,11 @@ void ServiceManager::binderDied(const wp& who) { } } -void ServiceManager::tryStartService(const std::string& name) { - ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not " - "configured to be a lazy service, it may be stuck starting or still starting).", - name.c_str()); +void ServiceManager::tryStartService(const Access::CallingContext& ctx, const std::string& name) { + ALOGI("Since '%s' could not be found (requested by debug pid %d), trying to start it as a lazy " + "AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or " + "still starting).", + name.c_str(), ctx.debugPid); std::thread([=] { if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) { @@ -700,13 +732,21 @@ Status ServiceManager::registerClientCallback(const std::string& name, const sp< return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath."); } - // make sure all callbacks have been told about a consistent state - b/278038751 + // WARNING: binderDied makes an assumption about this. If we open up client + // callbacks to other services, certain race conditions may lead to services + // getting extra client callback notifications. + // Make sure all callbacks have been told about a consistent state - b/278038751 if (serviceIt->second.hasClients) { cb->onClients(service, true); } mNameToClientCallback[name].push_back(cb); + // Flush updated info to client callbacks (especially if guaranteeClient + // and !hasClient, see b/285202885). We may or may not have clients at + // this point, so ignore the return value. + (void)handleServiceClientCallback(2 /* sm + transaction */, name, false); + return Status::ok(); } diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 3aa6731eb3f4742e8a6c32d09e5a3e35ae12fa68..3b925a48cb5172f5dfb7d11cc32e7bba47e46066 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -67,7 +67,7 @@ public: void clear(); protected: - virtual void tryStartService(const std::string& name); + virtual void tryStartService(const Access::CallingContext& ctx, const std::string& name); private: struct Service { diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp deleted file mode 100644 index e19b6eb27989da969047b344266ae686931c3f63..0000000000000000000000000000000000000000 --- a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 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 -#include - -#include "Access.h" -#include "ServiceManager.h" - -using ::android::Access; -using ::android::Parcel; -using ::android::ServiceManager; -using ::android::sp; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - FuzzedDataProvider provider(data, size); - auto accessPtr = std::make_unique(); - auto serviceManager = sp::make(std::move(accessPtr)); - - // Reserved bytes - provider.ConsumeBytes(8); - uint32_t code = provider.ConsumeIntegral(); - uint32_t flag = provider.ConsumeIntegral(); - std::vector parcelData = provider.ConsumeRemainingBytes(); - - Parcel inputParcel; - inputParcel.setData(parcelData.data(), parcelData.size()); - - Parcel reply; - serviceManager->transact(code, inputParcel, &reply, flag); - - serviceManager->clear(); - - return 0; -} diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 deleted file mode 100644 index 07319f864e46deabad8816b6a8f998de6f19443c..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 deleted file mode 100644 index 07319f864e46deabad8816b6a8f998de6f19443c..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 deleted file mode 100644 index 07319f864e46deabad8816b6a8f998de6f19443c..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 deleted file mode 100644 index 07319f864e46deabad8816b6a8f998de6f19443c..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 deleted file mode 100644 index e69ab49d5d8fd74fc1cda4b2411994378bbd3f9a..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 deleted file mode 100644 index 88ad474f0944e99b479840294c5b26059cff5baf..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 deleted file mode 100644 index fae15a2fead96c2da652ddc6cc70e466bff229b6..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 deleted file mode 100644 index b326907a5807254045e75c0e9e09568bf284dccd..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 deleted file mode 100644 index 05b27bf413f8c7e60d359002d540ab3cea632cb4..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 deleted file mode 100644 index b326907a5807254045e75c0e9e09568bf284dccd..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 deleted file mode 100644 index cdaa1f01b11af8e98f3cff8f36580941d4a85454..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 deleted file mode 100644 index ff0941b7a60144777b6362b7dc594aa5270286bc..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 deleted file mode 100644 index cdaa1f01b11af8e98f3cff8f36580941d4a85454..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 deleted file mode 100644 index 7e5f94868282083c10e0afc66bb7adf247aeb56d..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 deleted file mode 100644 index 07319f864e46deabad8816b6a8f998de6f19443c..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 deleted file mode 100644 index 07319f864e46deabad8816b6a8f998de6f19443c..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 and /dev/null differ diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 deleted file mode 100644 index 39e510492747e4e37c44114bc2ec47a6edfd92db..0000000000000000000000000000000000000000 Binary files a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 and /dev/null differ diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index bc9cb1609d273651128ca3d0e4720aff523edbe9..ae56cb0ed3638d31dce3b7288fc3d2156415127d 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -133,7 +133,9 @@ int main(int argc, char** argv) { } IPCThreadState::self()->setTheContextObject(manager); - ps->becomeContextManager(); + if (!ps->becomeContextManager()) { + LOG(FATAL) << "Could not become context manager"; + } sp looper = Looper::prepare(false /*allowNonCallbacks*/); diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc index b927c018dfd001a472fb5cf63e3c9b0f80166f48..6354fd7d538174775681c89ad866a18bb88320fa 100644 --- a/cmds/servicemanager/servicemanager.recovery.rc +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -1,5 +1,6 @@ service servicemanager /system/bin/servicemanager disabled group system readproc + user root onrestart setprop servicemanager.ready false seclabel u:r:servicemanager:s0 diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index cae32e3bc387c8b8ffe4daa829b3f750448e4383..97e500d0a79255aa5bcda56538d648e8d98ef34f 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -27,11 +27,14 @@ #include "Access.h" #include "ServiceManager.h" -using android::sp; using android::Access; using android::BBinder; using android::IBinder; using android::ServiceManager; +using android::sp; +using android::base::EndsWith; +using android::base::GetProperty; +using android::base::StartsWith; using android::binder::Status; using android::os::BnServiceCallback; using android::os::IServiceManager; @@ -62,7 +65,7 @@ public: class MockServiceManager : public ServiceManager { public: MockServiceManager(std::unique_ptr&& access) : ServiceManager(std::move(access)) {} - MOCK_METHOD1(tryStartService, void(const std::string& name)); + MOCK_METHOD2(tryStartService, void(const Access::CallingContext&, const std::string& name)); }; static sp getPermissiveServiceManager() { @@ -77,9 +80,11 @@ static sp getPermissiveServiceManager() { return sm; } -static bool isCuttlefish() { - return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""), - "vsoc_"); +// Determines if test device is a cuttlefish phone device +static bool isCuttlefishPhone() { + auto device = GetProperty("ro.product.vendor.device", ""); + auto product = GetProperty("ro.product.vendor.name", ""); + return StartsWith(device, "vsoc_") && EndsWith(product, "_phone"); } TEST(AddService, HappyHappy) { @@ -314,7 +319,7 @@ TEST(ListServices, CriticalServices) { } TEST(Vintf, UpdatableViaApex) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::optional updatableViaApex; @@ -326,7 +331,7 @@ TEST(Vintf, UpdatableViaApex) { } TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::optional updatableViaApex; @@ -337,7 +342,7 @@ TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) { } TEST(Vintf, GetUpdatableNames) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::vector names; @@ -348,7 +353,7 @@ TEST(Vintf, GetUpdatableNames) { } TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::vector names; diff --git a/cmds/sfdo/Android.bp b/cmds/sfdo/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..c19c9da7bf16a51e4561ba86944343ba54fcadec --- /dev/null +++ b/cmds/sfdo/Android.bp @@ -0,0 +1,17 @@ +cc_binary { + name: "sfdo", + + srcs: ["sfdo.cpp"], + + shared_libs: [ + "libutils", + "libgui", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], +} diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de0e1718ab0e2b6ccb7027645d641727e067f698 --- /dev/null +++ b/cmds/sfdo/sfdo.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2023 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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace android; + +std::map g_functions; + +enum class ParseToggleResult { + kError, + kFalse, + kTrue, +}; + +const std::map g_function_details = { + {"debugFlash", "[optional(delay)] Perform a debug flash."}, + {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."}, + {"scheduleComposite", "Force composite ahead of next VSYNC."}, + {"scheduleCommit", "Force commit ahead of next VSYNC."}, + {"scheduleComposite", "PENDING - if you have a good understanding let me know!"}, + {"forceClientComposition", + "[enabled | disabled] When enabled, it disables " + "Hardware Overlays, and routes all window composition to the GPU. This can " + "help check if there is a bug in HW Composer."}, +}; + +static void ShowUsage() { + std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n"; + for (const auto& sf : g_functions) { + const std::string fn = sf.first; + std::string fdetails = "TODO"; + if (g_function_details.find(fn) != g_function_details.end()) + fdetails = g_function_details.find(fn)->second; + std::cout << " " << fn << ": " << fdetails << "\n"; + } +} + +// Returns 1 for positive keywords and 0 for negative keywords. +// If the string does not match any it will return -1. +ParseToggleResult parseToggle(const char* str) { + const std::unordered_set positive{"1", "true", "y", "yes", + "on", "enabled", "show"}; + const std::unordered_set negative{"0", "false", "n", "no", + "off", "disabled", "hide"}; + + const std::string word(str); + if (positive.count(word)) { + return ParseToggleResult::kTrue; + } + if (negative.count(word)) { + return ParseToggleResult::kFalse; + } + + return ParseToggleResult::kError; +} + +int frameRateIndicator(int argc, char** argv) { + bool hide = false, show = false; + if (argc == 3) { + show = strcmp(argv[2], "show") == 0; + hide = strcmp(argv[2], "hide") == 0; + } + + if (show || hide) { + ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show); + } else { + std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n"; + return -1; + } + return 0; +} + +int debugFlash(int argc, char** argv) { + int delay = 0; + if (argc == 3) { + delay = atoi(argv[2]) == 0; + } + + ComposerServiceAIDL::getComposerService()->setDebugFlash(delay); + return 0; +} + +int scheduleComposite([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { + ComposerServiceAIDL::getComposerService()->scheduleComposite(); + return 0; +} + +int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { + ComposerServiceAIDL::getComposerService()->scheduleCommit(); + return 0; +} + +int forceClientComposition(int argc, char** argv) { + bool enabled = true; + // A valid command looks like this: + // adb shell sfdo forceClientComposition enabled + if (argc >= 3) { + const ParseToggleResult toggle = parseToggle(argv[2]); + if (toggle == ParseToggleResult::kError) { + std::cerr << "Incorrect usage of forceClientComposition. " + "Missing [enabled | disabled].\n"; + return -1; + } + if (argc > 3) { + std::cerr << "Too many arguments after [enabled | disabled]. " + "Ignoring extra arguments.\n"; + } + enabled = (toggle == ParseToggleResult::kTrue); + } else { + std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n"; + return -1; + } + + ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled); + return 0; +} + +int main(int argc, char** argv) { + std::cout << "Execute SurfaceFlinger internal commands.\n"; + std::cout << "sfdo requires to be run with root permissions..\n"; + + g_functions["frameRateIndicator"] = frameRateIndicator; + g_functions["debugFlash"] = debugFlash; + g_functions["scheduleComposite"] = scheduleComposite; + g_functions["scheduleCommit"] = scheduleCommit; + g_functions["forceClientComposition"] = forceClientComposition; + + if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) { + std::cout << "Running: " << argv[1] << "\n"; + const std::string key(argv[1]); + const auto fn = g_functions[key]; + int result = std::any_cast(fn)(argc, argv); + if (result == 0) { + std::cout << "Success.\n"; + } + return result; + } else { + ShowUsage(); + } + return 0; +} \ No newline at end of file diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 226cae12aa638703813b1a59043e73971407f8ed..89736eceb7dce6f6200c99149a4394e7fa43829d 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -82,6 +82,12 @@ prebuilt_etc { defaults: ["frameworks_native_data_etc_defaults"], } +prebuilt_etc { + name: "android.hardware.consumerir.prebuilt.xml", + src: "android.hardware.consumerir.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + prebuilt_etc { name: "android.hardware.ethernet.prebuilt.xml", src: "android.hardware.ethernet.xml", @@ -106,12 +112,42 @@ prebuilt_etc { defaults: ["frameworks_native_data_etc_defaults"], } +prebuilt_etc { + name: "android.hardware.nfc.prebuilt.xml", + src: "android.hardware.nfc.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { + name: "android.hardware.nfc.hce.prebuilt.xml", + src: "android.hardware.nfc.hce.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + prebuilt_etc { name: "android.hardware.reboot_escrow.prebuilt.xml", src: "android.hardware.reboot_escrow.xml", defaults: ["frameworks_native_data_etc_defaults"], } +prebuilt_etc { + name: "android.hardware.se.omapi.ese.prebuilt.xml", + src: "android.hardware.se.omapi.ese.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { + name: "android.hardware.se.omapi.sd.prebuilt.xml", + src: "android.hardware.se.omapi.sd.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { + name: "android.hardware.se.omapi.uicc.prebuilt.xml", + src: "android.hardware.se.omapi.uicc.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + prebuilt_etc { name: "android.hardware.sensor.accelerometer_limited_axes_uncalibrated.prebuilt.xml", src: "android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml", @@ -257,8 +293,8 @@ prebuilt_etc { } prebuilt_etc { - name: "android.hardware.telephony.satellite.prebuilt.xml", - src: "android.hardware.telephony.satellite.xml", + name: "android.hardware.thread_network.prebuilt.xml", + src: "android.hardware.thread_network.xml", defaults: ["frameworks_native_data_etc_defaults"], } @@ -334,6 +370,18 @@ prebuilt_etc { defaults: ["frameworks_native_data_etc_defaults"], } +prebuilt_etc { + name: "android.software.opengles.deqp.level-2024-03-01.prebuilt.xml", + src: "android.software.opengles.deqp.level-2024-03-01.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { + name: "android.software.opengles.deqp.level-latest.prebuilt.xml", + src: "android.software.opengles.deqp.level-latest.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + prebuilt_etc { name: "android.software.sip.voip.prebuilt.xml", src: "android.software.sip.voip.xml", @@ -364,6 +412,18 @@ prebuilt_etc { defaults: ["frameworks_native_data_etc_defaults"], } +prebuilt_etc { + name: "android.software.vulkan.deqp.level-2024-03-01.prebuilt.xml", + src: "android.software.vulkan.deqp.level-2024-03-01.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { + name: "android.software.vulkan.deqp.level-latest.prebuilt.xml", + src: "android.software.vulkan.deqp.level-latest.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + prebuilt_etc { name: "aosp_excluded_hardware.prebuilt.xml", src: "aosp_excluded_hardware.xml", diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.thread_network.xml similarity index 74% rename from data/etc/android.hardware.telephony.satellite.xml rename to data/etc/android.hardware.thread_network.xml index d36c9587635b99a9f47e82644e3370e8a048509d..b116ed6bf8f230612293d507ad0cc8b4a2b2330a 100644 --- a/data/etc/android.hardware.telephony.satellite.xml +++ b/data/etc/android.hardware.thread_network.xml @@ -1,5 +1,5 @@ - - - + - + diff --git a/data/etc/android.hardware.type.automotive.xml b/data/etc/android.hardware.type.automotive.xml index a9b4b0526a2a3a0b1f16322e5266b57bcad78ba4..8605d18d19f914ecf6ec9d994f00e567a4c5dbd4 100644 --- a/data/etc/android.hardware.type.automotive.xml +++ b/data/etc/android.hardware.type.automotive.xml @@ -17,4 +17,6 @@ + + diff --git a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml new file mode 100644 index 0000000000000000000000000000000000000000..4eeed2af4f9d1231faacbe3d8401fef39ddea184 --- /dev/null +++ b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml new file mode 100644 index 0000000000000000000000000000000000000000..62bb10161a44b71e754ac4e87a5135c4f68f7cbf --- /dev/null +++ b/data/etc/android.software.opengles.deqp.level-latest.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml new file mode 100644 index 0000000000000000000000000000000000000000..8b2b4c8199e050a1f1f097d328b52b304d3154f5 --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml new file mode 100644 index 0000000000000000000000000000000000000000..0fc12b3b5f4709e9b19e20492644b11c027e49cd --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-latest.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/data/etc/aosp_excluded_hardware.xml b/data/etc/aosp_excluded_hardware.xml index c12f4358c5472f28e394e362beba10950f08627c..013f278f340790d818bde963410c20840e302bb7 100644 --- a/data/etc/aosp_excluded_hardware.xml +++ b/data/etc/aosp_excluded_hardware.xml @@ -18,5 +18,4 @@ - diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 35f87f96ae1df68d063515e511f7b6c8995ab81b..87a14c021de370118575df7d5b7c38f185c1e45f 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -196,7 +196,7 @@ enum AndroidBitmapCompressFormat { * * @param userContext Pointer to user-defined data passed to * {@link AndroidBitmap_compress}. - * @param data Compressed data of |size| bytes to write. + * @param data Compressed data of `size` bytes to write. * @param size Length in bytes of data to write. * @return Whether the operation succeeded. */ @@ -205,7 +205,7 @@ typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext, size_t size) __INTRODUCED_IN(30); /** - * Compress |pixels| as described by |info|. + * Compress `pixels` as described by `info`. * * Available since API level 30. * diff --git a/include/android/input.h b/include/android/input.h index 9a0eb4d83875e8dff45d564d6049bd8fea007b45..16d86af44c7534a2f9989645463f8227b94188ff 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -54,16 +54,12 @@ #include #include #include - -// This file is included by modules that have host support but android/looper.h is not supported -// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled. -#ifndef __BIONIC__ -#define __REMOVED_IN(x) __attribute__((deprecated)) -#endif #include #include +// This file may also be built on glibc or on Windows/MacOS libc's, so no-op +// definitions are provided. #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ #endif diff --git a/include/android/looper.h b/include/android/looper.h index 4fe142a8e2644ca14de93491947a7c287c7bab03..e50730d5c102cbc6cadf5ce8b64e54e7226a519b 100644 --- a/include/android/looper.h +++ b/include/android/looper.h @@ -26,10 +26,18 @@ #ifndef ANDROID_LOOPER_H #define ANDROID_LOOPER_H +#include + #ifdef __cplusplus extern "C" { #endif +// This file may also be built on glibc or on Windows/MacOS libc's, so +// deprecated definitions are provided. +#if !defined(__REMOVED_IN) +#define __REMOVED_IN(__api_level) __attribute__((__deprecated__)) +#endif + struct ALooper; /** * ALooper diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index b494f897d5df2680a67fe97dcbac5d92fcb32c55..9d2c79139fae09c1127b2ac7846b9771af97148b 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -14,6 +14,23 @@ * limitations under the License. */ + /** + * @defgroup APerformanceHint Performance Hint Manager + * + * APerformanceHint allows apps to create performance hint sessions for groups + * of threads, and provide hints to the system about the workload of those threads, + * to help the system more accurately allocate power for them. It is the NDK + * counterpart to the Java PerformanceHintManager SDK API. + * + * @{ + */ + +/** + * @file performance_hint.h + * @brief API for creating and managing a hint session. + */ + + #ifndef ANDROID_NATIVE_PERFORMANCE_HINT_H #define ANDROID_NATIVE_PERFORMANCE_HINT_H @@ -43,12 +60,33 @@ __BEGIN_DECLS struct APerformanceHintManager; struct APerformanceHintSession; +struct AWorkDuration; + +/** + * {@link AWorkDuration} is an opaque type that represents the breakdown of the + * actual workload duration in each component internally. + * + * A new {@link AWorkDuration} can be obtained using + * {@link AWorkDuration_create()}, when the client finishes using + * {@link AWorkDuration}, {@link AWorkDuration_release()} must be + * called to destroy and free up the resources associated with + * {@link AWorkDuration}. + * + * This file provides a set of functions to allow clients to set the measured + * work duration of each component on {@link AWorkDuration}. + * + * - AWorkDuration_setWorkPeriodStartTimestampNanos() + * - AWorkDuration_setActualTotalDurationNanos() + * - AWorkDuration_setActualCpuDurationNanos() + * - AWorkDuration_setActualGpuDurationNanos() + */ +typedef struct AWorkDuration AWorkDuration; /** * An opaque type representing a handle to a performance hint manager. * It must be released after use. * - *

To use:

    + * To use:
      *
    • Obtain the performance hint manager instance by calling * {@link APerformanceHint_getManager} function.
    • *
    • Create an {@link APerformanceHintSession} with @@ -61,54 +99,47 @@ typedef struct APerformanceHintManager APerformanceHintManager; /** * An opaque type representing a handle to a performance hint session. * A session can only be acquired from a {@link APerformanceHintManager} - * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be + * with {@link APerformanceHint_createSession}. It must be * freed with {@link APerformanceHint_closeSession} after use. * * A Session represents a group of threads with an inter-related workload such that hints for * their performance should be considered as a unit. The threads in a given session should be - * long-life and not created or destroyed dynamically. - * - *

      Each session is expected to have a periodic workload with a target duration for each - * cycle. The cycle duration is likely greater than the target work duration to allow other - * parts of the pipeline to run within the available budget. For example, a renderer thread may - * work at 60hz in order to produce frames at the display's frame but have a target work - * duration of only 6ms.

      - * - *

      After each cycle of work, the client is expected to use - * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to - * complete.

      - * - *

      To use:

        - *
      • Update a sessions target duration for each cycle of work - * with {@link APerformanceHint_updateTargetWorkDuration}.
      • - *
      • Report the actual duration for the last cycle of work with - * {@link APerformanceHint_reportActualWorkDuration}.
      • - *
      • Release the session instance with - * {@link APerformanceHint_closeSession}.

      + * long-lived and not created or destroyed dynamically. + * + * The work duration API can be used with periodic workloads to dynamically adjust thread + * performance and keep the work on schedule while optimizing the available power budget. + * When using the work duration API, the starting target duration should be specified + * while creating the session, and can later be adjusted with + * {@link APerformanceHint_updateTargetWorkDuration}. While using the work duration + * API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each + * cycle to report the actual time taken to complete to the system. + * + * All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)` */ typedef struct APerformanceHintSession APerformanceHintSession; /** * Acquire an instance of the performance hint manager. * - * @return manager instance on success, nullptr on failure. + * @return APerformanceHintManager instance on success, nullptr on failure. */ -APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); +APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); /** * Creates a session for the given set of threads and sets their initial target work * duration. + * * @param manager The performance hint manager instance. * @param threadIds The list of threads to be associated with this session. They must be part of - * this app's thread group. - * @param size the size of threadIds. - * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session. - * This must be positive. - * @return manager instance on success, nullptr on failure. - */ -APerformanceHintSession* APerformanceHint_createSession( - APerformanceHintManager* manager, - const int32_t* threadIds, size_t size, + * this process' thread group. + * @param size The size of the list of threadIds. + * @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session. + * This must be positive if using the work duration API, or 0 otherwise. + * @return APerformanceHintManager instance on success, nullptr on failure. + */ +APerformanceHintSession* _Nullable APerformanceHint_createSession( + APerformanceHintManager* _Nonnull manager, + const int32_t* _Nonnull threadIds, size_t size, int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -118,37 +149,36 @@ APerformanceHintSession* APerformanceHint_createSession( * @return the preferred update rate supported by device software. */ int64_t APerformanceHint_getPreferredUpdateRateNanos( - APerformanceHintManager* manager) __INTRODUCED_IN(__ANDROID_API_T__); + APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__); /** * Updates this session's target duration for each cycle of work. * * @param session The performance hint session instance to update. - * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive. - * @return 0 on success + * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive. + * @return 0 on success. * EINVAL if targetDurationNanos is not positive. * EPIPE if communication with the system service has failed. */ int APerformanceHint_updateTargetWorkDuration( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, int64_t targetDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** * Reports the actual duration for the last cycle of work. * - *

      The system will attempt to adjust the core placement of the threads within the thread - * group and/or the frequency of the core on which they are run to bring the actual duration - * close to the target duration.

      + * The system will attempt to adjust the scheduling and performance of the + * threads within the thread group to bring the actual duration close to the target duration. * * @param session The performance hint session instance to update. - * @param actualDurationNanos how long the thread group took to complete its last task in - * nanoseconds. This must be positive. - * @return 0 on success + * @param actualDurationNanos The duration of time the thread group took to complete its last + * task in nanoseconds. This must be positive. + * @return 0 on success. * EINVAL if actualDurationNanos is not positive. * EPIPE if communication with the system service has failed. */ int APerformanceHint_reportActualWorkDuration( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, int64_t actualDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -158,25 +188,123 @@ int APerformanceHint_reportActualWorkDuration( * @param session The performance hint session instance to release. */ void APerformanceHint_closeSession( - APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__); + APerformanceHintSession* _Nonnull session) __INTRODUCED_IN(__ANDROID_API_T__); /** * Set a list of threads to the performance hint session. This operation will replace * the current list of threads with the given list of threads. * - * @param session The performance hint session instance for the threads. + * @param session The performance hint session instance to update. * @param threadIds The list of threads to be associated with this session. They must be part of * this app's thread group. - * @param size the size of the list of threadIds. + * @param size The size of the list of threadIds. * @return 0 on success. - * EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group. + * EINVAL if the list of thread ids is empty or if any of the thread ids are not part of + the thread group. * EPIPE if communication with the system service has failed. + * EPERM if any thread id doesn't belong to the application. */ int APerformanceHint_setThreads( - APerformanceHintSession* session, - const pid_t* threadIds, + APerformanceHintSession* _Nonnull session, + const pid_t* _Nonnull threadIds, size_t size) __INTRODUCED_IN(__ANDROID_API_U__); +/** + * This tells the session that these threads can be + * safely scheduled to prefer power efficiency over performance. + * + * @param session The performance hint session instance to update. + * @param enabled The flag which sets whether this session will use power-efficient scheduling. + * @return 0 on success. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_setPreferPowerEfficiency( + APerformanceHintSession* _Nonnull session, + bool enabled) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Reports the durations for the last cycle of work. + * + * The system will attempt to adjust the scheduling and performance of the + * threads within the thread group to bring the actual duration close to the target duration. + * + * @param session The {@link APerformanceHintSession} instance to update. + * @param workDuration The {@link AWorkDuration} structure of times the thread group took to + * complete its last task in nanoseconds breaking down into different components. + * + * The work period start timestamp, actual total duration and actual CPU duration must be + * positive. + * + * The actual GPU duration must be non-negative. If the actual GPU duration is 0, it means + * the actual GPU duration is not measured. + * + * @return 0 on success. + * EINVAL if session is nullptr or any duration is an invalid number. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_reportActualWorkDuration2( + APerformanceHintSession* _Nonnull session, + AWorkDuration* _Nonnull workDuration) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should + * call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources + * associated with it. + * + * @return AWorkDuration on success and nullptr otherwise. + */ +AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Destroys {@link AWorkDuration} and free all resources associated to it. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + */ +void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the work period start timestamp in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on + * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive. + */ +void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual total work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be + * positive. + */ +void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual CPU work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be + * positive. + */ +void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual GPU work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}. + * @param actualGpuDurationNanos The actual GPU work duration in nanoseconds, the number must be + * non-negative. If the actual GPU duration is 0, it means the actual GPU duration is + * measured. + */ +void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualGpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H + +/** @} */ diff --git a/include/android/sensor.h b/include/android/sensor.h index 16c5dde60fab56709e95d84dc548783ea892ba45..a618393e66c1b7ffc14908931fbefc3a48a1c556 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -29,6 +29,8 @@ #ifndef ANDROID_SENSOR_H #define ANDROID_SENSOR_H +#include + /****************************************************************** * * IMPORTANT NOTICE: @@ -45,11 +47,6 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -// This file is included by modules that have host support but android/looper.h is not supported -// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled. -#ifndef __BIONIC__ -#define __REMOVED_IN(x) __attribute__((deprecated)) -#endif #include #include @@ -57,6 +54,8 @@ #include #include +// This file may also be built on glibc or on Windows/MacOS libc's, so no-op +// and deprecated definitions are provided. #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ #endif @@ -658,7 +657,7 @@ typedef struct ASensorEvent { uint32_t flags; int32_t reserved1[3]; } ASensorEvent; -// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h) +// LINT.ThenChange(hardware/libhardware/include/hardware/sensors.h) struct ASensorManager; /** diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h index e0a8045d41f37d08420a1e25556927c114969fdc..1d513a6abef6f2023f4b93c66a7344a1211ca80b 100644 --- a/include/android/sharedmem.h +++ b/include/android/sharedmem.h @@ -53,27 +53,27 @@ extern "C" { /** * Create a shared memory region. * - * Create shared memory region and returns an file descriptor. The resulting file descriptor can be - * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory - * region can be restricted with {@link ASharedMemory_setProt}. + * Create a shared memory region and returns a file descriptor. The resulting file descriptor can be + * mapped into the process' memory using mmap(2) with `PROT_READ | PROT_WRITE | PROT_EXEC`. + * Access to shared memory regions can be restricted with {@link ASharedMemory_setProt}. * - * Use close() to release the shared memory region. + * Use close(2) to release the shared memory region. * * Use android.os.ParcelFileDescriptor * to pass the file descriptor to another process. File descriptors may also be sent to other - * processes over a Unix domain socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and + * processes over a Unix domain socket with sendmsg(2) and `SCM_RIGHTS`. See sendmsg(3) and * cmsg(3) man pages for more information. * * If you intend to share this file descriptor with a child process after - * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD - * to clear the FD_CLOEXEC flag for this to work on all versions of Android. + * calling exec(3), note that you will need to use fcntl(2) with `F_SETFD` + * to clear the `FD_CLOEXEC` flag for this to work on all versions of Android. * * Available since API level 26. * * \param name an optional name. * \param size size of the shared memory region * \return file descriptor that denotes the shared memory; - * -1 and sets errno on failure, or -EINVAL if the error is that size was 0. + * -1 and sets `errno` on failure, or `-EINVAL` if the error is that size was 0. */ int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26); @@ -83,7 +83,7 @@ int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26); * Available since API level 26. * * \param fd file descriptor of the shared memory region - * \return size in bytes; 0 if fd is not a valid shared memory file descriptor. + * \return size in bytes; 0 if `fd` is not a valid shared memory file descriptor. */ size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26); @@ -115,9 +115,9 @@ size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26); * Available since API level 26. * * \param fd file descriptor of the shared memory region. - * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting + * \param prot any bitwise-or'ed combination of `PROT_READ`, `PROT_WRITE`, `PROT_EXEC` denoting * updated access. Note access can only be removed, but not added back. - * \return 0 for success, -1 and sets errno on failure. + * \return 0 for success, -1 and sets `errno` on failure. */ int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26); diff --git a/include/android/thermal.h b/include/android/thermal.h index 32580badc034f26802a5dfc903c1967e19d9e831..0b57e9376d7ea882dd6f6ba8848ad694a5b87a9c 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -111,7 +111,7 @@ typedef struct AThermalManager AThermalManager; * It's passed the updated thermal status as parameter, as well as the * pointer provided by the client that registered a callback. */ -typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status); +typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status); /** * Acquire an instance of the thermal manager. This must be freed using @@ -188,13 +188,13 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, * Note that this only attempts to track the headroom of slow-moving sensors, such as * the skin temperature sensor. This means that there is no benefit to calling this function * more frequently than about once per second, and attempted to call significantly - * more frequently may result in the function returning {@code NaN}. + * more frequently may result in the function returning `NaN`. * * In addition, in order to be able to provide an accurate forecast, the system does * not attempt to forecast until it has multiple temperature samples from which to * extrapolate. This should only take a few seconds from the time of the first call, * but during this time, no forecasting will occur, and the current headroom will be - * returned regardless of the value of {@code forecastSeconds}. + * returned regardless of the value of `forecastSeconds`. * * The value returned is a non-negative float that represents how much of the thermal envelope * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is @@ -222,6 +222,70 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) __INTRODUCED_IN(31); +/** + * This struct defines an instance of headroom threshold value and its status. + *

      + * The value should be monotonically non-decreasing as the thermal status increases. + * For {@link ATHERMAL_STATUS_SEVERE}, its headroom threshold is guaranteed to + * be 1.0f. For status below severe status, the value should be lower or equal + * to 1.0f, and for status above severe, the value should be larger or equal to 1.0f. + *

      + * Also see {@link AThermal_getThermalHeadroom} for explanation on headroom, and + * {@link AThermal_getThermalHeadroomThresholds} for how to use this. + */ +struct AThermalHeadroomThreshold { + float headroom; + AThermalStatus thermalStatus; +}; + +/** + * Gets the thermal headroom thresholds for all available thermal status. + * + * A thermal status will only exist in output if the device manufacturer has the + * corresponding threshold defined for at least one of its slow-moving skin temperature + * sensors. If it's set, one should also expect to get it from + * {@link #AThermal_getCurrentThermalStatus} or {@link AThermal_StatusCallback}. + *

      + * The headroom threshold is used to interpret the possible thermal throttling status based on + * the headroom prediction. For example, if the headroom threshold for + * {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75 + * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system + * could be in lightly throttled state if the workload remains the same. The app can consider + * taking actions according to the nearest throttling status the difference between the headroom and + * the threshold. + *

      + * For new devices it's guaranteed to have a single sensor, but for older devices with multiple + * sensors reporting different threshold values, the minimum threshold is taken to be conservative + * on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value + * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above + * will always come with lightly throttled state + * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower + * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}). + * While it's always guaranteed that the device won't be throttled heavier than the unmet + * threshold's state, so a real-time headroom of 0.75 will never come with + * {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with + * {@link ATHERMAL_STATUS_LIGHT} but {@link #ATHERMAL_STATUS_NONE}. + *

      + * The returned list of thresholds is cached on first successful query and owned by the thermal + * manager, which will not change between calls to this function. The caller should only need to + * free the manager with {@link AThermal_releaseManager}. + * + * @param manager The manager instance to use. + * Acquired via {@link AThermal_acquireManager}. + * @param outThresholds non-null output pointer to null AThermalHeadroomThreshold pointer, which + * will be set to the cached array of thresholds if thermal thresholds are supported + * by the system or device, otherwise nullptr or unmodified. + * @param size non-null output pointer whose value will be set to the size of the threshold array + * or 0 if it's not supported. + * @return 0 on success + * EINVAL if outThresholds or size_t is nullptr, or *outThresholds is not nullptr. + * EPIPE if communication with the system service has failed. + * ENOSYS if the feature is disabled by the current system. + */ +int AThermal_getThermalHeadroomThresholds(AThermalManager* manager, + const AThermalHeadroomThreshold ** outThresholds, + size_t* size) __INTRODUCED_IN(35); + #ifdef __cplusplus } #endif diff --git a/include/binder/unique_fd.h b/include/binder/unique_fd.h new file mode 120000 index 0000000000000000000000000000000000000000..433c968151cb2b4a5d0f3d84d8f84403ec13dbb2 --- /dev/null +++ b/include/binder/unique_fd.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/unique_fd.h \ No newline at end of file diff --git a/include/ftl/OWNERS b/include/ftl/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..3f6129226a0d88224bfb53ea092c46b0e456b44c --- /dev/null +++ b/include/ftl/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/native:/services/surfaceflinger/OWNERS \ No newline at end of file diff --git a/include/ftl/details/function.h b/include/ftl/details/function.h new file mode 100644 index 0000000000000000000000000000000000000000..35c5a8b30201a934de8c99b57ba78d60dd65a6f6 --- /dev/null +++ b/include/ftl/details/function.h @@ -0,0 +1,135 @@ +/* + * Copyright 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 +#include +#include +#include +#include + +namespace android::ftl::details { + +// The maximum allowed value for the template argument `N` in +// `ftl::Function`. +constexpr size_t kFunctionMaximumN = 14; + +// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member +// function type `Ret(Args...)`. + +template +struct remove_member_function_pointer; + +template +struct remove_member_function_pointer { + using type = Ret(Args...); +}; + +template +struct remove_member_function_pointer { + using type = Ret(Args...); +}; + +template +using remove_member_function_pointer_t = + typename remove_member_function_pointer::type; + +// Helper functions for binding to the supported targets. + +template +auto bind_opaque_no_op() -> Ret (*)(void*, Args...) { + return [](void*, Args...) -> Ret { + if constexpr (!std::is_void_v) { + return Ret{}; + } + }; +} + +template +auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) { + return [](void* opaque, Args... args) -> Ret { + return std::invoke(*static_cast(opaque), std::forward(args)...); + }; +} + +template +auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) { + return [instance](Args... args) -> Ret { + return std::invoke(MemberFunction, instance, std::forward(args)...); + }; +} + +template +auto bind_free_function(Ret (*)(Args...) = nullptr) { + return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward(args)...); }; +} + +// Traits class for the opaque storage used by Function. + +template +struct function_opaque_storage { + // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, + // which allows a lambda with zero or one capture args. + using type = std::array; + + template + static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v; + + template + static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v; + + template + static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); + + template + static constexpr bool require_alignment_compatible = + std::alignment_of_v <= std::alignment_of_v; + + // Copies `src` into the opaque storage, and returns that storage. + template + static type opaque_copy(const S& src) { + // TODO: Replace with C++20 concepts/constraints which can give more details. + static_assert(require_trivially_copyable, + "ftl::Function can only store lambdas that capture trivially copyable data."); + static_assert( + require_trivially_destructible, + "ftl::Function can only store lambdas that capture trivially destructible data."); + static_assert(require_will_fit_in_opaque_storage, + "ftl::Function has limited storage for lambda captured state. Maybe you need to " + "increase N?"); + static_assert(require_alignment_compatible); + + type opaque; + std::memcpy(opaque.data(), &src, sizeof(S)); + return opaque; + } +}; + +// Traits class to help determine the template parameters to use for a ftl::Function, given a +// function object. + +template +struct function_traits { + // The function type `F` with which to instantiate the `Function` template. + using type = remove_member_function_pointer_t<&F::operator()>; + + // The (minimum) size `N` with which to instantiate the `Function` template. + static constexpr std::size_t size = + (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); +}; + +} // namespace android::ftl::details diff --git a/include/ftl/enum.h b/include/ftl/enum.h index 075d12bd1757011851fed72354fc364f06d141ca..2c86e2e4c9e8fe0a53913dec29863f737e041b18 100644 --- a/include/ftl/enum.h +++ b/include/ftl/enum.h @@ -25,12 +25,12 @@ #include -// Returns the name of enumerator E::V (i.e. "V") as std::optional by parsing the -// compiler-generated string literal for the signature of this function. The function is defined in -// the global namespace with a short name and inferred return type to reduce bloat in the read-only -// data segment. -template -constexpr auto ftl_enum() { +// Returns the name of enumerator E::V and optionally the class (i.e. "E::V" or "V") as +// std::optional by parsing the compiler-generated string literal for the +// signature of this function. The function is defined in the global namespace with a short name +// and inferred return type to reduce bloat in the read-only data segment. +template +constexpr auto ftl_enum_builder() { static_assert(std::is_enum_v); using R = std::optional; @@ -58,7 +58,9 @@ constexpr auto ftl_enum() { // V = android::test::Enum::kValue // view = view.substr(value_begin); - const auto name_begin = view.rfind("::"sv); + const auto pos = S ? view.rfind("::"sv) - 2 : view.npos; + + const auto name_begin = view.rfind("::"sv, pos); if (name_begin == view.npos) return R{}; // Chop off the leading "::". @@ -68,6 +70,18 @@ constexpr auto ftl_enum() { return name.find(')') == view.npos ? R{name} : R{}; } +// Returns the name of enumerator E::V (i.e. "V") as std::optional +template +constexpr auto ftl_enum() { + return ftl_enum_builder(); +} + +// Returns the name of enumerator and class E::V (i.e. "E::V") as std::optional +template +constexpr auto ftl_enum_full() { + return ftl_enum_builder(); +} + namespace android::ftl { // Trait for determining whether a type is specifically a scoped enum or not. By definition, a @@ -191,6 +205,11 @@ struct EnumName { static constexpr auto value = ftl_enum(); }; +template +struct EnumNameFull { + static constexpr auto value = ftl_enum_full(); +}; + template struct FlagName { using E = decltype(I); @@ -230,6 +249,18 @@ constexpr std::string_view enum_name() { return *kName; } +// Returns a stringified enumerator with class at compile time. +// +// enum class E { A, B, C }; +// static_assert(ftl::enum_name() == "E::B"); +// +template +constexpr std::string_view enum_name_full() { + constexpr auto kName = ftl_enum_full(); + static_assert(kName, "Unknown enumerator"); + return *kName; +} + // Returns a stringified enumerator, possibly at compile time. // // enum class E { A, B, C, F = 5, ftl_last = F }; @@ -249,6 +280,25 @@ constexpr std::optional enum_name(E v) { return kRange.values[value - kBegin]; } +// Returns a stringified enumerator with class, possibly at compile time. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// static_assert(ftl::enum_name(E::C).value_or("?") == "E::C"); +// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); +// +template +constexpr std::optional enum_name_full(E v) { + const auto value = to_underlying(v); + + constexpr auto kBegin = to_underlying(enum_begin_v); + constexpr auto kLast = to_underlying(enum_last_v); + if (value < kBegin || value > kLast) return {}; + + constexpr auto kRange = details::EnumRange{}; + return kRange.values[value - kBegin]; +} + // Returns a stringified flag enumerator, possibly at compile time. // // enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; @@ -282,6 +332,21 @@ inline std::string enum_string(E v) { return to_string(to_underlying(v)); } +// Returns a stringified enumerator with class, or its integral value if not named. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// assert(ftl::enum_string(E::C) == "E::C"); +// assert(ftl::enum_string(E{3}) == "3"); +// +template +inline std::string enum_string_full(E v) { + if (const auto name = enum_name_full(v)) { + return std::string(*name); + } + return to_string(to_underlying(v)); +} + // Returns a stringified flag enumerator, or its integral value if not named. // // enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; diff --git a/include/ftl/function.h b/include/ftl/function.h new file mode 100644 index 0000000000000000000000000000000000000000..3538ca4eae2fa5f3e93865db9a012d102d542c2b --- /dev/null +++ b/include/ftl/function.h @@ -0,0 +1,297 @@ +/* + * Copyright 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 +#include +#include +#include + +#include + +namespace android::ftl { + +// ftl::Function is a container for function object, and can mostly be used in place of +// std::function. +// +// Unlike std::function, a ftl::Function: +// +// * Uses a static amount of memory (controlled by N), and never any dynamic allocation. +// * Satisfies the std::is_trivially_copyable<> trait. +// * Satisfies the std::is_trivially_destructible<> trait. +// +// However those same limits are also required from the contained function object in turn. +// +// The size of a ftl::Function is guaranteed to be: +// +// sizeof(std::intptr_t) * (N + 2) +// +// A ftl::Function can always be implicitly converted to a larger size ftl::Function. +// Trying to convert the other way leads to a compilation error. +// +// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns +// false in this state. It is undefined behavior to attempt to invoke the function in this state. +// +// The ftl::Function can also be constructed or assigned from ftl::no_op. This sets up the +// ftl::Function to be non-empty, with a function that when called does nothing except +// default-constructs a return value. +// +// The ftl::make_function() helpers construct a ftl::Function, including deducing the +// values of F and N from the arguments it is given. +// +// The static ftl::Function::make() helpers construct a ftl::Function without that +// deduction, and also allow for implicit argument conversion if the target being called needs them. +// +// The construction helpers allow any of the following types of functions to be stored: +// +// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small +// capture, or other "functor". The requirements are: +// +// 1) The function object must be trivial to destroy (in fact, the destructor will never +// actually be called once copied to the internal storage). +// 2) The function object must be trivial to copy (the raw bytes will be copied as the +// ftl::Function is copied/moved). +// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1), +// and it cannot require stricter alignment than alignof(std::intptr_t). +// +// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is +// enough to capture `this`, which is why N=0 is the default. +// +// * A member function, with the address passed as the template value argument to the construction +// helper function, along with the instance pointer needed to invoke it passed as an ordinary +// argument. +// +// ftl::make_function<&Class::member_function>(this); +// +// Note that the indicated member function will be invoked non-virtually. If you need it to be +// invoked virtually, you should invoke it yourself with a small lambda like so: +// +// ftl::function([this] { virtual_member_function(); }); +// +// * An ordinary function ("free function"), with the address of the function passed as a template +// value argument. +// +// ftl::make_function<&std::atoi>(); +// +// As with the member function helper, as the function is known at compile time, it will be called +// directly. +// +// Example usage: +// +// class MyClass { +// public: +// void on_event() const {} +// int on_string(int*, std::string_view) { return 1; } +// +// auto get_function() { +// return ftl::function([this] { on_event(); }); +// } +// } cls; +// +// // A function container with no arguments, and returning no value. +// ftl::Function f; +// +// // Construct a ftl::Function containing a small lambda. +// f = cls.get_function(); +// +// // Construct a ftl::Function that calls `cls.on_event()`. +// f = ftl::function<&MyClass::on_event>(&cls); +// +// // Create a do-nothing function. +// f = ftl::no_op; +// +// // Invoke the contained function. +// f(); +// +// // Also invokes it. +// std::invoke(f); +// +// // Create a typedef to give a more meaningful name and bound the size. +// using MyFunction = ftl::Function; +// int* ptr = nullptr; +// auto f1 = MyFunction::make_function( +// [cls = &cls, ptr](std::string_view sv) { +// return cls->on_string(ptr, sv); +// }); +// int r = f1("abc"sv); +// +// // Returns a default-constructed int (0). +// f1 = ftl::no_op; +// r = f1("abc"sv); +// assert(r == 0); + +template +class Function; + +// Used to construct a Function that does nothing. +struct NoOpTag {}; + +constexpr NoOpTag no_op; + +// Detects that a type is a `ftl::Function` regardless of what `F` and `N` are. +template +struct is_function : public std::false_type {}; + +template +struct is_function> : public std::true_type {}; + +template +constexpr bool is_function_v = is_function::value; + +template +class Function final { + // Enforce a valid size, with an arbitrary maximum allowed size for the container of + // sizeof(std::intptr_t) * 16, though that maximum can be relaxed. + static_assert(N <= details::kFunctionMaximumN); + + using OpaqueStorageTraits = details::function_opaque_storage; + + public: + // Defining result_type allows ftl::Function to be substituted for std::function. + using result_type = Ret; + + // Constructs an empty ftl::Function. + Function() = default; + + // Constructing or assigning from nullptr_t also creates an empty ftl::Function. + Function(std::nullptr_t) {} + Function& operator=(std::nullptr_t) { return *this = Function(nullptr); } + + // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which + // returns a default constructed return value. + Function(NoOpTag) : function_(details::bind_opaque_no_op()) {} + Function& operator=(NoOpTag) { return *this = Function(no_op); } + + // Constructing/assigning from a function object stores a copy of that function object, however: + // * It must be trivially copyable, as the implementation makes a copy with memcpy(). + // * It must be trivially destructible, as the implementation doesn't destroy the copy! + // * It must fit in the limited internal storage, which enforces size/alignment restrictions. + + template >> + Function(const F& f) + : opaque_(OpaqueStorageTraits::opaque_copy(f)), + function_(details::bind_opaque_function_object(f)) {} + + template >> + Function& operator=(const F& f) noexcept { + return *this = Function{OpaqueStorageTraits::opaque_copy(f), + details::bind_opaque_function_object(f)}; + } + + // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else. + + template + Function(const Function& other) + : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {} + + template + auto& operator=(const Function& other) { + return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_}; + } + + // Returns true if a function is set. + explicit operator bool() const { return function_ != nullptr; } + + // Checks if the other function has the same contents as this one. + bool operator==(const Function& other) const { + return other.opaque_ == opaque_ && other.function_ == function_; + } + bool operator!=(const Function& other) const { return !operator==(other); } + + // Alternative way of testing for a function being set. + bool operator==(std::nullptr_t) const { return function_ == nullptr; } + bool operator!=(std::nullptr_t) const { return function_ != nullptr; } + + // Invokes the function. + Ret operator()(Args... args) const { + return std::invoke(function_, opaque_.data(), std::forward(args)...); + } + + // Creation helper for function objects, such as lambdas. + template + static auto make(const F& f) -> decltype(Function{f}) { + return Function{f}; + } + + // Creation helper for a class pointer and a compile-time chosen member function to call. + template + static auto make(Class* instance) -> decltype(Function{ + details::bind_member_function(instance, + static_cast(nullptr))}) { + return Function{details::bind_member_function( + instance, static_cast(nullptr))}; + } + + // Creation helper for a compile-time chosen free function to call. + template + static auto make() -> decltype(Function{ + details::bind_free_function(static_cast(nullptr))}) { + return Function{ + details::bind_free_function(static_cast(nullptr))}; + } + + private: + // Needed so a Function can be converted to a Function. + template + friend class Function; + + // The function pointer type of function stored in `function_`. The first argument is always + // `&opaque_`. + using StoredFunction = Ret(void*, Args...); + + // The type of the opaque storage, used to hold an appropriate function object. + // The type stored here is ONLY known to the StoredFunction. + // We always use at least one std::intptr_t worth of storage, and always a multiple of that size. + using OpaqueStorage = typename OpaqueStorageTraits::type; + + // Internal constructor for creating from a raw opaque blob + function pointer. + Function(const OpaqueStorage& opaque, StoredFunction* function) + : opaque_(opaque), function_(function) {} + + // Note: `mutable` so that `operator() const` can use it. + mutable OpaqueStorage opaque_{}; + StoredFunction* function_{nullptr}; +}; + +// Makes a ftl::Function given a function object `F`. +template > +Function(const F&) -> Function; + +template +auto make_function(const F& f) -> decltype(Function{f}) { + return Function{f}; +} + +// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`. +template +auto make_function(Class* instance) + -> decltype(Function{details::bind_member_function( + instance, + static_cast*>(nullptr))}) { + return Function{details::bind_member_function( + instance, static_cast*>(nullptr))}; +} + +// Makes a ftl::Function given an ordinary free function. +template +auto make_function() -> decltype(Function{ + details::bind_free_function(static_cast(nullptr))}) { + return Function{ + details::bind_free_function(static_cast(nullptr))}; +} + +} // namespace android::ftl diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 7457496784110bb00a35c12a1e8404f3ca5e2ed8..b0eceefba06b2dfc1b5ab4819c44ed14fa3150b8 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -130,9 +130,9 @@ struct DisplayViewport { "isActive=[%d]", ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(), physicalPort ? ftl::to_string(*physicalPort).c_str() : "", - orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, - physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, - deviceHeight, isActive); + static_cast(orientation), logicalLeft, logicalTop, logicalRight, + logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom, + deviceWidth, deviceHeight, isActive); } }; diff --git a/include/input/Input.h b/include/input/Input.h index 527a47741c4ba71ab23df5e514cd906e5c56e51e..1c4ea6b41612d67e9cd2a798e849f80fbbfc16ee 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -257,6 +257,10 @@ enum class KeyState { bool isStylusToolType(ToolType toolType); +struct PointerProperties; + +bool isStylusEvent(uint32_t source, const std::vector& properties); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -282,8 +286,19 @@ enum { // Indicates that the key represents a special gesture that has been detected by // the touch firmware or driver. Causes touch events from the same device to be canceled. + // This policy flag prevents key events from changing touch mode state. POLICY_FLAG_GESTURE = 0x00000008, + // Indicates that key usage mapping represents a fallback mapping. + // Fallback mappings cannot be used to definitively determine whether a device + // supports a key code. For example, a HID device can report a key press + // as a HID usage code if it is not mapped to any linux key code in the kernel. + // However, we cannot know which HID usage codes that device supports from + // userspace through the evdev. We can use fallback mappings to convert HID + // usage codes to Android key codes without needing to know if a device can + // actually report the usage code. + POLICY_FLAG_FALLBACK_USAGE_MAPPING = 0x00000010, + POLICY_FLAG_RAW_MASK = 0x0000ffff, #ifdef __linux__ @@ -492,14 +507,17 @@ struct PointerProperties { toolType = ToolType::UNKNOWN; } - bool operator==(const PointerProperties& other) const; + bool operator==(const PointerProperties& other) const = default; inline bool operator!=(const PointerProperties& other) const { return !(*this == other); } - void copyFrom(const PointerProperties& other); + PointerProperties& operator=(const PointerProperties&) = default; }; +// TODO(b/211379801) : Use a strong type from ftl/mixins.h instead +using DeviceId = int32_t; + /* * Input events. */ @@ -511,7 +529,7 @@ public: inline int32_t getId() const { return mId; } - inline int32_t getDeviceId() const { return mDeviceId; } + inline DeviceId getDeviceId() const { return mDeviceId; } inline uint32_t getSource() const { return mSource; } @@ -526,13 +544,13 @@ public: static int32_t nextId(); protected: - void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId, std::array hmac); void initialize(const InputEvent& from); int32_t mId; - int32_t mDeviceId; + DeviceId mDeviceId; uint32_t mSource; int32_t mDisplayId; std::array mHmac; @@ -547,7 +565,7 @@ class KeyEvent : public InputEvent { public: virtual ~KeyEvent() { } - virtual InputEventType getType() const { return InputEventType::KEY; } + InputEventType getType() const override { return InputEventType::KEY; } inline int32_t getAction() const { return mAction; } @@ -570,7 +588,7 @@ public: static const char* getLabel(int32_t keyCode); static std::optional getKeyCodeFromLabel(const char* label); - void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); @@ -598,7 +616,7 @@ class MotionEvent : public InputEvent { public: virtual ~MotionEvent() { } - virtual InputEventType getType() const { return InputEventType::MOTION; } + InputEventType getType() const override { return InputEventType::MOTION; } inline int32_t getAction() const { return mAction; } @@ -834,7 +852,7 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, @@ -926,7 +944,7 @@ class FocusEvent : public InputEvent { public: virtual ~FocusEvent() {} - virtual InputEventType getType() const override { return InputEventType::FOCUS; } + InputEventType getType() const override { return InputEventType::FOCUS; } inline bool getHasFocus() const { return mHasFocus; } @@ -945,7 +963,7 @@ class CaptureEvent : public InputEvent { public: virtual ~CaptureEvent() {} - virtual InputEventType getType() const override { return InputEventType::CAPTURE; } + InputEventType getType() const override { return InputEventType::CAPTURE; } inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; } @@ -964,7 +982,7 @@ class DragEvent : public InputEvent { public: virtual ~DragEvent() {} - virtual InputEventType getType() const override { return InputEventType::DRAG; } + InputEventType getType() const override { return InputEventType::DRAG; } inline bool isExiting() const { return mIsExiting; } @@ -988,7 +1006,7 @@ class TouchModeEvent : public InputEvent { public: virtual ~TouchModeEvent() {} - virtual InputEventType getType() const override { return InputEventType::TOUCH_MODE; } + InputEventType getType() const override { return InputEventType::TOUCH_MODE; } inline bool isInTouchMode() const { return mIsInTouchMode; } @@ -1012,7 +1030,7 @@ struct __attribute__((__packed__)) VerifiedInputEvent { }; Type type; - int32_t deviceId; + DeviceId deviceId; nsecs_t eventTimeNanos; uint32_t source; int32_t displayId; @@ -1120,6 +1138,24 @@ private: std::queue> mTouchModeEventPool; }; +/** + * An input event factory implementation that simply creates the input events on the heap, when + * needed. The caller is responsible for destroying the returned references. + * It is recommended that the caller wrap these return values into std::unique_ptr. + */ +class DynamicInputEventFactory : public InputEventFactoryInterface { +public: + explicit DynamicInputEventFactory(){}; + ~DynamicInputEventFactory(){}; + + KeyEvent* createKeyEvent() override { return new KeyEvent(); }; + MotionEvent* createMotionEvent() override { return new MotionEvent(); }; + FocusEvent* createFocusEvent() override { return new FocusEvent(); }; + CaptureEvent* createCaptureEvent() override { return new CaptureEvent(); }; + DragEvent* createDragEvent() override { return new DragEvent(); }; + TouchModeEvent* createTouchModeEvent() override { return new TouchModeEvent(); }; +}; + /* * Describes a unique request to enable or disable Pointer Capture. */ diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h index 9c0c10e6036bfae07c3d0a8921c8525d1d01174b..2d23b97386fe76f9ec7c5bd6b2f49184e012d08a 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -160,4 +160,90 @@ private: std::vector mPointers; }; +class KeyEventBuilder { +public: + KeyEventBuilder(int32_t action, int32_t source) { + mAction = action; + mSource = source; + mEventTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDownTime = mEventTime; + } + + KeyEventBuilder(const KeyEvent& event) { + mAction = event.getAction(); + mDeviceId = event.getDeviceId(); + mSource = event.getSource(); + mDownTime = event.getDownTime(); + mEventTime = event.getEventTime(); + mDisplayId = event.getDisplayId(); + mFlags = event.getFlags(); + mKeyCode = event.getKeyCode(); + mScanCode = event.getScanCode(); + mMetaState = event.getMetaState(); + mRepeatCount = event.getRepeatCount(); + } + + KeyEventBuilder& deviceId(int32_t deviceId) { + mDeviceId = deviceId; + return *this; + } + + KeyEventBuilder& downTime(nsecs_t downTime) { + mDownTime = downTime; + return *this; + } + + KeyEventBuilder& eventTime(nsecs_t eventTime) { + mEventTime = eventTime; + return *this; + } + + KeyEventBuilder& displayId(int32_t displayId) { + mDisplayId = displayId; + return *this; + } + + KeyEventBuilder& policyFlags(int32_t policyFlags) { + mPolicyFlags = policyFlags; + return *this; + } + + KeyEventBuilder& addFlag(uint32_t flags) { + mFlags |= flags; + return *this; + } + + KeyEventBuilder& keyCode(int32_t keyCode) { + mKeyCode = keyCode; + return *this; + } + + KeyEventBuilder& repeatCount(int32_t repeatCount) { + mRepeatCount = repeatCount; + return *this; + } + + KeyEvent build() const { + KeyEvent event{}; + event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC, + mAction, mFlags, mKeyCode, mScanCode, mMetaState, mRepeatCount, mDownTime, + mEventTime); + return event; + } + +private: + int32_t mAction; + int32_t mDeviceId = DEFAULT_DEVICE_ID; + uint32_t mSource; + nsecs_t mDownTime; + nsecs_t mEventTime; + int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS; + int32_t mFlags{0}; + int32_t mKeyCode{AKEYCODE_UNKNOWN}; + int32_t mScanCode{0}; + int32_t mMetaState{AMETA_NONE}; + int32_t mRepeatCount{0}; +}; + } // namespace android diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 909bf087ddb6de2bc58a95d2a01d288c7d4e48b6..44247c1249a56d30f26144436fe517987f108987 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -69,6 +69,12 @@ public: static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value); + static std::optional getLinuxEvdevEventTypeByLabel(const char* label); + + static std::optional getLinuxEvdevEventCodeByLabel(int32_t type, const char* label); + + static std::optional getLinuxEvdevInputPropByLabel(const char* label); + private: InputEventLookup(); diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 4f53c36d6f2508560872c6d35b8789aa63291573..59b9495c428d6677bcaad54ea931fe0d22fbb1f4 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -240,7 +240,7 @@ public: android::base::unique_fd fd, sp token); InputChannel() = default; InputChannel(const InputChannel& other) - : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){}; + : mName(other.mName), mFd(other.dupFd()), mToken(other.mToken){}; InputChannel(const std::string name, android::base::unique_fd fd, sp token); ~InputChannel() override; /** @@ -310,7 +310,7 @@ public: if (fstat(mFd.get(), &lhs) != 0) { return false; } - if (fstat(inputChannel.getFd(), &rhs) != 0) { + if (fstat(inputChannel.getFd().get(), &rhs) != 0) { return false; } // If file descriptors are pointing to same inode they are duplicated fds. @@ -322,7 +322,7 @@ private: base::unique_fd dupFd() const; std::string mName; - android::base::unique_fd mFd; + base::unique_fd mFd; sp mToken; }; diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h index 371540838829d1b6399ef72b8752f328a91ed684..14dd463425a411a996e5df2ef048504ea93062b1 100644 --- a/include/input/InputVerifier.h +++ b/include/input/InputVerifier.h @@ -46,11 +46,13 @@ class InputVerifier { public: InputVerifier(const std::string& name); - android::base::Result processMovement(int32_t deviceId, int32_t action, + android::base::Result processMovement(int32_t deviceId, int32_t source, int32_t action, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, int32_t flags); + void resetDevice(int32_t deviceId); + private: rust::Box mVerifier; }; diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index b2e8baade364f65f857240fe7902f4870985c1f0..dfcf766402323b0708eb2bab1dca80e4f0cc7903 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -146,7 +146,7 @@ public: #ifdef __linux__ /* Reads a key map from a parcel. */ - static std::shared_ptr readFromParcel(Parcel* parcel); + static std::unique_ptr readFromParcel(Parcel* parcel); /* Writes a key map to a parcel. */ void writeToParcel(Parcel* parcel) const; diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 8c3c74af205f46191a1f40c41387942bf91c7dd9..b126abecea890f34849135add5b11b8c21e35f89 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -17,13 +17,13 @@ #pragma once #include +#include + #include #include #include #include -#include - namespace android { struct AxisInfo { diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h index 8797962886850043cc4bdf22bb51499b9b13e80e..3b6e40183fa4175846fdd110eecc270f0ca4d7b7 100644 --- a/include/input/MotionPredictor.h +++ b/include/input/MotionPredictor.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -57,20 +58,23 @@ static inline bool isMotionPredictionEnabled() { */ class MotionPredictor { public: + using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction; + /** * Parameters: * predictionTimestampOffsetNanos: additional, constant shift to apply to the target * prediction time. The prediction will target the time t=(prediction time + * predictionTimestampOffsetNanos). * - * modelPath: filesystem path to a TfLiteMotionPredictorModel flatbuffer, or nullptr to use the - * default model path. - * - * checkEnableMotionPredition: the function to check whether the prediction should run. Used to + * checkEnableMotionPrediction: the function to check whether the prediction should run. Used to * provide an additional way of turning prediction on and off. Can be toggled at runtime. + * + * reportAtomFunction: the function that will be called to report prediction metrics. If + * omitted, the implementation will choose a default metrics reporting mechanism. */ MotionPredictor(nsecs_t predictionTimestampOffsetNanos, - std::function checkEnableMotionPrediction = isMotionPredictionEnabled); + std::function checkEnableMotionPrediction = isMotionPredictionEnabled, + ReportAtomFunction reportAtomFunction = {}); /** * Record the actual motion received by the view. This event will be used for calculating the @@ -95,6 +99,8 @@ private: std::optional mLastEvent; std::optional mMetricsManager; + + const ReportAtomFunction mReportAtomFunction; }; } // namespace android diff --git a/include/input/MotionPredictorMetricsManager.h b/include/input/MotionPredictorMetricsManager.h index 12e50ba3b49a0fd8a8cdcbfd3c96c078a42a79e5..38472d8df7140dd8384cc543d3007a02d0ae9a9b 100644 --- a/include/input/MotionPredictorMetricsManager.h +++ b/include/input/MotionPredictorMetricsManager.h @@ -18,7 +18,6 @@ #include #include #include -#include #include #include // for MotionEvent @@ -37,15 +36,33 @@ namespace android { * * This class stores AggregatedStrokeMetrics, updating them as new MotionEvents are passed in. When * onRecord receives an UP or CANCEL event, this indicates the end of the stroke, and the final - * AtomFields are computed and reported to the stats library. + * AtomFields are computed and reported to the stats library. The number of atoms reported is equal + * to the value of `maxNumPredictions` passed to the constructor. Each atom corresponds to one + * "prediction time bucket" — the amount of time into the future being predicted. * * If mMockLoggedAtomFields is set, the batch of AtomFields that are reported to the stats library * for one stroke are also stored in mMockLoggedAtomFields at the time they're reported. */ class MotionPredictorMetricsManager { public: - // Note: the MetricsManager assumes that the input interval equals the prediction interval. - MotionPredictorMetricsManager(nsecs_t predictionInterval, size_t maxNumPredictions); + struct AtomFields; + + using ReportAtomFunction = std::function; + + static void defaultReportAtomFunction(const AtomFields& atomFields); + + // Parameters: + // • predictionInterval: the time interval between successive prediction target timestamps. + // Note: the MetricsManager assumes that the input interval equals the prediction interval. + // • maxNumPredictions: the maximum number of distinct target timestamps the prediction model + // will generate predictions for. The MetricsManager reports this many atoms per stroke. + // • [Optional] reportAtomFunction: the function that will be called to report metrics. If + // omitted (or if an empty function is given), the `stats_write(…)` function from the Android + // stats library will be used. + MotionPredictorMetricsManager( + nsecs_t predictionInterval, + size_t maxNumPredictions, + ReportAtomFunction reportAtomFunction = defaultReportAtomFunction); // This method should be called once for each call to MotionPredictor::record, receiving the // forwarded MotionEvent argument. @@ -121,7 +138,7 @@ public: // magnitude makes it unobtainable in practice.) static const int NO_DATA_SENTINEL = std::numeric_limits::min(); - // Final metrics reported in the atom. + // Final metric values reported in the atom. struct AtomFields { int deltaTimeBucketMilliseconds = 0; @@ -140,15 +157,6 @@ public: int scaleInvariantOffTrajectoryRmse = NO_DATA_SENTINEL; // millipixels }; - // Allow tests to pass in a mock AtomFields pointer. - // - // When metrics are reported to the stats library on stroke end, they will also be written to - // mockLoggedAtomFields, overwriting existing data. The size of mockLoggedAtomFields will equal - // the number of calls to stats_write for that stroke. - void setMockLoggedAtomFields(std::vector* mockLoggedAtomFields) { - mMockLoggedAtomFields = mockLoggedAtomFields; - } - private: // The interval between consecutive predictions' target timestamps. We assume that the input // interval also equals this value. @@ -172,11 +180,7 @@ private: std::vector mAggregatedMetrics; std::vector mAtomFields; - // Non-owning pointer to the location of mock AtomFields. If present, will be filled with the - // values reported to stats_write on each batch of reported metrics. - // - // This pointer must remain valid as long as the MotionPredictorMetricsManager exists. - std::vector* mMockLoggedAtomFields = nullptr; + const ReportAtomFunction mReportAtomFunction; // Helper methods for the implementation of onRecord and onPredict. @@ -196,10 +200,7 @@ private: // Computes the atom fields to mAtomFields from the values in mAggregatedMetrics. void computeAtomFields(); - // Reports the metrics given by the current data in mAtomFields: - // • If on an Android device, reports the metrics to stats_write. - // • If mMockLoggedAtomFields is present, it will be overwritten with logged metrics, with one - // AtomFields element per call to stats_write. + // Reports the current data in mAtomFields by calling mReportAtomFunction. void reportMetrics(); }; diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 0e3fbb1982244eb31955b82f3545260710c3f84a..83fffa37c6f25ff92e936c91377eae30213966e3 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,13 @@ std::string bitsetToString(const std::bitset& bitset) { return bitset.to_string(); } +template +std::string streamableToString(const T& streamable) { + std::stringstream out; + out << streamable; + return out.str(); +} + template inline std::string constToString(const T& v) { return std::to_string(v); @@ -75,11 +83,12 @@ std::string dumpSet(const std::set& v, std::string (*toString)(const T&) = co } /** - * Convert a map to string. Both keys and values of the map should be integral type. + * Convert a map or multimap to string. Both keys and values of the map should be integral type. */ -template -std::string dumpMap(const std::map& map, std::string (*keyToString)(const K&) = constToString, - std::string (*valueToString)(const V&) = constToString) { +template +std::string dumpMap(const T& map, + std::string (*keyToString)(const typename T::key_type&) = constToString, + std::string (*valueToString)(const typename T::mapped_type&) = constToString) { std::string out; for (const auto& [k, v] : map) { if (!out.empty()) { @@ -104,15 +113,13 @@ std::string dumpMapKeys(const std::map& map, return out.empty() ? "{}" : (out + "}"); } -/** - * Convert a vector to a string. The values of the vector should be of a type supported by - * constToString. - */ +/** Convert a vector to a string. */ template -std::string dumpVector(std::vector values) { - std::string dump = constToString(values[0]); +std::string dumpVector(const std::vector& values, + std::string (*valueToString)(const T&) = constToString) { + std::string dump = valueToString(values[0]); for (size_t i = 1; i < values.size(); i++) { - dump += ", " + constToString(values[i]); + dump += ", " + valueToString(values[i]); } return dump; } diff --git a/include/input/RingBuffer.h b/include/input/RingBuffer.h index 37fe5afeeacc754aaf6550bae99d029dabb13b2b..d2747d6fadd73a52aad188831c24a66352ea5fd6 100644 --- a/include/input/RingBuffer.h +++ b/include/input/RingBuffer.h @@ -24,7 +24,6 @@ #include #include -#include #include namespace android { @@ -277,15 +276,16 @@ private: // Converts the index of an element in [0, size()] to its corresponding index in mBuffer. size_type bufferIndex(size_type elementIndex) const { - CHECK_LE(elementIndex, size()); + if (elementIndex > size()) { + abort(); + } size_type index = mBegin + elementIndex; if (index >= capacity()) { index -= capacity(); } - CHECK_LT(index, capacity()) - << android::base::StringPrintf("Invalid index calculated for element (%zu) " - "in buffer of size %zu", - elementIndex, size()); + if (index >= capacity()) { + abort(); + } return index; } diff --git a/include/input/TraceTools.h b/include/input/TraceTools.h new file mode 100644 index 0000000000000000000000000000000000000000..81fc7af4ae911092f5215679758c2d0b96fc4987 --- /dev/null +++ b/include/input/TraceTools.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 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 +#include + +// A macro for tracing when the given condition is true. +// This macro relies on the fact that only one branch of the ternary operator is evaluated. That +// means if `message` is an expression that evaluates to a std::string value, the value will +// not be computed unless the condition is true. +#define ATRACE_NAME_IF(condition, message) \ + const auto _trace_token = condition \ + ? std::make_optional(ATRACE_TAG, (message).c_str()) \ + : std::nullopt diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h index f3c201e7c43fe087bc350faea094236c8eb659a5..b78f63e1ae444b1bd7c35181d07fb1dce02e26bb 100644 --- a/include/input/VelocityControl.h +++ b/include/input/VelocityControl.h @@ -88,7 +88,7 @@ public: VelocityControl(); /* Gets the various parameters. */ - VelocityControlParameters& getParameters(); + const VelocityControlParameters& getParameters() const; /* Sets the various parameters. */ void setParameters(const VelocityControlParameters& parameters); diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 4257cb5e05df28fe30f763ed99a9081ccef4bf73..ee7445544b89933cace9b26be5ef28a29d7425b8 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -16,7 +16,9 @@ #pragma once +#include #include +#include #include #include #include @@ -31,40 +33,25 @@ class VelocityTrackerStrategy; */ class VelocityTracker { public: + static const size_t MAX_DEGREE = 4; + enum class Strategy : int32_t { - DEFAULT = -1, - MIN = 0, - IMPULSE = 0, - LSQ1 = 1, - LSQ2 = 2, - LSQ3 = 3, - WLSQ2_DELTA = 4, - WLSQ2_CENTRAL = 5, - WLSQ2_RECENT = 6, - INT1 = 7, - INT2 = 8, - LEGACY = 9, + DEFAULT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_DEFAULT, + IMPULSE = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_IMPULSE, + LSQ1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ1, + LSQ2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ2, + LSQ3 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ3, + WLSQ2_DELTA = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA, + WLSQ2_CENTRAL = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL, + WLSQ2_RECENT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT, + INT1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT1, + INT2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT2, + LEGACY = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LEGACY, + MIN = IMPULSE, MAX = LEGACY, ftl_last = LEGACY, }; - struct Estimator { - static const size_t MAX_DEGREE = 4; - - // Estimator time base. - nsecs_t time = 0; - - // Polynomial coefficients describing motion. - std::array coeff{}; - - // Polynomial degree (number of coefficients), or zero if no information is - // available. - uint32_t degree = 0; - - // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). - float confidence = 0; - }; - /* * Contains all available velocity data from a VelocityTracker. */ @@ -111,7 +98,7 @@ public: void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. - void addMovement(const MotionEvent* event); + void addMovement(const MotionEvent& event); // Returns the velocity of the specified pointer id and axis in position units per second. // Returns empty optional if there is insufficient movement information for the pointer, or if @@ -123,11 +110,6 @@ public: // [-maxVelocity, maxVelocity], inclusive. ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity); - // Gets an estimator for the recent movements of the specified pointer id for the given axis. - // Returns false and clears the estimator if there is no information available - // about the pointer. - std::optional getEstimator(int32_t axis, int32_t pointerId) const; - // Gets the active pointer id, or -1 if none. inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); } @@ -168,14 +150,48 @@ public: virtual void clearPointer(int32_t pointerId) = 0; virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0; - virtual std::optional getEstimator(int32_t pointerId) const = 0; + virtual std::optional getVelocity(int32_t pointerId) const = 0; }; +/** + * A `VelocityTrackerStrategy` that accumulates added data points and processes the accumulated data + * points when getting velocity. + */ +class AccumulatingVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + AccumulatingVelocityTrackerStrategy(nsecs_t horizonNanos, bool maintainHorizonDuringAdd); + + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + void clearPointer(int32_t pointerId) override; + +protected: + struct Movement { + nsecs_t eventTime; + float position; + }; + + // Number of samples to keep. + // If different strategies would like to maintain different history size, we can make this a + // protected const field. + static constexpr uint32_t HISTORY_SIZE = 20; + + /** + * Duration, in nanoseconds, since the latest movement where a movement may be considered for + * velocity calculation. + */ + const nsecs_t mHorizonNanos; + /** + * If true, data points outside of horizon (see `mHorizonNanos`) will be cleared after each + * addition of a new movement. + */ + const bool mMaintainHorizonDuringAdd; + std::map> mMovements; +}; /* * Velocity tracker algorithm based on least-squares linear regression. */ -class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { +class LeastSquaresVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { public: enum class Weighting { // No weights applied. All data points are equally reliable. @@ -192,13 +208,11 @@ public: RECENT, }; - // Degree must be no greater than Estimator::MAX_DEGREE. + // Degree must be no greater than VelocityTracker::MAX_DEGREE. LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE); ~LeastSquaresVelocityTrackerStrategy() override; - void clearPointer(int32_t pointerId) override; - void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; - std::optional getEstimator(int32_t pointerId) const override; + std::optional getVelocity(int32_t pointerId) const override; private: // Sample horizon. @@ -206,23 +220,19 @@ private: // changes in direction. static const nsecs_t HORIZON = 100 * 1000000; // 100 ms - // Number of samples to keep. - static const uint32_t HISTORY_SIZE = 20; - - struct Movement { - nsecs_t eventTime; - float position; - }; - float chooseWeight(int32_t pointerId, uint32_t index) const; + /** + * An optimized least-squares solver for degree 2 and no weight (i.e. `Weighting.NONE`). + * The provided container of movements shall NOT be empty, and shall have the movements in + * chronological order. + */ + std::optional solveUnweightedLeastSquaresDeg2( + const RingBuffer& movements) const; const uint32_t mDegree; const Weighting mWeighting; - std::map mIndex; - std::map> mMovements; }; - /* * Velocity tracker algorithm that uses an IIR filter. */ @@ -234,7 +244,7 @@ public: void clearPointer(int32_t pointerId) override; void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override; - std::optional getEstimator(int32_t pointerId) const override; + std::optional getVelocity(int32_t pointerId) const override; private: // Current state estimate for a particular pointer. @@ -251,49 +261,33 @@ private: void initState(State& state, nsecs_t eventTime, float pos) const; void updateState(State& state, nsecs_t eventTime, float pos) const; - void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; }; /* * Velocity tracker strategy used prior to ICS. */ -class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { +class LegacyVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { public: LegacyVelocityTrackerStrategy(); ~LegacyVelocityTrackerStrategy() override; - void clearPointer(int32_t pointerId) override; - void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; - std::optional getEstimator(int32_t pointerId) const override; + std::optional getVelocity(int32_t pointerId) const override; private: // Oldest sample to consider when calculating the velocity. static const nsecs_t HORIZON = 200 * 1000000; // 100 ms - // Number of samples to keep. - static const uint32_t HISTORY_SIZE = 20; - // The minimum duration between samples when estimating velocity. static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms - - struct Movement { - nsecs_t eventTime; - float position; - }; - - std::map mIndex; - std::map> mMovements; }; -class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { +class ImpulseVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { public: ImpulseVelocityTrackerStrategy(bool deltaValues); ~ImpulseVelocityTrackerStrategy() override; - void clearPointer(int32_t pointerId) override; - void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; - std::optional getEstimator(int32_t pointerId) const override; + std::optional getVelocity(int32_t pointerId) const override; private: // Sample horizon. @@ -301,21 +295,10 @@ private: // changes in direction. static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms - // Number of samples to keep. - static constexpr size_t HISTORY_SIZE = 20; - - struct Movement { - nsecs_t eventTime; - float position; - }; - // Whether or not the input movement values for the strategy come in the form of delta values. // If the input values are not deltas, the strategy needs to calculate deltas as part of its // velocity calculation. const bool mDeltaValues; - - std::map mIndex; - std::map> mMovements; }; } // namespace android diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h index 82e4551c3c7aafd605fc3fce8a2e539e231528dd..9e426d3ea3792324aae39a2187d281716d0ca36a 100644 --- a/include/powermanager/PowerHalController.h +++ b/include/powermanager/PowerHalController.h @@ -17,11 +17,11 @@ #ifndef ANDROID_POWERHALCONTROLLER_H #define ANDROID_POWERHALCONTROLLER_H +#include +#include +#include +#include #include -#include -#include -#include -#include #include namespace android { @@ -55,11 +55,13 @@ public: virtual void init(); - virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult> createHintSession( - int32_t tgid, int32_t uid, const std::vector& threadIds, - int64_t durationNanos) override; + virtual HalResult setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + virtual HalResult setMode(aidl::android::hardware::power::Mode mode, + bool enabled) override; + virtual HalResult> + createHintSession(int32_t tgid, int32_t uid, const std::vector& threadIds, + int64_t durationNanos) override; virtual HalResult getHintSessionPreferredRate() override; private: diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h index e0384f31dba2a0c1ad10ce92d68ba2da6f8d74b4..cbbfa597bad8559e1dcfae69c701da03997445cb 100644 --- a/include/powermanager/PowerHalLoader.h +++ b/include/powermanager/PowerHalLoader.h @@ -17,11 +17,11 @@ #ifndef ANDROID_POWERHALLOADER_H #define ANDROID_POWERHALLOADER_H +#include #include #include #include #include -#include namespace android { @@ -31,7 +31,7 @@ namespace power { class PowerHalLoader { public: static void unloadAll(); - static sp loadAidl(); + static std::shared_ptr loadAidl(); static sp loadHidlV1_0(); static sp loadHidlV1_1(); static sp loadHidlV1_2(); @@ -39,7 +39,7 @@ public: private: static std::mutex gHalMutex; - static sp gHalAidl GUARDED_BY(gHalMutex); + static std::shared_ptr gHalAidl GUARDED_BY(gHalMutex); static sp gHalHidlV1_0 GUARDED_BY(gHalMutex); static sp gHalHidlV1_1 GUARDED_BY(gHalMutex); static sp gHalHidlV1_2 GUARDED_BY(gHalMutex); diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index 8028aa86e1455c3ca8ca7aac94e71d6bc27ae626..4e4a1b000d28d8615e46c19721452c6d753c28f2 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -17,14 +17,15 @@ #ifndef ANDROID_POWERHALWRAPPER_H #define ANDROID_POWERHALWRAPPER_H +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include +#include namespace android { @@ -47,7 +48,7 @@ public: } static HalResult unsupported() { return HalResult("", /* unsupported= */ true); } - static HalResult fromStatus(binder::Status status, T data) { + static HalResult fromStatus(const binder::Status& status, T data) { if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { return HalResult::unsupported(); } @@ -56,14 +57,28 @@ public: } return HalResult::failed(std::string(status.toString8().c_str())); } - static HalResult fromStatus(hardware::power::V1_0::Status status, T data); + + static HalResult fromStatus(const ndk::ScopedAStatus& status, T data) { + if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult::unsupported(); + } + if (status.isOk()) { + return HalResult::ok(data); + } + return HalResult::failed(std::string(status.getDescription())); + } template - static HalResult fromReturn(hardware::Return& ret, T data); + static HalResult fromReturn(hardware::Return& ret, T data) { + return ret.isOk() ? HalResult::ok(data) : HalResult::failed(ret.description()); + } template static HalResult fromReturn(hardware::Return& ret, hardware::power::V1_0::Status status, - T data); + T data) { + return ret.isOk() ? HalResult::fromStatus(status, data) + : HalResult::failed(ret.description()); + } // This will throw std::bad_optional_access if this result is not ok. const T& value() const { return mValue.value(); } @@ -91,12 +106,30 @@ public: static HalResult failed(std::string msg) { return HalResult(std::move(msg)); } static HalResult unsupported() { return HalResult(/* unsupported= */ true); } - static HalResult fromStatus(status_t status); - static HalResult fromStatus(binder::Status status); - static HalResult fromStatus(hardware::power::V1_0::Status status); + static HalResult fromStatus(const binder::Status& status) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult::unsupported(); + } + if (status.isOk()) { + return HalResult::ok(); + } + return HalResult::failed(std::string(status.toString8().c_str())); + } + + static HalResult fromStatus(const ndk::ScopedAStatus& status) { + if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult::unsupported(); + } + if (status.isOk()) { + return HalResult::ok(); + } + return HalResult::failed(std::string(status.getDescription())); + } template - static HalResult fromReturn(hardware::Return& ret); + static HalResult fromReturn(hardware::Return& ret) { + return ret.isOk() ? HalResult::ok() : HalResult::failed(ret.description()); + } bool isOk() const { return !mUnsupported && !mFailed; } bool isFailed() const { return !mUnsupported && mFailed; } @@ -119,11 +152,12 @@ class HalWrapper { public: virtual ~HalWrapper() = default; - virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0; - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0; - virtual HalResult> createHintSession( - int32_t tgid, int32_t uid, const std::vector& threadIds, - int64_t durationNanos) = 0; + virtual HalResult setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) = 0; + virtual HalResult setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0; + virtual HalResult> + createHintSession(int32_t tgid, int32_t uid, const std::vector& threadIds, + int64_t durationNanos) = 0; virtual HalResult getHintSessionPreferredRate() = 0; }; @@ -131,14 +165,15 @@ public: class EmptyHalWrapper : public HalWrapper { public: EmptyHalWrapper() = default; - ~EmptyHalWrapper() = default; + ~EmptyHalWrapper() override = default; - virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult> createHintSession( + HalResult setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult> createHintSession( int32_t tgid, int32_t uid, const std::vector& threadIds, int64_t durationNanos) override; - virtual HalResult getHintSessionPreferredRate() override; + HalResult getHintSessionPreferredRate() override; }; // Wrapper for the HIDL Power HAL v1.0. @@ -146,14 +181,15 @@ class HidlHalWrapperV1_0 : public HalWrapper { public: explicit HidlHalWrapperV1_0(sp handleV1_0) : mHandleV1_0(std::move(handleV1_0)) {} - virtual ~HidlHalWrapperV1_0() = default; + ~HidlHalWrapperV1_0() override = default; - virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult> createHintSession( + HalResult setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult> createHintSession( int32_t tgid, int32_t uid, const std::vector& threadIds, int64_t durationNanos) override; - virtual HalResult getHintSessionPreferredRate() override; + HalResult getHintSessionPreferredRate() override; protected: const sp mHandleV1_0; @@ -167,67 +203,71 @@ private: // Wrapper for the HIDL Power HAL v1.1. class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 { public: - HidlHalWrapperV1_1(sp handleV1_1) + explicit HidlHalWrapperV1_1(sp handleV1_1) : HidlHalWrapperV1_0(std::move(handleV1_1)) {} - virtual ~HidlHalWrapperV1_1() = default; + ~HidlHalWrapperV1_1() override = default; protected: - virtual HalResult sendPowerHint(hardware::power::V1_3::PowerHint hintId, - uint32_t data) override; + HalResult sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; }; // Wrapper for the HIDL Power HAL v1.2. class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 { public: - virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; - HidlHalWrapperV1_2(sp handleV1_2) + HalResult setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + explicit HidlHalWrapperV1_2(sp handleV1_2) : HidlHalWrapperV1_1(std::move(handleV1_2)) {} - virtual ~HidlHalWrapperV1_2() = default; + ~HidlHalWrapperV1_2() override = default; protected: - virtual HalResult sendPowerHint(hardware::power::V1_3::PowerHint hintId, - uint32_t data) override; + HalResult sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; }; // Wrapper for the HIDL Power HAL v1.3. class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 { public: - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; - HidlHalWrapperV1_3(sp handleV1_3) + HalResult setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + explicit HidlHalWrapperV1_3(sp handleV1_3) : HidlHalWrapperV1_2(std::move(handleV1_3)) {} - virtual ~HidlHalWrapperV1_3() = default; + ~HidlHalWrapperV1_3() override = default; protected: - virtual HalResult sendPowerHint(hardware::power::V1_3::PowerHint hintId, - uint32_t data) override; + HalResult sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; }; // Wrapper for the AIDL Power HAL. class AidlHalWrapper : public HalWrapper { public: - explicit AidlHalWrapper(sp handle) : mHandle(std::move(handle)) {} - virtual ~AidlHalWrapper() = default; - - virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult> createHintSession( + explicit AidlHalWrapper(std::shared_ptr handle) + : mHandle(std::move(handle)) {} + ~AidlHalWrapper() override = default; + + HalResult setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult> createHintSession( int32_t tgid, int32_t uid, const std::vector& threadIds, int64_t durationNanos) override; - virtual HalResult getHintSessionPreferredRate() override; + HalResult getHintSessionPreferredRate() override; private: // Control access to the boost and mode supported arrays. std::mutex mBoostMutex; std::mutex mModeMutex; - sp mHandle; + std::shared_ptr mHandle; // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT. // Need to increase the array size if more boost supported. - std::array, - static_cast(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1> + std::array< + std::atomic, + static_cast(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + + 1> mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN}; std::array, - static_cast(*(android::enum_range().end() - 1)) + 1> + static_cast( + *(ndk::enum_range().end() - 1)) + + 1> mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN}; }; diff --git a/cmds/installd/view_compiler.h b/include/private/thermal_private.h similarity index 62% rename from cmds/installd/view_compiler.h rename to include/private/thermal_private.h index aa141ca2575f1324474c9323b2b9ce5d74a8ba0c..951d9532677f2dacc17dfa41a2d254db76dfba93 100644 --- a/cmds/installd/view_compiler.h +++ b/include/private/thermal_private.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2023 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. @@ -14,16 +14,18 @@ * limitations under the License. */ -#ifndef VIEW_COMPILER_H_ -#define VIEW_COMPILER_H_ +#ifndef ANDROID_PRIVATE_NATIVE_THERMAL_H +#define ANDROID_PRIVATE_NATIVE_THERMAL_H -namespace android { -namespace installd { +#include -bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, - int uid); +__BEGIN_DECLS -} // namespace installd -} // namespace android +/** + * For testing only. + */ +void AThermal_setIThermalServiceForTesting(void* iThermalService); + +__END_DECLS -#endif // VIEW_COMPILER_H_ +#endif // ANDROID_PRIVATE_NATIVE_THERMAL_H \ No newline at end of file diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 5e539f24e15a987981b952db4cdad708929996a6..1a9766d72ea5f14b0e090ea1490101a5e92aaf80 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -72,6 +72,7 @@ cc_library_static { "//apex_available:platform", "com.android.media", "com.android.media.swcodec", + "com.android.neuralnetworks", ], } diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index aca5009148a771f20e5382cf65773d73b609e1ec..526427663b0017439f60fac47294965f7ff9b02e 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -33,27 +34,36 @@ ActivityManager::ActivityManager() sp ActivityManager::getService() { std::lock_guard scoped_lock(mLock); - int64_t startTime = 0; sp service = mService; - while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { - sp binder = defaultServiceManager()->checkService(String16("activity")); - if (binder == nullptr) { - // Wait for the activity service to come back... - if (startTime == 0) { - startTime = uptimeMillis(); - ALOGI("Waiting for activity service"); - } else if ((uptimeMillis() - startTime) > 1000000) { - ALOGW("Waiting too long for activity service, giving up"); - service = nullptr; - break; - } - usleep(25000); - } else { + if (ProcessState::self()->isThreadPoolStarted()) { + if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp binder = defaultServiceManager()->waitForService(String16("activity")); service = interface_cast(binder); mService = service; } + } else { + ALOGI("Thread pool not started. Polling for activity service."); + int64_t startTime = 0; + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp binder = defaultServiceManager()->checkService(String16("activity")); + if (binder == nullptr) { + // Wait for the activity service to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + ALOGI("Waiting for activity service"); + } else if ((uptimeMillis() - startTime) > 1000000) { + ALOGW("Waiting too long for activity service, giving up"); + service = nullptr; + break; + } + usleep(25000); + } else { + service = interface_cast(binder); + mService = service; + } + } } - return service; + return mService; } int ActivityManager::openContentUri(const String16& stringUri) diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 49dd9c7366ceea3fed9752ebe1690682322c8172..bf3699ccabb7bd1291c0b3e16fae2bd4c3f16ccd 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -22,23 +22,51 @@ package { } cc_library_headers { - name: "libbinder_headers", + name: "libbinder_headers_base", export_include_dirs: ["include"], vendor_available: true, recovery_available: true, host_supported: true, - // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, header_libs: [ - "libbase_headers", "libbinder_headers_platform_shared", + ], + export_header_lib_headers: [ + "libbinder_headers_platform_shared", + ], + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], + min_sdk_version: "29", + target: { + darwin: { + enabled: false, + }, + }, + visibility: [ + ":__subpackages__", + ], +} + +cc_library_headers { + name: "libbinder_headers", + vendor_available: true, + recovery_available: true, + host_supported: true, + native_bridge_supported: true, + + header_libs: [ + "libbase_headers", + "libbinder_headers_base", "libcutils_headers", "libutils_headers", ], export_header_lib_headers: [ "libbase_headers", - "libbinder_headers_platform_shared", + "libbinder_headers_base", "libcutils_headers", "libutils_headers", ], @@ -87,24 +115,27 @@ cc_defaults { "RpcSession.cpp", "RpcServer.cpp", "RpcState.cpp", + "RpcTransportRaw.cpp", "Stability.cpp", "Status.cpp", "TextOutput.cpp", - "Trace.cpp", "Utils.cpp", + "file.cpp", ], - shared_libs: [ - "libcutils", - "libutils", - ], - - static_libs: [ - "libbase", + header_libs: [ + "libbinder_headers_base", ], - header_libs: [ - "libbinder_headers", + cflags: [ + "-Wextra", + "-Wextra-semi", + "-Werror", + "-Wzero-as-null-pointer-constant", + "-Wreorder-init-list", + "-Wunused-const-variable", + "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], } @@ -118,8 +149,8 @@ cc_defaults { ], srcs: [ - "OS.cpp", - "RpcTransportRaw.cpp", + "OS_android.cpp", + "OS_unix_base.cpp", ], target: { @@ -134,20 +165,7 @@ cc_defaults { export_aidl_headers: true, }, - cflags: [ - "-Wextra", - "-Wextra-semi", - "-Werror", - "-Wzero-as-null-pointer-constant", - "-Wreorder-init-list", - "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", - "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", - ], product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], - }, - debuggable: { cflags: [ "-DBINDER_RPC_DEV_SERVERS", @@ -157,11 +175,18 @@ cc_defaults { }, shared_libs: [ + "libcutils", "liblog", + "libutils", + ], + + static_libs: [ + "libbase", ], header_libs: [ "jni_headers", + "libbinder_headers", ], export_header_lib_headers: [ @@ -194,6 +219,9 @@ cc_defaults { "-performance-move-const-arg", // b/273486801 "portability*", ], + lto: { + thin: true, + }, } cc_library_headers { @@ -215,11 +243,21 @@ cc_defaults { host_supported: true, header_libs: [ + "libbinder_headers_base", + "liblog_stub", "trusty_mock_headers", ], + shared_libs: [ + "libutils_binder_sdk", + ], + cflags: [ "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_ENABLE_LIBLOG_ASSERT", + "-DBINDER_DISABLE_NATIVE_HANDLE", + "-DBINDER_DISABLE_BLOB", + "-DBINDER_NO_LIBBASE", // Trusty libbinder uses vendor stability for its binders "-D__ANDROID_VNDK__", "-U__ANDROID__", @@ -250,7 +288,6 @@ cc_library_shared { srcs: [ // Trusty-specific files - "trusty/logging.cpp", "trusty/OS.cpp", "trusty/RpcServerTrusty.cpp", "trusty/RpcTransportTipcTrusty.cpp", @@ -285,14 +322,6 @@ cc_defaults { cflags: [ "-DBINDER_WITH_KERNEL_IPC", ], - arch: { - // TODO(b/254713216): undefined symbol in BufferedTextOutput::getBuffer - riscv64: { - lto: { - thin: false, - }, - }, - }, } cc_library { @@ -353,6 +382,44 @@ cc_library { afdo: true, } +cc_library_host_shared { + name: "libbinder_sdk", + + defaults: [ + "libbinder_common_defaults", + ], + + shared_libs: [ + "libutils_binder_sdk", + ], + + cflags: [ + "-DBINDER_ENABLE_LIBLOG_ASSERT", + "-DBINDER_DISABLE_NATIVE_HANDLE", + "-DBINDER_DISABLE_BLOB", + "-DBINDER_NO_LIBBASE", + ], + + header_libs: [ + "liblog_stub", + ], + + srcs: [ + "OS_non_android_linux.cpp", + "OS_unix_base.cpp", + ], + + visibility: [ + ":__subpackages__", + ], + + target: { + windows: { + enabled: false, + }, + }, +} + cc_library_static { name: "libbinder_rpc_no_kernel", vendor_available: true, @@ -365,6 +432,38 @@ cc_library_static { ], } +cc_library_static { + name: "libbinder_rpc_no_blob", + vendor_available: true, + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + "libbinder_kernel_defaults", + ], + cflags: [ + "-DBINDER_DISABLE_BLOB", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_no_native_handle", + vendor_available: true, + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + "libbinder_kernel_defaults", + ], + cflags: [ + "-DBINDER_DISABLE_NATIVE_HANDLE", + ], + visibility: [ + ":__subpackages__", + ], +} + cc_library_static { name: "libbinder_rpc_single_threaded", defaults: [ @@ -531,7 +630,6 @@ cc_library { "libbase", "libbinder", "libbinder_ndk", - "libcutils_sockets", "liblog", "libutils", ], @@ -548,16 +646,12 @@ cc_library { ":__subpackages__", "//packages/modules/Virtualization/javalib/jni", "//packages/modules/Virtualization/vm_payload", + "//packages/modules/Virtualization/demo_native", "//device/google/cuttlefish/shared/minidroid:__subpackages__", "//system/software_defined_vehicle:__subpackages__", ], } -filegroup { - name: "libbinder_rpc_unstable_header", - srcs: ["include_rpc_unstable/binder_rpc_unstable.hpp"], -} - // libbinder historically contained additional interfaces that provided specific // functionality in the platform but have nothing to do with binder itself. These // are moved out of libbinder in order to avoid the overhead of their vtables. @@ -626,4 +720,7 @@ cc_binary { "libutils", "android.debug_aidl-cpp", ], + static_libs: [ + "libc++fs", + ], } diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 3e49656575c8de7144d4a3f51b8ce4a33471734b..c57c9cdd62d734265661aec87eb662bdc544722c 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -19,8 +19,6 @@ #include #include -#include -#include #include #include #include @@ -29,10 +27,8 @@ #include #include #include -#include -#include +#include #include -#include #include #include @@ -42,10 +38,15 @@ #endif #include "BuildFlags.h" +#include "OS.h" #include "RpcState.h" namespace android { +using android::binder::unique_fd; + +constexpr uid_t kUidRoot = 0; + // Service implementations inherit from BBinder and IBinder, and this is frozen // in prebuilts. #ifdef __LP64__ @@ -58,15 +59,15 @@ static_assert(sizeof(BBinder) == 20); // global b/c b/230079120 - consistent symbol table #ifdef BINDER_RPC_DEV_SERVERS -bool kEnableRpcDevServers = true; +constexpr bool kEnableRpcDevServers = true; #else -bool kEnableRpcDevServers = false; +constexpr bool kEnableRpcDevServers = false; #endif #ifdef BINDER_ENABLE_RECORDING -bool kEnableRecording = true; +constexpr bool kEnableRecording = true; #else -bool kEnableRecording = false; +constexpr bool kEnableRecording = false; #endif // Log any reply transactions for which the data exceeds this size @@ -168,8 +169,7 @@ status_t IBinder::getDebugPid(pid_t* out) { return OK; } -status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd, - const sp& keepAliveBinder) { +status_t IBinder::setRpcClientDebug(unique_fd socketFd, const sp& keepAliveBinder) { if (!kEnableRpcDevServers) { ALOGW("setRpcClientDebug disallowed because RPC is not enabled"); return INVALID_OPERATION; @@ -270,11 +270,11 @@ public: bool mInheritRt = false; // for below objects - Mutex mLock; + RpcMutex mLock; std::set> mRpcServerLinks; BpBinder::ObjectManager mObjects; - android::base::unique_fd mRecordingFd; + unique_fd mRecordingFd; }; // --------------------------------------------------------------------------- @@ -301,14 +301,14 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { return INVALID_OPERATION; } uid_t uid = IPCThreadState::self()->getCallingUid(); - if (uid != AID_ROOT) { + if (uid != kUidRoot) { ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid); return PERMISSION_DENIED; } Extras* e = getOrCreateExtras(); - AutoMutex lock(e->mLock); + RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { - LOG(INFO) << "Could not start Binder recording. Another is already in progress."; + ALOGI("Could not start Binder recording. Another is already in progress."); return INVALID_OPERATION; } else { status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd)); @@ -316,7 +316,7 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { return readStatus; } mRecordingOn = true; - LOG(INFO) << "Started Binder recording."; + ALOGI("Started Binder recording."); return NO_ERROR; } } @@ -331,19 +331,19 @@ status_t BBinder::stopRecordingTransactions() { return INVALID_OPERATION; } uid_t uid = IPCThreadState::self()->getCallingUid(); - if (uid != AID_ROOT) { + if (uid != kUidRoot) { ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid); return PERMISSION_DENIED; } Extras* e = getOrCreateExtras(); - AutoMutex lock(e->mLock); + RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { e->mRecordingFd.reset(); mRecordingOn = false; - LOG(INFO) << "Stopped Binder recording."; + ALOGI("Stopped Binder recording."); return NO_ERROR; } else { - LOG(INFO) << "Could not stop Binder recording. One is not in progress."; + ALOGI("Could not stop Binder recording. One is not in progress."); return INVALID_OPERATION; } } @@ -377,11 +377,11 @@ status_t BBinder::transact( err = stopRecordingTransactions(); break; case EXTENSION_TRANSACTION: - CHECK(reply != nullptr); + LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr"); err = reply->writeStrongBinder(getExtension()); break; case DEBUG_PID_TRANSACTION: - CHECK(reply != nullptr); + LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr"); err = reply->writeInt32(getDebugPid()); break; case SET_RPC_CLIENT_TRANSACTION: { @@ -402,9 +402,9 @@ status_t BBinder::transact( } } - if (CC_UNLIKELY(kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION)) { + if (kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION) [[unlikely]] { Extras* e = mExtras.load(std::memory_order_acquire); - AutoMutex lock(e->mLock); + RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { Parcel emptyReply; timespec ts; @@ -414,10 +414,10 @@ status_t BBinder::transact( reply ? *reply : emptyReply, err); if (transaction) { if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) { - LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err; + ALOGI("Failed to dump RecordedTransaction to file with error %d", err); } } else { - LOG(INFO) << "Failed to create RecordedTransaction object."; + ALOGI("Failed to create RecordedTransaction object."); } } } @@ -451,7 +451,7 @@ void* BBinder::attachObject(const void* objectID, void* object, void* cleanupCoo Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.attach(objectID, object, cleanupCookie, func); } @@ -460,7 +460,7 @@ void* BBinder::findObject(const void* objectID) const Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return nullptr; - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.find(objectID); } @@ -468,7 +468,7 @@ void* BBinder::detachObject(const void* objectID) { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return nullptr; - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.detach(objectID); } @@ -476,7 +476,7 @@ void BBinder::withLock(const std::function& doWithLock) { Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); doWithLock(); } @@ -484,7 +484,7 @@ sp BBinder::lookupOrCreateWeak(const void* objectID, object_make_func m const void* makeArgs) { Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs); } @@ -635,13 +635,13 @@ status_t BBinder::setRpcClientDebug(const Parcel& data) { return INVALID_OPERATION; } uid_t uid = IPCThreadState::self()->getCallingUid(); - if (uid != AID_ROOT) { + if (uid != kUidRoot) { ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid); return PERMISSION_DENIED; } status_t status; bool hasSocketFd; - android::base::unique_fd clientFd; + unique_fd clientFd; if (status = data.readBool(&hasSocketFd); status != OK) return status; if (hasSocketFd) { @@ -653,8 +653,7 @@ status_t BBinder::setRpcClientDebug(const Parcel& data) { return setRpcClientDebug(std::move(clientFd), keepAliveBinder); } -status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, - const sp& keepAliveBinder) { +status_t BBinder::setRpcClientDebug(unique_fd socketFd, const sp& keepAliveBinder) { if (!kEnableRpcDevServers) { ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; @@ -691,7 +690,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, auto weakThis = wp::fromExisting(this); Extras* e = getOrCreateExtras(); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); auto rpcServer = RpcServer::make(); LOG_ALWAYS_FATAL_IF(rpcServer == nullptr, "RpcServer::make returns null"); auto link = sp::make(rpcServer, keepAliveBinder, weakThis); @@ -705,7 +704,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, return status; } rpcServer->setMaxThreads(binderThreadPoolMaxCount); - LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor(); + ALOGI("RpcBinder: Started Binder debug on %s", String8(getInterfaceDescriptor()).c_str()); rpcServer->start(); e->mRpcServerLinks.emplace(link); LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint); @@ -715,7 +714,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, void BBinder::removeRpcServerLink(const sp& link) { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return; - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); (void)e->mRpcServerLinks.erase(link); } @@ -723,20 +722,20 @@ BBinder::~BBinder() { if (!wasParceled()) { if (getExtension()) { - ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + ALOGW("Binder %p destroyed with extension attached before being parceled.", this); } if (isRequestingSid()) { - ALOGW("Binder %p destroyed when requesting SID before being parceled.", this); + ALOGW("Binder %p destroyed when requesting SID before being parceled.", this); } if (isInheritRt()) { - ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this); + ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this); } #ifdef __linux__ if (getMinSchedulerPolicy() != SCHED_NORMAL) { - ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); } if (getMinSchedulerPriority() != 0) { - ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); } #endif // __linux__ } @@ -752,7 +751,7 @@ status_t BBinder::onTransact( { switch (code) { case INTERFACE_TRANSACTION: - CHECK(reply != nullptr); + LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr"); reply->writeString16(getInterfaceDescriptor()); return NO_ERROR; @@ -795,7 +794,7 @@ status_t BBinder::onTransact( } case SYSPROPS_TRANSACTION: { - report_sysprop_change(); + if (!binder::os::report_sysprop_change()) return INVALID_OPERATION; return NO_ERROR; } diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 8d9955dd6becd0cf926cf19fb2d973874569443d..42dd6916c7eca1f3c4e911e8b1d83a3220516ff0 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -23,23 +23,22 @@ #include #include #include -#include -#include #include #include "BuildFlags.h" - -#include +#include "file.h" //#undef ALOGV //#define ALOGV(...) fprintf(stderr, __VA_ARGS__) namespace android { +using android::binder::unique_fd; + // --------------------------------------------------------------------------- -Mutex BpBinder::sTrackingLock; +RpcMutex BpBinder::sTrackingLock; std::unordered_map BpBinder::sTrackingMap; std::unordered_map BpBinder::sLastLimitCallbackMap; int BpBinder::sNumTrackedUids = 0; @@ -54,6 +53,11 @@ uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; // Another arbitrary value a binder count needs to drop below before another callback will be called uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000; +std::atomic BpBinder::sBinderProxyCount(0); +std::atomic BpBinder::sBinderProxyCountWarned(0); + +static constexpr uint32_t kBinderProxyCountWarnInterval = 5000; + // Log any transactions for which the data exceeds this size #define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024) @@ -159,9 +163,9 @@ sp BpBinder::create(int32_t handle) { int32_t trackedUid = -1; if (sCountByUidEnabled) { trackedUid = IPCThreadState::self()->getCallingUid(); - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[trackedUid]; - if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) { + if (trackedValue & LIMIT_REACHED_MASK) [[unlikely]] { if (sBinderProxyThrottleCreate) { return nullptr; } @@ -193,6 +197,18 @@ sp BpBinder::create(int32_t handle) { } sTrackingMap[trackedUid]++; } + uint32_t numProxies = sBinderProxyCount.fetch_add(1, std::memory_order_relaxed); + uint32_t numLastWarned = sBinderProxyCountWarned.load(std::memory_order_relaxed); + uint32_t numNextWarn = numLastWarned + kBinderProxyCountWarnInterval; + if (numProxies >= numNextWarn) { + // Multiple threads can get here, make sure only one of them gets to + // update the warn counter. + if (sBinderProxyCountWarned.compare_exchange_strong(numLastWarned, + numNextWarn, + std::memory_order_relaxed)) { + ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies); + } + } return sp::make(BinderHandle{handle}, trackedUid); } @@ -260,8 +276,8 @@ std::optional BpBinder::getDebugBinderHandle() const { } bool BpBinder::isDescriptorCached() const { - Mutex::Autolock _l(mLock); - return mDescriptorCache.string() != kDescriptorUninit.string(); + RpcMutexUniqueLock _l(mLock); + return mDescriptorCache.c_str() != kDescriptorUninit.c_str(); } const String16& BpBinder::getInterfaceDescriptor() const @@ -276,10 +292,10 @@ const String16& BpBinder::getInterfaceDescriptor() const status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply); if (err == NO_ERROR) { String16 res(reply.readString16()); - Mutex::Autolock _l(mLock); + RpcMutexUniqueLock _l(mLock); // mDescriptorCache could have been assigned while the lock was // released. - if (mDescriptorCache.string() == kDescriptorUninit.string()) mDescriptorCache = res; + if (mDescriptorCache.c_str() == kDescriptorUninit.c_str()) mDescriptorCache = res; } } @@ -303,7 +319,7 @@ status_t BpBinder::pingBinder() return transact(PING_TRANSACTION, data, &reply); } -status_t BpBinder::startRecordingBinder(const android::base::unique_fd& fd) { +status_t BpBinder::startRecordingBinder(const unique_fd& fd) { Parcel send, reply; send.writeUniqueFileDescriptor(fd); return transact(START_RECORDING_TRANSACTION, send, &reply); @@ -347,7 +363,7 @@ status_t BpBinder::transact( Stability::Level required = privateVendor ? Stability::VENDOR : Stability::getLocalLevel(); - if (CC_UNLIKELY(!Stability::check(stability, required))) { + if (!Stability::check(stability, required)) [[unlikely]] { ALOGE("Cannot do a user transaction on a %s binder (%s) in a %s context.", Stability::levelString(stability).c_str(), String8(getInterfaceDescriptor()).c_str(), @@ -357,7 +373,7 @@ status_t BpBinder::transact( } status_t status; - if (CC_UNLIKELY(isRpcBinder())) { + if (isRpcBinder()) [[unlikely]] { status = rpcSession()->transact(sp::fromExisting(this), code, data, reply, flags); } else { @@ -369,7 +385,7 @@ status_t BpBinder::transact( status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { - Mutex::Autolock _l(mLock); + RpcMutexUniqueLock _l(mLock); ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d", data.dataSize(), String8(mDescriptorCache).c_str(), code); } @@ -415,7 +431,7 @@ status_t BpBinder::linkToDeath( "linkToDeath(): recipient must be non-NULL"); { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); if (!mObitsSent) { if (!mObituaries) { @@ -451,7 +467,7 @@ status_t BpBinder::unlinkToDeath( return INVALID_OPERATION; } - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); if (mObitsSent) { return DEAD_OBJECT; @@ -539,30 +555,30 @@ void BpBinder::reportOneDeath(const Obituary& obit) void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); return mObjects.attach(objectID, object, cleanupCookie, func); } void* BpBinder::findObject(const void* objectID) const { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); return mObjects.find(objectID); } void* BpBinder::detachObject(const void* objectID) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); return mObjects.detach(objectID); } void BpBinder::withLock(const std::function& doWithLock) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); doWithLock(); } sp BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, const void* makeArgs) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); return mObjects.lookupOrCreateWeak(objectID, make, makeArgs); } @@ -572,7 +588,9 @@ BpBinder* BpBinder::remoteBinder() } BpBinder::~BpBinder() { - if (CC_UNLIKELY(isRpcBinder())) return; + if (isRpcBinder()) [[unlikely]] { + return; + } if constexpr (!kEnableKernelIpc) { LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); @@ -584,16 +602,15 @@ BpBinder::~BpBinder() { IPCThreadState* ipc = IPCThreadState::self(); if (mTrackedUid >= 0) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[mTrackedUid]; - if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) { + if ((trackedValue & COUNTING_VALUE_MASK) == 0) [[unlikely]] { ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, binderHandle()); } else { - if (CC_UNLIKELY( - (trackedValue & LIMIT_REACHED_MASK) && - ((trackedValue & COUNTING_VALUE_MASK) <= sBinderProxyCountLowWatermark) - )) { + auto countingValue = trackedValue & COUNTING_VALUE_MASK; + if ((trackedValue & LIMIT_REACHED_MASK) && + (countingValue <= sBinderProxyCountLowWatermark)) [[unlikely]] { ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)", getuid(), sBinderProxyCountLowWatermark, mTrackedUid); sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK; @@ -604,6 +621,7 @@ BpBinder::~BpBinder() { } } } + --sBinderProxyCount; if (ipc) { ipc->expungeHandle(binderHandle(), this); @@ -612,7 +630,9 @@ BpBinder::~BpBinder() { } void BpBinder::onFirstRef() { - if (CC_UNLIKELY(isRpcBinder())) return; + if (isRpcBinder()) [[unlikely]] { + return; + } if constexpr (!kEnableKernelIpc) { LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); @@ -625,7 +645,7 @@ void BpBinder::onFirstRef() { } void BpBinder::onLastStrongRef(const void* /*id*/) { - if (CC_UNLIKELY(isRpcBinder())) { + if (isRpcBinder()) [[unlikely]] { (void)rpcSession()->sendDecStrong(this); return; } @@ -666,7 +686,9 @@ void BpBinder::onLastStrongRef(const void* /*id*/) { bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) { // RPC binder doesn't currently support inc from weak binders - if (CC_UNLIKELY(isRpcBinder())) return false; + if (isRpcBinder()) [[unlikely]] { + return false; + } if constexpr (!kEnableKernelIpc) { LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); @@ -680,7 +702,7 @@ bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) uint32_t BpBinder::getBinderProxyCount(uint32_t uid) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); auto it = sTrackingMap.find(uid); if (it != sTrackingMap.end()) { return it->second & COUNTING_VALUE_MASK; @@ -688,9 +710,14 @@ uint32_t BpBinder::getBinderProxyCount(uint32_t uid) return 0; } +uint32_t BpBinder::getBinderProxyCount() +{ + return sBinderProxyCount.load(); +} + void BpBinder::getCountByUid(Vector& uids, Vector& counts) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); uids.setCapacity(sTrackingMap.size()); counts.setCapacity(sTrackingMap.size()); for (const auto& it : sTrackingMap) { @@ -704,12 +731,12 @@ void BpBinder::disableCountByUid() { sCountByUidEnabled.store(false); } void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); } void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); sLimitCallback = cb; } void BpBinder::setBinderProxyCountWatermarks(int high, int low) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); sBinderProxyCountHighWatermark = high; sBinderProxyCountLowWatermark = low; } diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index c6e4fb378d118fbbf72c7e5bfb794dbd8f9b7e34..7ae616e2b0bc4eace431e42a0e49f33b4d90d851 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -19,8 +19,6 @@ #include -#include - #include #include #include diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 8ee6cb0615cc92c17b0f102cf457f6f0568b47ea..455a4338e5b892c4c1bfb04a4fdb86976e1a0ef2 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -21,16 +21,20 @@ #include -#include -#include +#include +#include "FdUtils.h" #include "RpcState.h" +#include "Utils.h" + namespace android { +using namespace android::binder::impl; + std::unique_ptr FdTrigger::make() { auto ret = std::make_unique(); #ifndef BINDER_RPC_SINGLE_THREADED - if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) { + if (!binder::Pipe(&ret->mRead, &ret->mWrite)) { ALOGE("Could not create pipe %s", strerror(errno)); return nullptr; } @@ -50,7 +54,7 @@ bool FdTrigger::isTriggered() { #ifdef BINDER_RPC_SINGLE_THREADED return mTriggered; #else - return mWrite == -1; + return !mWrite.ok(); #endif } @@ -74,10 +78,9 @@ status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, "Only one thread should be polling on Fd!"); transportFd.setPollingState(true); - auto pollingStateGuard = - android::base::make_scope_guard([&]() { transportFd.setPollingState(false); }); + auto pollingStateGuard = make_scope_guard([&]() { transportFd.setPollingState(false); }); - int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); + int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1)); if (ret < 0) { return -errno; } diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h index 5fbf2908ad2fc0c5e5ac653886dd2111f5a1c600..e4a02839dcbedd69a624b55f06c93cecfb73a968 100644 --- a/libs/binder/FdTrigger.h +++ b/libs/binder/FdTrigger.h @@ -17,11 +17,10 @@ #include -#include -#include #include #include +#include namespace android { @@ -62,8 +61,8 @@ private: #ifdef BINDER_RPC_SINGLE_THREADED bool mTriggered = false; #else - base::unique_fd mWrite; - base::unique_fd mRead; + binder::unique_fd mWrite; + binder::unique_fd mRead; #endif }; } // namespace android diff --git a/libs/binder/FdUtils.h b/libs/binder/FdUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..52ae48728f16e6d1f36b5a275a5e08caa4624f4b --- /dev/null +++ b/libs/binder/FdUtils.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 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 + +#if defined(_WIN32) || defined(__TRUSTY__) +// Pipe and Socketpair are missing there +#elif !defined(BINDER_NO_LIBBASE) + +namespace android::binder { +using android::base::Pipe; +using android::base::Socketpair; +} // namespace android::binder + +#else // BINDER_NO_LIBBASE + +#include + +namespace android::binder { + +// Inline functions, so that they can be used header-only. + +// See pipe(2). +// This helper hides the details of converting to unique_fd, and also hides the +// fact that macOS doesn't support O_CLOEXEC or O_NONBLOCK directly. +inline bool Pipe(unique_fd* read, unique_fd* write, int flags = O_CLOEXEC) { + int pipefd[2]; + +#if defined(__APPLE__) + if (flags & ~(O_CLOEXEC | O_NONBLOCK)) { + return false; + } + if (pipe(pipefd) != 0) { + return false; + } + + if (flags & O_CLOEXEC) { + if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || + fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } + } + if (flags & O_NONBLOCK) { + if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || + fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } + } +#else + if (pipe2(pipefd, flags) != 0) { + return false; + } +#endif + + read->reset(pipefd[0]); + write->reset(pipefd[1]); + return true; +} + +// See socketpair(2). +// This helper hides the details of converting to unique_fd. +inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) { + int sockfd[2]; + if (socketpair(domain, type, protocol, sockfd) != 0) { + return false; + } + left->reset(sockfd[0]); + right->reset(sockfd[1]); + return true; +} + +// See socketpair(2). +// This helper hides the details of converting to unique_fd. +inline bool Socketpair(int type, unique_fd* left, unique_fd* right) { + return Socketpair(AF_UNIX, type, 0, left, right); +} + +} // namespace android::binder + +#endif // BINDER_NO_LIBBASE diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 7d6ae00ed2f50cb6e52026c7bd081530e533fae4..152c815ec3facc94ff90a481018bfba8394a3957 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -52,8 +52,8 @@ public: } } else { // An exception was thrown back; fall through to return failure - ALOGD("openContentUri(%s) caught exception %d\n", - String8(stringUri).string(), exceptionCode); + ALOGD("openContentUri(%s) caught exception %d\n", String8(stringUri).c_str(), + exceptionCode); } } return fd; diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp index 2780bd4cd967df2ca989d45cb9f4187996d4cf6a..dea26038cf3fae6887f2de62d0459cd967b44dc4 100644 --- a/libs/binder/IInterface.cpp +++ b/libs/binder/IInterface.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "IInterface" -#include #include namespace android { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index da582511492bc17f7a2c92b89b664b674440be7c..9341eff91e716e4f19bd9f4769d6a7d1bcaf3ca8 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -395,7 +394,9 @@ void IPCThreadState::restoreGetCallingSpGuard(const SpGuard* guard) { } void IPCThreadState::checkContextIsBinderForUse(const char* use) const { - if (LIKELY(mServingStackPointerGuard == nullptr)) return; + if (mServingStackPointerGuard == nullptr) [[likely]] { + return; + } if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) { LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).", @@ -832,7 +833,7 @@ status_t IPCThreadState::transact(int32_t handle, } if ((flags & TF_ONE_WAY) == 0) { - if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) { + if (mCallRestriction != ProcessState::CallRestriction::NONE) [[unlikely]] { if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) { ALOGE("Process making non-oneway call (code: %u) but is restricted.", code); CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), @@ -842,13 +843,13 @@ status_t IPCThreadState::transact(int32_t handle, } } - #if 0 +#if 0 if (code == 4) { // relayout ALOGI(">>>>>> CALLING transaction 4"); } else { ALOGI(">>>>>> CALLING transaction %d", code); } - #endif +#endif if (reply) { err = waitForResponse(reply); } else { diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp index cd92217f84157d005104f2a84adc9743439da170..60ece7269cd25477a4b6b41dcddc1aab39397820 100644 --- a/libs/binder/IResultReceiver.cpp +++ b/libs/binder/IResultReceiver.cpp @@ -18,7 +18,6 @@ #include -#include #include #include diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 24083074594e62f4888459131ed5b97232914316..fe566fccb202bee29bb9482b43592c0c5de3fc26 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -200,7 +200,7 @@ bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t } bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure) { - static Mutex gPermissionControllerLock; + static std::mutex gPermissionControllerLock; static sp gPermissionController; sp pc; @@ -216,8 +216,8 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logP if (res) { if (startTime != 0) { ALOGI("Check passed after %d seconds for %s from uid=%d pid=%d", - (int)((uptimeMillis()-startTime)/1000), - String8(permission).string(), uid, pid); + (int)((uptimeMillis() - startTime) / 1000), String8(permission).c_str(), + uid, pid); } return res; } @@ -225,7 +225,7 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logP // Is this a permission failure, or did the controller go away? if (IInterface::asBinder(pc)->isBinderAlive()) { if (logPermissionFailure) { - ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).string(), + ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).c_str(), uid, pid); } return false; @@ -246,7 +246,7 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logP if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting to check permission %s from uid=%d pid=%d", - String8(permission).string(), uid, pid); + String8(permission).c_str(), uid, pid); } sleep(1); } else { @@ -295,7 +295,7 @@ sp ServiceManagerShim::getService(const String16& name) const // retry interval in millisecond; note that vendor services stay at 100ms const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100; - ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), + ALOGI("Waiting for service '%s' on '%s'...", String8(name).c_str(), ProcessState::self()->getDriverName().c_str()); int n = 0; @@ -306,12 +306,12 @@ sp ServiceManagerShim::getService(const String16& name) const sp svc = checkService(name); if (svc != nullptr) { ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms", - String8(name).string(), ProcessState::self()->getDriverName().c_str(), + String8(name).c_str(), ProcessState::self()->getDriverName().c_str(), uptimeMillis() - startTime); return svc; } } - ALOGW("Service %s didn't start. Returning NULL", String8(name).string()); + ALOGW("Service %s didn't start. Returning NULL", String8(name).c_str()); return nullptr; } diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index f66993f9266e9285aa6b2d242d8ba489c17b35d0..7644806e2b1493bebfd369640efcb7d035065b6c 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -324,6 +324,10 @@ LazyServiceRegistrar& LazyServiceRegistrar::getInstance() { return *registrarInstance; } +LazyServiceRegistrar LazyServiceRegistrar::createExtraTestInstance() { + return LazyServiceRegistrar(); +} + status_t LazyServiceRegistrar::registerService(const sp& service, const std::string& name, bool allowIsolated, int dumpFlags) { if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) { diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index 03553f36e9d626c37f678a304ac377e0c4246be7..95bdbb4b65dd63421c1ba4f8a70a25739428ac07 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -155,7 +155,7 @@ private: void dump_l(String8& res, const char* what) const; static const int kMemoryAlign; - mutable Mutex mLock; + mutable std::mutex mLock; LinkedList mList; size_t mHeapSize; }; @@ -305,14 +305,14 @@ size_t SimpleBestFitAllocator::size() const size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) { - Mutex::Autolock _l(mLock); + std::unique_lock _l(mLock); ssize_t offset = alloc(size, flags); return offset; } status_t SimpleBestFitAllocator::deallocate(size_t offset) { - Mutex::Autolock _l(mLock); + std::unique_lock _l(mLock); chunk_t const * const freed = dealloc(offset); if (freed) { return NO_ERROR; @@ -420,7 +420,7 @@ SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) void SimpleBestFitAllocator::dump(const char* what) const { - Mutex::Autolock _l(mLock); + std::unique_lock _l(mLock); dump_l(what); } @@ -428,13 +428,13 @@ void SimpleBestFitAllocator::dump_l(const char* what) const { String8 result; dump_l(result, what); - ALOGD("%s", result.string()); + ALOGD("%s", result.c_str()); } void SimpleBestFitAllocator::dump(String8& result, const char* what) const { - Mutex::Autolock _l(mLock); + std::unique_lock _l(mLock); dump_l(result, what); } diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 34e747ef21f6593bb72768bb00cd58c56c68c595..fc273e0367385940774da8c1c88efc3b2038a2d0 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -78,7 +78,7 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) 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)); - munmap(mBase, mSize); + if (mNeedUnmap) munmap(mBase, mSize); mBase = nullptr; mSize = 0; close(fd); diff --git a/libs/binder/OS.h b/libs/binder/OS.h index fecae31763f61adf0ced21075b5be8e9ed7ec959..0035aeb2050620233c7a0d5941cd131db1e372a3 100644 --- a/libs/binder/OS.h +++ b/libs/binder/OS.h @@ -18,14 +18,16 @@ #include #include -#include -#include #include +#include #include -namespace android { +namespace android::binder::os { -android::base::Result setNonBlocking(android::base::borrowed_fd fd); +void trace_begin(uint64_t tag, const char* name); +void trace_end(uint64_t tag); + +status_t setNonBlocking(borrowed_fd fd); status_t getRandomBytes(uint8_t* data, size_t size); @@ -33,12 +35,14 @@ status_t dupFileDescriptor(int oldFd, int* newFd); std::unique_ptr makeDefaultRpcTransportCtxFactory(); -ssize_t sendMessageOnSocket( - const RpcTransportFd& socket, iovec* iovs, int niovs, - const std::vector>* ancillaryFds); +ssize_t sendMessageOnSocket(const RpcTransportFd& socket, iovec* iovs, int niovs, + const std::vector>* ancillaryFds); + +ssize_t receiveMessageFromSocket(const RpcTransportFd& socket, iovec* iovs, int niovs, + std::vector>* ancillaryFds); + +uint64_t GetThreadId(); -ssize_t receiveMessageFromSocket( - const RpcTransportFd& socket, iovec* iovs, int niovs, - std::vector>* ancillaryFds); +bool report_sysprop_change(); -} // namespace android +} // namespace android::binder::os diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1eace857fb504019c0a021e0cd72a5328ccfc62f --- /dev/null +++ b/libs/binder/OS_android.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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 +#include +#include + +namespace android::binder { +namespace os { + +uint64_t GetThreadId() { +#ifdef BINDER_RPC_SINGLE_THREADED + return 0; +#else + return base::GetThreadId(); +#endif +} + +bool report_sysprop_change() { + android::report_sysprop_change(); + return true; +} + +void trace_begin(uint64_t tag, const char* name) { + atrace_begin(tag, name); +} + +void trace_end(uint64_t tag) { + atrace_end(tag); +} + +} // namespace os + +// Legacy trace symbol. To be removed once all of downstream rebuilds. +void atrace_begin(uint64_t tag, const char* name) { + os::trace_begin(tag, name); +} + +// Legacy trace symbol. To be removed once all of downstream rebuilds. +void atrace_end(uint64_t tag) { + os::trace_end(tag); +} + +} // namespace android::binder diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b525d1ac34000b584d9bff5b5e368aa65ab49353 --- /dev/null +++ b/libs/binder/OS_non_android_linux.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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 + +#include +#include + +#ifdef __ANDROID__ +#error "This module is not intended for Android, just bare Linux" +#endif +#ifdef __APPLE__ +#error "This module is not intended for MacOS" +#endif +#ifdef _WIN32 +#error "This module is not intended for Windows" +#endif + +namespace android::binder::os { + +void trace_begin(uint64_t, const char*) {} + +void trace_end(uint64_t) {} + +uint64_t GetThreadId() { + return syscall(__NR_gettid); +} + +bool report_sysprop_change() { + return false; +} + +} // namespace android::binder::os + +int __android_log_print(int /*prio*/, const char* /*tag*/, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + return 1; +} diff --git a/libs/binder/OS.cpp b/libs/binder/OS_unix_base.cpp similarity index 82% rename from libs/binder/OS.cpp rename to libs/binder/OS_unix_base.cpp index ce60e33ba7f0c187630760562ce96889e66872c6..ca998d46d3bfcc0d9ad6fed6732e0936fadb250d 100644 --- a/libs/binder/OS.cpp +++ b/libs/binder/OS_unix_base.cpp @@ -15,39 +15,41 @@ */ #include "OS.h" +#include "Utils.h" +#include "file.h" -#include #include #include #include +#include -using android::base::ErrnoError; -using android::base::Result; +using android::binder::ReadFully; -namespace android { +namespace android::binder::os { // Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets. constexpr size_t kMaxFdsPerMsg = 253; -Result setNonBlocking(android::base::borrowed_fd fd) { +status_t setNonBlocking(borrowed_fd fd) { int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); if (flags == -1) { - return ErrnoError() << "Could not get flags for fd"; + PLOGE("Failed setNonBlocking: Could not get flags for fd"); + return -errno; } 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"; + PLOGE("Failed setNonBlocking: Could not set non-blocking flag for fd"); + return -errno; } - return {}; + return OK; } 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) { + unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW))); + if (!fd.ok()) { return -errno; } - base::unique_fd fd(ret); - if (!base::ReadFully(fd, data, size)) { + if (!ReadFully(fd, data, size)) { return -errno; } return OK; @@ -67,9 +69,8 @@ std::unique_ptr makeDefaultRpcTransportCtxFactory() { return RpcTransportCtxFactoryRaw::make(); } -ssize_t sendMessageOnSocket( - const RpcTransportFd& socket, iovec* iovs, int niovs, - const std::vector>* ancillaryFds) { +ssize_t sendMessageOnSocket(const RpcTransportFd& socket, iovec* iovs, int niovs, + const std::vector>* ancillaryFds) { if (ancillaryFds != nullptr && !ancillaryFds->empty()) { if (ancillaryFds->size() > kMaxFdsPerMsg) { errno = EINVAL; @@ -112,9 +113,8 @@ ssize_t sendMessageOnSocket( return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL)); } -ssize_t receiveMessageFromSocket( - const RpcTransportFd& socket, iovec* iovs, int niovs, - std::vector>* ancillaryFds) { +ssize_t receiveMessageFromSocket(const RpcTransportFd& socket, iovec* iovs, int niovs, + std::vector>* ancillaryFds) { if (ancillaryFds != nullptr) { int fdBuffer[kMaxFdsPerMsg]; alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))]; @@ -140,7 +140,7 @@ ssize_t receiveMessageFromSocket( 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])); + ancillaryFds->emplace_back(unique_fd(fdBuffer[i])); } break; } @@ -162,4 +162,4 @@ ssize_t receiveMessageFromSocket( return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL)); } -} // namespace android +} // namespace android::binder::os diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS index bb17683a23b98df310950cc25c98e91b2de52631..a70b55826f65f7d1705a50ae358e90a2565ddb17 100644 --- a/libs/binder/OWNERS +++ b/libs/binder/OWNERS @@ -1,4 +1,5 @@ # Bug component: 32456 -maco@google.com + smoreland@google.com -tkjos@google.com +tkjos@google.com # kernel +maco@google.com # historical diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 0aca163eabd6e02c4ff0a7f090f414dd72df738d..c1770b35d13595606eda5b45b8c539b7b1a9a839 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "Parcel" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -32,6 +33,7 @@ #include #include +#include #include #include #include @@ -39,14 +41,11 @@ #include #include -#include +#ifndef BINDER_DISABLE_BLOB #include -#include -#include -#include +#endif #include #include -#include #include "OS.h" #include "RpcState.h" @@ -69,6 +68,10 @@ typedef uintptr_t binder_uintptr_t; #endif // BINDER_WITH_KERNEL_IPC +#ifdef __BIONIC__ +#include +#endif + #define LOG_REFS(...) // #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOG_ALLOC(...) @@ -93,6 +96,10 @@ static size_t pad_size(size_t s) { namespace android { +using namespace android::binder::impl; +using binder::borrowed_fd; +using binder::unique_fd; + // many things compile this into prebuilts on the stack #ifdef __LP64__ static_assert(sizeof(Parcel) == 120); @@ -107,7 +114,38 @@ static std::atomic gParcelGlobalAllocSize; constexpr size_t kMaxFds = 1024; // Maximum size of a blob to transfer in-place. -static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; +[[maybe_unused]] static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; + +#if defined(__BIONIC__) +static void FdTag(int fd, const void* old_addr, const void* new_addr) { + if (android_fdsan_exchange_owner_tag) { + uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL, + reinterpret_cast(old_addr)); + uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL, + reinterpret_cast(new_addr)); + android_fdsan_exchange_owner_tag(fd, old_tag, new_tag); + } +} +static void FdTagClose(int fd, const void* addr) { + if (android_fdsan_close_with_tag) { + uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL, + reinterpret_cast(addr)); + android_fdsan_close_with_tag(fd, tag); + } else { + close(fd); + } +} +#else +static void FdTag(int fd, const void* old_addr, const void* new_addr) { + (void)fd; + (void)old_addr; + (void)new_addr; +} +static void FdTagClose(int fd, const void* addr) { + (void)addr; + close(fd); +} +#endif enum { BLOB_INPLACE = 0, @@ -134,6 +172,9 @@ static void acquire_object(const sp& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { + if (obj.cookie != 0) { // owned + FdTag(obj.handle, nullptr, who); + } return; } } @@ -159,8 +200,10 @@ static void release_object(const sp& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { + // note: this path is not used when mOwner, so the tag is also released + // in 'closeFileDescriptors' if (obj.cookie != 0) { // owned - close(obj.handle); + FdTagClose(obj.handle, who); } return; } @@ -170,7 +213,7 @@ static void release_object(const sp& proc, const flat_binder_objec } #endif // BINDER_WITH_KERNEL_IPC -static int toRawFd(const std::variant& v) { +static int toRawFd(const std::variant& v) { return std::visit([](const auto& fd) { return fd.get(); }, v); } @@ -554,7 +597,6 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { kernelFields->mObjectsSize++; flat_binder_object* flat = reinterpret_cast(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 @@ -567,6 +609,8 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { err = FDS_NOT_ALLOWED; } } + + acquire_object(proc, *flat, this); } } #else @@ -585,7 +629,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } const size_t savedDataPos = mDataPos; - base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; }; + auto scopeGuard = make_scope_guard([&]() { mDataPos = savedDataPos; }); rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size()); if (otherRpcFields->mFds != nullptr) { @@ -622,10 +666,10 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { // To match kernel binder behavior, we always dup, even if the // FD was unowned in the source parcel. int newFd = -1; - if (status_t status = dupFileDescriptor(oldFd, &newFd); status != OK) { + if (status_t status = binder::os::dupFileDescriptor(oldFd, &newFd); status != OK) { ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status)); } - rpcFields->mFds->emplace_back(base::unique_fd(newFd)); + rpcFields->mFds->emplace_back(unique_fd(newFd)); // Fixup the index in the data. mDataPos = newDataPos + 4; if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) { @@ -842,6 +886,9 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { } #ifdef BINDER_WITH_KERNEL_IPC + +#if defined(__ANDROID__) + #if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); #elif defined(__ANDROID_RECOVERY__) @@ -849,12 +896,20 @@ constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); #endif + +#else // ANDROID not defined + +// If kernel binder is used in new environments, we need to make sure it's separated +// out and has a separate header. +constexpr int32_t kHeader = B_PACK_CHARS('U', 'N', 'K', 'N'); +#endif + #endif // BINDER_WITH_KERNEL_IPC // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { - return writeInterfaceToken(interface.string(), interface.size()); + return writeInterfaceToken(interface.c_str(), interface.size()); } status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { @@ -918,7 +973,7 @@ bool Parcel::checkInterface(IBinder* binder) const bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { - return enforceInterface(interface.string(), interface.size(), threadState); + return enforceInterface(interface.c_str(), interface.size(), threadState); } bool Parcel::enforceInterface(const char16_t* interface, @@ -947,7 +1002,10 @@ bool Parcel::enforceInterface(const char16_t* interface, threadState->setCallingWorkSourceUidWithoutPropagation(workSource); // vendor header int32_t header = readInt32(); - if (header != kHeader) { + + // fuzzers skip this check, because it is for protecting the underlying ABI, but + // we don't want it to reduce our coverage + if (header != kHeader && !mServiceFuzzing) { ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header); return false; @@ -966,10 +1024,18 @@ bool Parcel::enforceInterface(const char16_t* interface, (!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) { return true; } else { - ALOGW("**** enforceInterface() expected '%s' but read '%s'", - String8(interface, len).string(), - String8(parcel_interface, parcel_interface_len).string()); - return false; + if (mServiceFuzzing) { + // ignore. Theoretically, this could cause a few false positives, because + // people could assume things about getInterfaceDescriptor if they pass + // this point, but it would be extremely fragile. It's more important that + // we fuzz with the above things read from the Parcel. + return true; + } else { + ALOGW("**** enforceInterface() expected '%s' but read '%s'", + String8(interface, len).c_str(), + String8(parcel_interface, parcel_interface_len).c_str()); + return false; + } } } @@ -977,6 +1043,14 @@ void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) { mEnforceNoDataAvail = enforceNoDataAvail; } +void Parcel::setServiceFuzzing() { + mServiceFuzzing = true; +} + +bool Parcel::isServiceFuzzing() const { + return mServiceFuzzing; +} + binder::Status Parcel::enforceNoDataAvail() const { if (!mEnforceNoDataAvail) { return binder::Status::ok(); @@ -1186,9 +1260,16 @@ status_t Parcel::writeUtf8VectorAsUtf16Vector( const std::unique_ptr>>& val) { return writeData(val); } status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector& val) { return writeData(val); } -status_t Parcel::writeUniqueFileDescriptorVector(const std::vector& val) { return writeData(val); } -status_t Parcel::writeUniqueFileDescriptorVector(const std::optional>& val) { return writeData(val); } -status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr>& val) { return writeData(val); } +status_t Parcel::writeUniqueFileDescriptorVector(const std::vector& val) { + return writeData(val); +} +status_t Parcel::writeUniqueFileDescriptorVector(const std::optional>& val) { + return writeData(val); +} +status_t Parcel::writeUniqueFileDescriptorVector( + const std::unique_ptr>& val) { + return writeData(val); +} status_t Parcel::writeStrongBinderVector(const std::vector>& val) { return writeData(val); } status_t Parcel::writeStrongBinderVector(const std::optional>>& val) { return writeData(val); } @@ -1241,9 +1322,16 @@ status_t Parcel::readUtf8VectorFromUtf16Vector( std::unique_ptr>>* val) const { return readData(val); } status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector* val) const { return readData(val); } -status_t Parcel::readUniqueFileDescriptorVector(std::optional>* val) const { return readData(val); } -status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr>* val) const { return readData(val); } -status_t Parcel::readUniqueFileDescriptorVector(std::vector* val) const { return readData(val); } +status_t Parcel::readUniqueFileDescriptorVector(std::optional>* val) const { + return readData(val); +} +status_t Parcel::readUniqueFileDescriptorVector( + std::unique_ptr>* val) const { + return readData(val); +} +status_t Parcel::readUniqueFileDescriptorVector(std::vector* val) const { + return readData(val); +} status_t Parcel::readStrongBinderVector(std::optional>>* val) const { return readData(val); } status_t Parcel::readStrongBinderVector(std::unique_ptr>>* val) const { return readData(val); } @@ -1357,7 +1445,7 @@ status_t Parcel::writeCString(const char* str) status_t Parcel::writeString8(const String8& str) { - return writeString8(str.string(), str.size()); + return writeString8(str.c_str(), str.size()); } status_t Parcel::writeString8(const char* str, size_t len) @@ -1380,7 +1468,7 @@ status_t Parcel::writeString8(const char* str, size_t len) status_t Parcel::writeString16(const String16& str) { - return writeString16(str.string(), str.size()); + return writeString16(str.c_str(), str.size()); } status_t Parcel::writeString16(const char16_t* str, size_t len) @@ -1416,6 +1504,7 @@ status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) { return writeParcelable(*parcelable); } +#ifndef BINDER_DISABLE_NATIVE_HANDLE status_t Parcel::writeNativeHandle(const native_handle* handle) { if (!handle || handle->version != sizeof(native_handle)) @@ -1438,14 +1527,15 @@ status_t Parcel::writeNativeHandle(const native_handle* handle) err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts); return err; } +#endif status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { if (auto* rpcFields = maybeRpcFields()) { - std::variant fdVariant; + std::variant fdVariant; if (takeOwnership) { - fdVariant = base::unique_fd(fd); + fdVariant = unique_fd(fd); } else { - fdVariant = base::borrowed_fd(fd); + fdVariant = borrowed_fd(fd); } if (!mAllowFds) { return FDS_NOT_ALLOWED; @@ -1495,7 +1585,7 @@ status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { status_t Parcel::writeDupFileDescriptor(int fd) { int dupFd; - if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) { + if (status_t err = binder::os::dupFileDescriptor(fd, &dupFd); err != OK) { return err; } status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/); @@ -1514,7 +1604,7 @@ status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership) status_t Parcel::writeDupParcelFileDescriptor(int fd) { int dupFd; - if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) { + if (status_t err = binder::os::dupFileDescriptor(fd, &dupFd); err != OK) { return err; } status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/); @@ -1524,12 +1614,18 @@ status_t Parcel::writeDupParcelFileDescriptor(int fd) return err; } -status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) { +status_t Parcel::writeUniqueFileDescriptor(const unique_fd& fd) { return writeDupFileDescriptor(fd.get()); } status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob) { +#ifdef BINDER_DISABLE_BLOB + (void)len; + (void)mutableCopy; + (void)outBlob; + return INVALID_OPERATION; +#else if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. @@ -1581,6 +1677,7 @@ status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob) } ::close(fd); return status; +#endif } status_t Parcel::writeDupImmutableBlobFileDescriptor(int fd) @@ -1722,7 +1819,9 @@ status_t Parcel::validateReadData(size_t upperBound) const do { 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); + if (!mServiceFuzzing) { + ALOGE("Attempt to read from protected data in Parcel %p", this); + } return PERMISSION_DENIED; } nextObject++; @@ -2092,7 +2191,11 @@ String8 Parcel::readString8() const size_t len; const char* str = readString8Inplace(&len); if (str) return String8(str, len); - ALOGE("Reading a NULL string not supported here."); + + if (!mServiceFuzzing) { + ALOGE("Reading a NULL string not supported here."); + } + return String8(); } @@ -2132,7 +2235,11 @@ String16 Parcel::readString16() const size_t len; const char16_t* str = readString16Inplace(&len); if (str) return String16(str, len); - ALOGE("Reading a NULL string not supported here."); + + if (!mServiceFuzzing) { + ALOGE("Reading a NULL string not supported here."); + } + return String16(); } @@ -2172,7 +2279,9 @@ status_t Parcel::readStrongBinder(sp* val) const { status_t status = readNullableStrongBinder(val); if (status == OK && !val->get()) { - ALOGW("Expecting binder but got null!"); + if (!mServiceFuzzing) { + ALOGW("Expecting binder but got null!"); + } status = UNEXPECTED_NULL; } return status; @@ -2200,6 +2309,7 @@ int32_t Parcel::readExceptionCode() const return status.exceptionCode(); } +#ifndef BINDER_DISABLE_NATIVE_HANDLE native_handle* Parcel::readNativeHandle() const { int numFds, numInts; @@ -2232,14 +2342,17 @@ native_handle* Parcel::readNativeHandle() const } return h; } +#endif 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); + if (!mServiceFuzzing) { + ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in " + "the object list", + this, mDataPos); + } return BAD_TYPE; } @@ -2304,8 +2417,7 @@ int Parcel::readParcelFileDescriptor() const { return fd; } -status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const -{ +status_t Parcel::readUniqueFileDescriptor(unique_fd* val) const { int got = readFileDescriptor(); if (got == BAD_TYPE) { @@ -2313,7 +2425,7 @@ status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const } int dupFd; - if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) { + if (status_t err = binder::os::dupFileDescriptor(got, &dupFd); err != OK) { return BAD_VALUE; } @@ -2326,8 +2438,7 @@ status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const return OK; } -status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const -{ +status_t Parcel::readUniqueParcelFileDescriptor(unique_fd* val) const { int got = readParcelFileDescriptor(); if (got == BAD_TYPE) { @@ -2335,7 +2446,7 @@ status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const } int dupFd; - if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) { + if (status_t err = binder::os::dupFileDescriptor(got, &dupFd); err != OK) { return BAD_VALUE; } @@ -2350,6 +2461,11 @@ status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const { +#ifdef BINDER_DISABLE_BLOB + (void)len; + (void)outBlob; + return INVALID_OPERATION; +#else int32_t blobType; status_t status = readInt32(&blobType); if (status) return status; @@ -2383,6 +2499,7 @@ status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const outBlob->init(fd, ptr, len, isMutable); return NO_ERROR; +#endif } status_t Parcel::read(FlattenableHelperInterface& val) const @@ -2497,8 +2614,11 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const return obj; } } - ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object list", - this, DPOS); + if (!mServiceFuzzing) { + ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object " + "list", + this, DPOS); + } } return nullptr; } @@ -2517,7 +2637,8 @@ void Parcel::closeFileDescriptors() { reinterpret_cast(mData + kernelFields->mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { // ALOGI("Closing fd: %ld", flat->handle); - close(flat->handle); + // FDs from the kernel are always owned + FdTagClose(flat->handle, this); } } #else // BINDER_WITH_KERNEL_IPC @@ -2598,6 +2719,10 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin kernelFields->mObjectsSize = 0; break; } + if (type == BINDER_TYPE_FD) { + // FDs from the kernel are always owned + FdTag(flat->handle, 0, this); + } minOffset = offset + sizeof(flat_binder_object); } scanForFds(); @@ -2610,8 +2735,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin status_t Parcel::rpcSetDataReference( const sp& session, const uint8_t* data, size_t dataSize, const uint32_t* objectTable, size_t objectTableSize, - std::vector>&& ancillaryFds, - release_func relFunc) { + std::vector>&& 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"); @@ -3093,6 +3217,7 @@ void Parcel::initState() mDeallocZero = false; mOwner = nullptr; mEnforceNoDataAvail = true; + mServiceFuzzing = false; } void Parcel::scanForFds() const { @@ -3122,6 +3247,7 @@ size_t Parcel::getOpenAshmemSize() const } size_t openAshmemSize = 0; +#ifndef BINDER_DISABLE_BLOB for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { const flat_binder_object* flat = reinterpret_cast(mData + kernelFields->mObjects[i]); @@ -3136,6 +3262,7 @@ size_t Parcel::getOpenAshmemSize() const } } } +#endif return openAshmemSize; } #endif // BINDER_WITH_KERNEL_IPC diff --git a/libs/binder/ParcelFileDescriptor.cpp b/libs/binder/ParcelFileDescriptor.cpp index 4f8b76f7d981e256cd1a94dbbecd7d2e80dc4dd1..c3c487442bd6f699e3003787decab53ec719c868 100644 --- a/libs/binder/ParcelFileDescriptor.cpp +++ b/libs/binder/ParcelFileDescriptor.cpp @@ -19,9 +19,11 @@ namespace android { namespace os { +using android::binder::unique_fd; + ParcelFileDescriptor::ParcelFileDescriptor() = default; -ParcelFileDescriptor::ParcelFileDescriptor(android::base::unique_fd fd) : mFd(std::move(fd)) {} +ParcelFileDescriptor::ParcelFileDescriptor(unique_fd fd) : mFd(std::move(fd)) {} ParcelFileDescriptor::~ParcelFileDescriptor() = default; diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp index 670fd55da3708727a6b810236873e97ff5ca211b..658686d2db49b5515c7a4d1393a5f6e57d5c3007 100644 --- a/libs/binder/PermissionCache.cpp +++ b/libs/binder/PermissionCache.cpp @@ -101,9 +101,8 @@ bool PermissionCache::checkPermission( nsecs_t t = -systemTime(); granted = android::checkPermission(permission, pid, uid); t += systemTime(); - ALOGD("checking %s for uid=%d => %s (%d us)", - String8(permission).string(), uid, - granted?"granted":"denied", (int)ns2us(t)); + ALOGD("checking %s for uid=%d => %s (%d us)", String8(permission).c_str(), uid, + granted ? "granted" : "denied", (int)ns2us(t)); pc.cache(permission, uid, granted); } return granted; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 5f1f50672a6502f8e641f9634be4c45978e5fa89..7de94e3c6c0808ddd93c66a69be40996a0f44b81 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -18,10 +18,9 @@ #include -#include -#include #include #include +#include #include #include #include @@ -32,6 +31,7 @@ #include #include "Static.h" +#include "Utils.h" #include "binder_module.h" #include @@ -60,6 +60,9 @@ const char* kDefaultDriver = "/dev/binder"; namespace android { +using namespace android::binder::impl; +using android::binder::unique_fd; + class PoolThread : public Thread { public: @@ -104,14 +107,7 @@ bool ProcessState::isVndservicemanagerEnabled() { return access("/vendor/bin/vndservicemanager", R_OK) == 0; } -sp 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 - +sp ProcessState::init(const char* driver, bool requireDefault) { if (driver == nullptr) { std::lock_guard l(gProcessMutex); if (gProcess) { @@ -196,9 +192,10 @@ void ProcessState::childPostFork() { void ProcessState::startThreadPool() { - AutoMutex _l(mLock); + std::unique_lock _l(mLock); if (!mThreadPoolStarted) { if (mMaxThreads == 0) { + // see also getThreadPoolMaxTotalThreadCount ALOGW("Extra binder thread started, but 0 threads requested. Do not use " "*startThreadPool when zero threads are requested."); } @@ -209,7 +206,7 @@ void ProcessState::startThreadPool() bool ProcessState::becomeContextManager() { - AutoMutex _l(mLock); + std::unique_lock _l(mLock); flat_binder_object obj { .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, @@ -316,7 +313,7 @@ sp ProcessState::getStrongProxyForHandle(int32_t handle) { sp result; - AutoMutex _l(mLock); + std::unique_lock _l(mLock); if (handle == 0 && the_context_object != nullptr) return the_context_object; @@ -380,7 +377,7 @@ sp ProcessState::getStrongProxyForHandle(int32_t handle) void ProcessState::expungeHandle(int32_t handle, IBinder* binder) { - AutoMutex _l(mLock); + std::unique_lock _l(mLock); handle_entry* e = lookupHandleLocked(handle); @@ -407,13 +404,18 @@ void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); - ALOGV("Spawning new pooled thread, name=%s\n", name.string()); + ALOGV("Spawning new pooled thread, name=%s\n", name.c_str()); sp t = sp::make(isMain); - t->run(name.string()); + t->run(name.c_str()); pthread_mutex_lock(&mThreadCountLock); mKernelStartedThreads++; pthread_mutex_unlock(&mThreadCountLock); } + // TODO: if startThreadPool is called on another thread after the process + // starts up, the kernel might think that it already requested those + // binder threads, and additional won't be started. This is likely to + // cause deadlocks, and it will also cause getThreadPoolMaxTotalThreadCount + // to return too high of a value. } status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { @@ -431,14 +433,34 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { pthread_mutex_lock(&mThreadCountLock); - base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); }; + auto detachGuard = make_scope_guard([&]() { pthread_mutex_unlock(&mThreadCountLock); }); - // may actually be one more than this, if join is called if (mThreadPoolStarted) { - return mCurrentThreads < mKernelStartedThreads - ? mMaxThreads - : mMaxThreads + mCurrentThreads - mKernelStartedThreads; + LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1, + "too many kernel-started threads: %zu > %zu + 1", mKernelStartedThreads, + mMaxThreads); + + // calling startThreadPool starts a thread + size_t threads = 1; + + // the kernel is configured to start up to mMaxThreads more threads + threads += mMaxThreads; + + // Users may call IPCThreadState::joinThreadPool directly. We don't + // currently have a way to count this directly (it could be added by + // adding a separate private joinKernelThread method in IPCThreadState). + // So, if we are in a race between the kernel thread variable being + // incremented in this file and mCurrentThreads being incremented + // in IPCThreadState, temporarily forget about the extra join threads. + // This is okay, because most callers of this method only care about + // having 0, 1, or more threads. + if (mCurrentThreads > mKernelStartedThreads) { + threads += mCurrentThreads - mKernelStartedThreads; + } + + return threads; } + // must not be initialized or maybe has poll thread setup, we // currently don't track this in libbinder LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0, @@ -486,38 +508,38 @@ status_t ProcessState::enableOnewaySpamDetection(bool enable) { } void ProcessState::giveThreadPoolName() { - androidSetThreadName( makeBinderThreadName().string() ); + androidSetThreadName(makeBinderThreadName().c_str()); } String8 ProcessState::getDriverName() { return mDriverName; } -static base::Result open_driver(const char* driver) { - int fd = open(driver, O_RDWR | O_CLOEXEC); - if (fd < 0) { - return base::ErrnoError() << "Opening '" << driver << "' failed"; +static unique_fd open_driver(const char* driver) { + auto fd = unique_fd(open(driver, O_RDWR | O_CLOEXEC)); + if (!fd.ok()) { + PLOGE("Opening '%s' failed", driver); + return {}; } int vers = 0; - status_t result = ioctl(fd, BINDER_VERSION, &vers); + int result = ioctl(fd.get(), BINDER_VERSION, &vers); if (result == -1) { - close(fd); - return base::ErrnoError() << "Binder ioctl to obtain version failed"; + PLOGE("Binder ioctl to obtain version failed"); + return {}; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - close(fd); - return base::Error() << "Binder driver protocol(" << vers - << ") does not match user space protocol(" - << BINDER_CURRENT_PROTOCOL_VERSION - << ")! ioctl() return value: " << result; + ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! " + "ioctl() return value: %d", + vers, BINDER_CURRENT_PROTOCOL_VERSION, result); + return {}; } size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; - result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + result = ioctl(fd.get(), BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION; - result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); + result = ioctl(fd.get(), BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); if (result == -1) { ALOGE_IF(ProcessState::isDriverFeatureEnabled( ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION), @@ -542,28 +564,27 @@ ProcessState::ProcessState(const char* driver) mThreadPoolStarted(false), mThreadPoolSeq(1), mCallRestriction(CallRestriction::NONE) { - base::Result opened = open_driver(driver); + unique_fd opened = open_driver(driver); if (opened.ok()) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, - opened.value(), 0); + opened.get(), 0); if (mVMStart == MAP_FAILED) { - close(opened.value()); // *sigh* - opened = base::Error() - << "Using " << driver << " failed: unable to mmap transaction memory."; + ALOGE("Using %s failed: unable to mmap transaction memory.", driver); + opened.reset(); mDriverName.clear(); } } #ifdef __ANDROID__ - LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating: %s", - driver, opened.error().message().c_str()); + LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.", + driver); #endif if (opened.ok()) { - mDriverFD = opened.value(); + mDriverFD = opened.release(); } } diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index 1c7613584ba9489311ae97e484472c73bae1dfbb..525ba2e9b9a5fc620a8626ef8d4a4c971f0af6f2 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -14,17 +14,23 @@ * limitations under the License. */ -#include -#include -#include -#include +#include "file.h" + +#include #include +#include + +#include #include +#include #include +using namespace android::binder::impl; using android::Parcel; -using android::base::borrowed_fd; -using android::base::unique_fd; +using android::binder::borrowed_fd; +using android::binder::ReadFully; +using android::binder::unique_fd; +using android::binder::WriteFully; using android::binder::debug::RecordedTransaction; #define PADDING8(s) ((8 - (s) % 8) % 8) @@ -124,20 +130,19 @@ std::optional RecordedTransaction::fromDetails( static_cast(timestamp.tv_nsec), 0}; - t.mData.mInterfaceName = std::string(String8(interfaceName).string()); + t.mData.mInterfaceName = std::string(String8(interfaceName).c_str()); if (interfaceName.size() != t.mData.mInterfaceName.size()) { - LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte " - "utf-8."; + ALOGE("Interface Name is not valid. Contains characters that aren't single byte utf-8."); return std::nullopt; } if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set sent parcel data."; + ALOGE("Failed to set sent parcel data."); return std::nullopt; } if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set reply parcel data."; + ALOGE("Failed to set reply parcel data."); return std::nullopt; } @@ -161,53 +166,43 @@ static_assert(sizeof(ChunkDescriptor) % 8 == 0); constexpr uint32_t kMaxChunkDataSize = 0xfffffff0; typedef uint64_t transaction_checksum_t; -static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut, - transaction_checksum_t* sum) { - if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) { - LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get(); - return android::UNKNOWN_ERROR; - } - - *sum ^= *reinterpret_cast(chunkOut); - return android::NO_ERROR; -} - std::optional RecordedTransaction::fromFile(const unique_fd& fd) { RecordedTransaction t; ChunkDescriptor chunk; const long pageSize = sysconf(_SC_PAGE_SIZE); struct stat fileStat; if (fstat(fd.get(), &fileStat) != 0) { - LOG(ERROR) << "Unable to get file information"; + ALOGE("Unable to get file information"); return std::nullopt; } off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); if (fdCurrentPosition == -1) { - LOG(ERROR) << "Invalid offset in file descriptor."; + ALOGE("Invalid offset in file descriptor."); return std::nullopt; } do { if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) { - LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor"; + ALOGE("Not enough file remains to contain expected chunk descriptor"); return std::nullopt; } - transaction_checksum_t checksum = 0; - if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) { - LOG(ERROR) << "Failed to read chunk descriptor."; + + if (!ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) { + ALOGE("Failed to read ChunkDescriptor from fd %d. %s", fd.get(), strerror(errno)); return std::nullopt; } + transaction_checksum_t checksum = *reinterpret_cast(&chunk); fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); if (fdCurrentPosition == -1) { - LOG(ERROR) << "Invalid offset in file descriptor."; + ALOGE("Invalid offset in file descriptor."); return std::nullopt; } off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize; off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart; if (chunk.dataSize > kMaxChunkDataSize) { - LOG(ERROR) << "Chunk data exceeds maximum size."; + ALOGE("Chunk data exceeds maximum size."); return std::nullopt; } @@ -215,19 +210,19 @@ std::optional RecordedTransaction::fromFile(const unique_fd chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t); if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) { - LOG(ERROR) << "Chunk payload exceeds remaining file size."; + ALOGE("Chunk payload exceeds remaining file size."); return std::nullopt; } if (PADDING8(chunkPayloadSize) != 0) { - LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize; + ALOGE("Invalid chunk size, not aligned %zu", chunkPayloadSize); return std::nullopt; } size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset; void* mappedMemory = mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart); - auto mmap_guard = android::base::make_scope_guard( + auto mmap_guard = make_scope_guard( [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); }); transaction_checksum_t* payloadMap = @@ -236,8 +231,7 @@ std::optional RecordedTransaction::fromFile(const unique_fd sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap // page-alignment if (payloadMap == MAP_FAILED) { - LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " " - << strerror(errno); + ALOGE("Memory mapping failed for fd %d: %d %s", fd.get(), errno, strerror(errno)); return std::nullopt; } for (size_t checksumIndex = 0; @@ -245,21 +239,21 @@ std::optional RecordedTransaction::fromFile(const unique_fd checksum ^= payloadMap[checksumIndex]; } if (checksum != 0) { - LOG(ERROR) << "Checksum failed."; + ALOGE("Checksum failed."); return std::nullopt; } fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR); if (fdCurrentPosition == -1) { - LOG(ERROR) << "Invalid offset in file descriptor."; + ALOGE("Invalid offset in file descriptor."); return std::nullopt; } switch (chunk.chunkType) { case HEADER_CHUNK: { if (chunk.dataSize != static_cast(sizeof(TransactionHeader))) { - LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected " - << sizeof(TransactionHeader) << "."; + ALOGE("Header Chunk indicated size %" PRIu32 "; Expected %zu.", chunk.dataSize, + sizeof(TransactionHeader)); return std::nullopt; } t.mData.mHeader = *reinterpret_cast(payloadMap); @@ -273,7 +267,7 @@ std::optional RecordedTransaction::fromFile(const unique_fd case DATA_PARCEL_CHUNK: { if (t.mSent.setData(reinterpret_cast(payloadMap), chunk.dataSize) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set sent parcel data."; + ALOGE("Failed to set sent parcel data."); return std::nullopt; } break; @@ -281,7 +275,7 @@ std::optional RecordedTransaction::fromFile(const unique_fd case REPLY_PARCEL_CHUNK: { if (t.mReply.setData(reinterpret_cast(payloadMap), chunk.dataSize) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set reply parcel data."; + ALOGE("Failed to set reply parcel data."); return std::nullopt; } break; @@ -289,7 +283,7 @@ std::optional RecordedTransaction::fromFile(const unique_fd case END_CHUNK: break; default: - LOG(INFO) << "Unrecognized chunk."; + ALOGI("Unrecognized chunk."); break; } } while (chunk.chunkType != END_CHUNK); @@ -300,7 +294,7 @@ std::optional RecordedTransaction::fromFile(const unique_fd android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType, size_t byteCount, const uint8_t* data) const { if (byteCount > kMaxChunkDataSize) { - LOG(ERROR) << "Chunk data exceeds maximum size"; + ALOGE("Chunk data exceeds maximum size"); return BAD_VALUE; } ChunkDescriptor descriptor = {.chunkType = chunkType, @@ -328,8 +322,8 @@ android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunk buffer.insert(buffer.end(), checksumBytes, checksumBytes + sizeof(transaction_checksum_t)); // Write buffer to file - if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) { - LOG(ERROR) << "Failed to write chunk fd " << fd.get(); + if (!WriteFully(fd, buffer.data(), buffer.size())) { + ALOGE("Failed to write chunk fd %d", fd.get()); return UNKNOWN_ERROR; } return NO_ERROR; @@ -339,26 +333,26 @@ android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { if (NO_ERROR != writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader), reinterpret_cast(&(mData.mHeader)))) { - LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get(); + ALOGE("Failed to write transactionHeader to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t), reinterpret_cast(mData.mInterfaceName.c_str()))) { - LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get(); + ALOGI("Failed to write Interface Name Chunk to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) { - LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get(); + ALOGE("Failed to write sent Parcel to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) { - LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get(); + ALOGE("Failed to write reply Parcel to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) { - LOG(ERROR) << "Failed to write end chunk to fd " << fd.get(); + ALOGE("Failed to write end chunk to fd %d", fd.get()); return UNKNOWN_ERROR; } return NO_ERROR; diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 9282856f5c5c9ee6a8dc194edbe0b542e94fd6f3..d9e926a9d5c4a641903d2f2428f217b88de85487 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "RpcServer" #include +#include #include #include #include @@ -24,13 +25,11 @@ #include #include -#include -#include +#include #include #include #include #include -#include #include "BuildFlags.h" #include "FdTrigger.h" @@ -45,8 +44,9 @@ namespace android { constexpr size_t kSessionIdBytes = 32; -using base::ScopeGuard; -using base::unique_fd; +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; RpcServer::RpcServer(std::unique_ptr ctx) : mCtx(std::move(ctx)) {} RpcServer::~RpcServer() { @@ -57,7 +57,7 @@ RpcServer::~RpcServer() { sp RpcServer::make(std::unique_ptr rpcTransportCtxFactory) { // Default is without TLS. if (rpcTransportCtxFactory == nullptr) - rpcTransportCtxFactory = makeDefaultRpcTransportCtxFactory(); + rpcTransportCtxFactory = binder::os::makeDefaultRpcTransportCtxFactory(); auto ctx = rpcTransportCtxFactory->newServerCtx(); if (ctx == nullptr) return nullptr; return sp::make(std::move(ctx)); @@ -81,6 +81,7 @@ status_t RpcServer::setupInetServer(const char* address, unsigned int port, auto aiStart = InetSocketAddress::getAddrInfo(address, port); if (aiStart == nullptr) return UNKNOWN_ERROR; for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { + if (ai->ai_addr == nullptr) continue; InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, address, port); if (status_t status = setupSocketServer(socketAddress); status != OK) { continue; @@ -123,8 +124,13 @@ size_t RpcServer::getMaxThreads() { return mMaxThreads; } -void RpcServer::setProtocolVersion(uint32_t version) { +bool RpcServer::setProtocolVersion(uint32_t version) { + if (!RpcState::validateProtocolVersion(version)) { + return false; + } + mProtocolVersion = version; + return true; } void RpcServer::setSupportedFileDescriptorTransportModes( @@ -148,7 +154,7 @@ void RpcServer::setRootObjectWeak(const wp& binder) { mRootObjectWeak = binder; } void RpcServer::setPerSessionRootObject( - std::function(const void*, size_t)>&& makeObject) { + std::function(wp session, const void*, size_t)>&& makeObject) { RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectWeak.clear(); @@ -161,6 +167,12 @@ void RpcServer::setConnectionFilter(std::function&& f mConnectionFilter = std::move(filter); } +void RpcServer::setServerSocketModifier(std::function&& modifier) { + RpcMutexLockGuard _l(mLock); + LOG_ALWAYS_FATAL_IF(mServer.fd.ok(), "Already started"); + mServerSocketModifier = std::move(modifier); +} + sp RpcServer::getRootObject() { RpcMutexLockGuard _l(mLock); bool hasWeak = mRootObjectWeak.unsafe_get(); @@ -189,7 +201,7 @@ void RpcServer::start() { status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) { RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY( accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK)))); - if (clientSocket.fd < 0) { + if (!clientSocket.fd.ok()) { int savedErrno = errno; ALOGE("Could not accept4 socket: %s", strerror(savedErrno)); return -savedErrno; @@ -202,9 +214,9 @@ status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransport status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out) { int zero = 0; iovec iov{&zero, sizeof(zero)}; - std::vector> fds; + std::vector> fds; - ssize_t num_bytes = receiveMessageFromSocket(server.mServer, &iov, 1, &fds); + ssize_t num_bytes = binder::os::receiveMessageFromSocket(server.mServer, &iov, 1, &fds); if (num_bytes < 0) { int savedErrno = errno; ALOGE("Failed recvmsg: %s", strerror(savedErrno)); @@ -219,10 +231,7 @@ status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTranspor } unique_fd fd(std::move(std::get(fds.back()))); - if (auto res = setNonBlocking(fd); !res.ok()) { - ALOGE("Failed setNonBlocking: %s", res.error().message().c_str()); - return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); - } + if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res; *out = RpcTransportFd(std::move(fd)); return OK; @@ -335,6 +344,8 @@ bool RpcServer::shutdown() { mJoinThread.reset(); } + mServer = RpcTransportFd(); + LOG_RPC_DETAIL("Finished waiting on shutdown."); mShutdownTrigger = nullptr; @@ -444,11 +455,12 @@ void RpcServer::establishConnection( LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(), "Must establish connection on owned thread"); thisThread = std::move(threadId->second); - ScopeGuard detachGuard = [&]() { + auto detachGuardLambda = [&]() { thisThread.detach(); _l.unlock(); server->mShutdownCv.notify_all(); }; + auto detachGuard = make_scope_guard(std::ref(detachGuardLambda)); server->mConnectingThreads.erase(threadId); if (status != OK || server->mShutdownTrigger->isTriggered()) { @@ -470,11 +482,11 @@ void RpcServer::establishConnection( // don't block if there is some entropy issue if (tries++ > 5) { ALOGE("Cannot find new address: %s", - base::HexString(sessionId.data(), sessionId.size()).c_str()); + HexString(sessionId.data(), sessionId.size()).c_str()); return; } - auto status = getRandomBytes(sessionId.data(), sessionId.size()); + auto status = binder::os::getRandomBytes(sessionId.data(), sessionId.size()); if (status != OK) { ALOGE("Failed to read random session ID: %s", strerror(-status)); return; @@ -501,7 +513,8 @@ void RpcServer::establishConnection( // if null, falls back to server root sp sessionSpecificRoot; if (server->mRootObjectFactory != nullptr) { - sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen); + sessionSpecificRoot = + server->mRootObjectFactory(wp(session), addr.data(), addrLen); if (sessionSpecificRoot == nullptr) { ALOGE("Warning: server returned null from root object factory"); } @@ -521,7 +534,7 @@ void RpcServer::establishConnection( auto it = server->mSessions.find(sessionId); if (it == server->mSessions.end()) { ALOGE("Cannot add thread, no record of session with ID %s", - base::HexString(sessionId.data(), sessionId.size()).c_str()); + HexString(sessionId.data(), sessionId.size()).c_str()); return; } session = it->second; @@ -533,7 +546,7 @@ void RpcServer::establishConnection( return; } - detachGuard.Disable(); + detachGuard.release(); session->preJoinThreadOwnership(std::move(thisThread)); } @@ -556,6 +569,25 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); return -savedErrno; } + + if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) { + int noDelay = 1; + int result = + setsockopt(socket_fd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)); + if (result < 0) { + int savedErrno = errno; + ALOGE("Could not set TCP_NODELAY on %s", strerror(savedErrno)); + return -savedErrno; + } + } + + { + RpcMutexLockGuard _l(mLock); + if (mServerSocketModifier != nullptr) { + mServerSocketModifier(socket_fd); + } + } + if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) { int savedErrno = errno; ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); @@ -587,15 +619,14 @@ status_t RpcServer::setupRawSocketServer(unique_fd socket_fd) { void RpcServer::onSessionAllIncomingThreadsEnded(const sp& session) { const std::vector& id = session->mId; LOG_ALWAYS_FATAL_IF(id.empty(), "Server sessions must be initialized with ID"); - LOG_RPC_DETAIL("Dropping session with address %s", - base::HexString(id.data(), id.size()).c_str()); + LOG_RPC_DETAIL("Dropping session with address %s", HexString(id.data(), id.size()).c_str()); 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()); + HexString(id.data(), id.size()).c_str()); LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %s", - base::HexString(id.data(), id.size()).c_str()); + HexString(id.data(), id.size()).c_str()); (void)mSessions.erase(it); } @@ -614,8 +645,7 @@ unique_fd RpcServer::releaseServer() { } status_t RpcServer::setupExternalServer( - base::unique_fd serverFd, - std::function&& acceptFn) { + unique_fd serverFd, std::function&& acceptFn) { RpcMutexLockGuard _l(mLock); if (mServer.fd.ok()) { ALOGE("Each RpcServer can only have one server."); @@ -626,7 +656,7 @@ status_t RpcServer::setupExternalServer( return OK; } -status_t RpcServer::setupExternalServer(base::unique_fd serverFd) { +status_t RpcServer::setupExternalServer(unique_fd serverFd) { return setupExternalServer(std::move(serverFd), &RpcServer::acceptSocketConnection); } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index fbad0f7756953126d9aeab98b580dc0a8eb66d99..16a7f9fd489d261ddbc2a2a5d52935e2371472bb 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -26,15 +26,12 @@ #include -#include -#include -#include #include +#include #include #include #include #include -#include #include #include "BuildFlags.h" @@ -53,7 +50,9 @@ extern "C" JavaVM* AndroidRuntimeGetJavaVM(); namespace android { -using base::unique_fd; +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; RpcSession::RpcSession(std::unique_ptr ctx) : mCtx(std::move(ctx)) { LOG_RPC_DETAIL("RpcSession created %p", this); @@ -70,7 +69,7 @@ RpcSession::~RpcSession() { sp RpcSession::make() { // Default is without TLS. - return make(makeDefaultRpcTransportCtxFactory()); + return make(binder::os::makeDefaultRpcTransportCtxFactory()); } sp RpcSession::make(std::unique_ptr rpcTransportCtxFactory) { @@ -104,11 +103,7 @@ size_t RpcSession::getMaxOutgoingThreads() { } bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) { - if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT && - version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { - ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version " - "is %u).", - version, RPC_WIRE_PROTOCOL_VERSION); + if (!RpcState::validateProtocolVersion(version)) { return false; } @@ -163,7 +158,7 @@ status_t RpcSession::setupUnixDomainSocketBootstrapClient(unique_fd bootstrapFd) int zero = 0; iovec iov{&zero, sizeof(zero)}; - std::vector> fds; + std::vector> fds; fds.push_back(std::move(serverFd)); status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov, @@ -192,17 +187,13 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { return NAME_NOT_FOUND; } -status_t RpcSession::setupPreconnectedClient(base::unique_fd fd, - std::function&& request) { +status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function&& request) { return setupClient([&](const std::vector& sessionId, bool incoming) -> status_t { if (!fd.ok()) { fd = request(); if (!fd.ok()) return BAD_VALUE; } - if (auto res = setNonBlocking(fd); !res.ok()) { - ALOGE("setupPreconnectedClient: %s", res.error().message().c_str()); - return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); - } + if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res; RpcTransportFd transportFd(std::move(fd)); status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming); @@ -217,7 +208,7 @@ status_t RpcSession::addNullDebuggingClient() { unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC))); - if (serverFd == -1) { + if (!serverFd.ok()) { int savedErrno = errno; ALOGE("Could not connect to /dev/null: %s", strerror(savedErrno)); return -savedErrno; @@ -314,8 +305,7 @@ status_t RpcSession::readId() { status = state()->getSessionId(connection.get(), sp::fromExisting(this), &mId); if (status != OK) return status; - LOG_RPC_DETAIL("RpcSession %p has id %s", this, - base::HexString(mId.data(), mId.size()).c_str()); + LOG_RPC_DETAIL("RpcSession %p has id %s", this, HexString(mId.data(), mId.size()).c_str()); return OK; } @@ -418,7 +408,9 @@ public: } private: - DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher); + JavaThreadAttacher(const JavaThreadAttacher&) = delete; + void operator=(const JavaThreadAttacher&) = delete; + bool mAttached = false; static JavaVM* getJavaVM() { @@ -503,7 +495,7 @@ status_t RpcSession::setupClient(const std::functionsa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); - if (serverFd == -1) { + if (!serverFd.ok()) { int savedErrno = errno; ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); @@ -713,7 +705,7 @@ status_t RpcSession::initAndAddConnection(RpcTransportFd fd, const std::vector rpcTran { RpcMutexLockGuard _l(mMutex); connection->rpcTransport = std::move(rpcTransport); - connection->exclusiveTid = rpcGetThreadId(); + connection->exclusiveTid = binder::os::GetThreadId(); mConnections.mOutgoing.push_back(connection); } @@ -829,7 +821,7 @@ sp RpcSession::assignIncomingConnectionToThisThread( sp session = sp::make(); session->rpcTransport = std::move(rpcTransport); - session->exclusiveTid = rpcGetThreadId(); + session->exclusiveTid = binder::os::GetThreadId(); mConnections.mIncoming.push_back(session); mConnections.mMaxIncoming = mConnections.mIncoming.size(); @@ -874,7 +866,7 @@ status_t RpcSession::ExclusiveConnection::find(const sp& session, Co connection->mConnection = nullptr; connection->mReentrant = false; - uint64_t tid = rpcGetThreadId(); + uint64_t tid = binder::os::GetThreadId(); RpcMutexUniqueLock _l(session->mMutex); session->mConnections.mWaitingThreads++; diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 03fa69973d2c6a7ac2c45eb9baf5fb6ecdb196d2..fe6e1a33183368b79aa946a237eb55d0508e9210 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -18,11 +18,8 @@ #include "RpcState.h" -#include -#include -#include -#include #include +#include #include #include @@ -31,12 +28,19 @@ #include "Utils.h" #include +#include #include +#ifdef __ANDROID__ +#include +#endif + namespace android { -using base::StringPrintf; +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { @@ -59,6 +63,7 @@ static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) { case RpcSession::FileDescriptorTransportMode::TRUSTY: return true; } + LOG_ALWAYS_FATAL("Invalid FileDescriptorTransportMode: %d", static_cast(mode)); } RpcState::RpcState() {} @@ -325,8 +330,10 @@ std::string RpcState::BinderNode::toString() const { desc = "(not promotable)"; } - return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}", - this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc); + std::stringstream ss; + ss << "node{" << intptr_t(this->binder.unsafe_get()) << " times sent: " << this->timesSent + << " times recd: " << this->timesRecd << " type: " << desc << "}"; + return ss.str(); } RpcState::CommandData::CommandData(size_t size) : mSize(size) { @@ -350,15 +357,14 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { mData.reset(new (std::nothrow) uint8_t[size]); } -status_t RpcState::rpcSend( - const sp& connection, const sp& session, - const char* what, iovec* iovs, int niovs, - const std::optional>& altPoll, - const std::vector>* ancillaryFds) { +status_t RpcState::rpcSend(const sp& connection, + const sp& session, const char* what, iovec* iovs, int niovs, + const std::optional>& altPoll, + const std::vector>* 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(), - android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); + HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } if (status_t status = @@ -375,10 +381,9 @@ status_t RpcState::rpcSend( return OK; } -status_t RpcState::rpcRec( - const sp& connection, const sp& session, - const char* what, iovec* iovs, int niovs, - std::vector>* ancillaryFds) { +status_t RpcState::rpcRec(const sp& connection, + const sp& session, const char* what, iovec* iovs, int niovs, + std::vector>* ancillaryFds) { if (status_t status = connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(), iovs, niovs, std::nullopt, @@ -393,11 +398,35 @@ status_t RpcState::rpcRec( for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Received %s (part %d of %d) on RpcTransport %p: %s", what, i + 1, niovs, connection->rpcTransport.get(), - android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); + HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } return OK; } +bool RpcState::validateProtocolVersion(uint32_t version) { + if (version == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { +#if defined(__ANDROID__) + char codename[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.codename", codename, ""); + if (!strcmp(codename, "REL")) { + ALOGE("Cannot use experimental RPC binder protocol in a release configuration."); + return false; + } +#else + ALOGE("Cannot use experimental RPC binder protocol outside of Android."); + return false; +#endif + } else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) { + ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol " + "version " + "is %u).", + version, RPC_WIRE_PROTOCOL_VERSION); + return false; + } + + return true; +} + status_t RpcState::readNewSessionResponse(const sp& connection, const sp& session, uint32_t* version) { RpcNewSessionResponse response; @@ -572,25 +601,24 @@ status_t RpcState::transactAddress(const sp& connecti {const_cast(data.data()), data.dataSize()}, objectTableSpan.toIovec(), }; - 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); - } + auto altPoll = [&] { + 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; - } + if (waitUs > 0) { + usleep(waitUs); + waitUs = std::min(kWaitMaxUs, waitUs * 2); + } else { + waitUs = 1; + } - return drainCommands(connection, session, CommandType::CONTROL_ONLY); - }, - rpcFields->mFds.get()); + return drainCommands(connection, session, CommandType::CONTROL_ONLY); + }; + if (status_t status = rpcSend(connection, session, "transaction", iovs, countof(iovs), + std::ref(altPoll), rpcFields->mFds.get()); status != OK) { // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate // errors here, then we may need to undo the binder-sent counts for the transaction as @@ -621,7 +649,7 @@ static void cleanup_reply_data(const uint8_t* data, size_t dataSize, const binde status_t RpcState::waitForReply(const sp& connection, const sp& session, Parcel* reply) { - std::vector> ancillaryFds; + std::vector> ancillaryFds; RpcWireHeader command; while (true) { iovec iov{&command, sizeof(command)}; @@ -662,7 +690,7 @@ status_t RpcState::waitForReply(const sp& connection, {&rpcReply, rpcReplyWireSize}, {data.data(), data.size()}, }; - if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr); + if (status_t status = rpcRec(connection, session, "reply body", iovs, countof(iovs), nullptr); status != OK) return status; @@ -732,14 +760,14 @@ status_t RpcState::sendDecStrongToTarget(const sp& co .bodySize = sizeof(RpcDecStrong), }; iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}}; - return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt); + return rpcSend(connection, session, "dec ref", iovs, countof(iovs), std::nullopt); } status_t RpcState::getAndExecuteCommand(const sp& connection, const sp& session, CommandType type) { LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get()); - std::vector> ancillaryFds; + std::vector> ancillaryFds; RpcWireHeader command; iovec iov{&command, sizeof(command)}; if (status_t status = @@ -768,7 +796,7 @@ status_t RpcState::drainCommands(const sp& connection status_t RpcState::processCommand( const sp& connection, const sp& session, const RpcWireHeader& command, CommandType type, - std::vector>&& ancillaryFds) { + std::vector>&& ancillaryFds) { #ifdef BINDER_WITH_KERNEL_IPC IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull(); IPCThreadState::SpGuard spGuard{ @@ -781,11 +809,11 @@ status_t RpcState::processCommand( origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard); } - base::ScopeGuard guardUnguard = [&]() { + auto guardUnguard = make_scope_guard([&]() { if (kernelBinderState != nullptr) { kernelBinderState->restoreGetCallingSpGuard(origGuard); } - }; + }); #endif // BINDER_WITH_KERNEL_IPC switch (command.command) { @@ -808,7 +836,7 @@ status_t RpcState::processCommand( status_t RpcState::processTransact( const sp& connection, const sp& session, const RpcWireHeader& command, - std::vector>&& ancillaryFds) { + std::vector>&& ancillaryFds) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command); CommandData transactionData(command.bodySize); @@ -835,7 +863,7 @@ static void do_nothing_to_transact_data(const uint8_t* data, size_t dataSize, status_t RpcState::processTransactInternal( const sp& connection, const sp& session, CommandData transactionData, - std::vector>&& ancillaryFds) { + std::vector>&& 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. @@ -1115,7 +1143,7 @@ processTransactInternalTailCall: {const_cast(reply.data()), reply.dataSize()}, objectTableSpan.toIovec(), }; - return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt, + return rpcSend(connection, session, "reply", iovs, countof(iovs), std::nullopt, rpcFields->mFds.get()); } @@ -1190,10 +1218,11 @@ status_t RpcState::validateParcel(const sp& session, const Parcel& p 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); + std::stringstream ss; + ss << "Parcel has attached objects but the session's protocol version (" << protocolVersion + << ") is too old, must be at least " + << RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE; + *errorMsg = ss.str(); return BAD_VALUE; } @@ -1206,9 +1235,10 @@ status_t RpcState::validateParcel(const sp& session, const Parcel& p 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); + std::stringstream ss; + ss << "Too many file descriptors in Parcel for unix domain socket: " + << rpcFields->mFds->size() << " (max is " << kMaxFdsPerMsg << ")"; + *errorMsg = ss.str(); return BAD_VALUE; } break; @@ -1219,9 +1249,10 @@ status_t RpcState::validateParcel(const sp& session, const Parcel& p // available on Android constexpr size_t kMaxFdsPerMsg = 8; if (rpcFields->mFds->size() > kMaxFdsPerMsg) { - *errorMsg = StringPrintf("Too many file descriptors in Parcel for Trusty " - "IPC connection: %zu (max is %zu)", - rpcFields->mFds->size(), kMaxFdsPerMsg); + std::stringstream ss; + ss << "Too many file descriptors in Parcel for Trusty IPC connection: " + << rpcFields->mFds->size() << " (max is " << kMaxFdsPerMsg << ")"; + *errorMsg = ss.str(); return BAD_VALUE; } break; diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 0e23ea7515e3d30e83b01da54646bdf57dd11c66..8b84602feec41f5cf6ce5b93414cff20c5b0f850 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -15,11 +15,12 @@ */ #pragma once -#include +#include #include #include #include #include +#include #include #include @@ -63,6 +64,8 @@ public: RpcState(); ~RpcState(); + [[nodiscard]] static bool validateProtocolVersion(uint32_t version); + [[nodiscard]] status_t readNewSessionResponse(const sp& connection, const sp& session, uint32_t* version); [[nodiscard]] status_t sendConnectionInit(const sp& connection, @@ -188,28 +191,29 @@ private: [[nodiscard]] status_t rpcSend( const sp& connection, const sp& session, const char* what, iovec* iovs, int niovs, - const std::optional>& altPoll, - const std::vector>* ancillaryFds = + const std::optional>& altPoll, + const std::vector>* ancillaryFds = nullptr); - [[nodiscard]] status_t rpcRec( - const sp& connection, const sp& session, - const char* what, iovec* iovs, int niovs, - std::vector>* ancillaryFds = nullptr); + [[nodiscard]] status_t rpcRec(const sp& connection, + const sp& session, const char* what, iovec* iovs, + int niovs, + std::vector>* + ancillaryFds = nullptr); [[nodiscard]] status_t waitForReply(const sp& connection, const sp& session, Parcel* reply); [[nodiscard]] status_t processCommand( const sp& connection, const sp& session, const RpcWireHeader& command, CommandType type, - std::vector>&& ancillaryFds); + std::vector>&& ancillaryFds); [[nodiscard]] status_t processTransact( const sp& connection, const sp& session, const RpcWireHeader& command, - std::vector>&& ancillaryFds); + std::vector>&& ancillaryFds); [[nodiscard]] status_t processTransactInternal( const sp& connection, const sp& session, CommandData transactionData, - std::vector>&& ancillaryFds); + std::vector>&& ancillaryFds); [[nodiscard]] status_t processDecStrong(const sp& connection, const sp& session, const RpcWireHeader& command); @@ -251,7 +255,7 @@ private: struct AsyncTodo { sp ref; CommandData data; - std::vector> ancillaryFds; + std::vector> ancillaryFds; uint64_t asyncNumber = 0; bool operator<(const AsyncTodo& o) const { diff --git a/libs/binder/RpcTlsUtils.cpp b/libs/binder/RpcTlsUtils.cpp index f3ca02a3bd77d4cec5866104ab809bb4459d15a3..d5c86d7227d6165d9f1f739f3cb62470838c2492 100644 --- a/libs/binder/RpcTlsUtils.cpp +++ b/libs/binder/RpcTlsUtils.cpp @@ -21,6 +21,8 @@ #include "Utils.h" +#include + namespace android { namespace { diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index cd067bfee76c30d6eee985397dd24b596cc4d5a6..aa3a6e506d7014f04e0f32b91918d3a18e24fc5f 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -29,7 +30,9 @@ namespace android { -namespace { +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { @@ -56,13 +59,12 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - const std::vector>* ancillaryFds) - override { + const std::optional>& altPoll, + const std::vector>* ancillaryFds) override { bool sentFds = false; auto send = [&](iovec* iovs, int niovs) -> ssize_t { - ssize_t ret = - sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds); + ssize_t ret = binder::os::sendMessageOnSocket(mSocket, iovs, niovs, + sentFds ? nullptr : ancillaryFds); sentFds |= ret > 0; return ret; }; @@ -72,16 +74,16 @@ public: status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - std::vector>* ancillaryFds) override { + const std::optional>& altPoll, + std::vector>* ancillaryFds) override { auto recv = [&](iovec* iovs, int niovs) -> ssize_t { - return receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds); + return binder::os::receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds); }; return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll); } - virtual bool isWaiting() { return mSocket.isInPollingState(); } + bool isWaiting() override { return mSocket.isInPollingState(); } private: android::RpcTransportFd mSocket; @@ -90,14 +92,13 @@ private: // RpcTransportCtx with TLS disabled. class RpcTransportCtxRaw : public RpcTransportCtx { public: - std::unique_ptr newTransport(android::RpcTransportFd socket, FdTrigger*) const { + std::unique_ptr newTransport(android::RpcTransportFd socket, + FdTrigger*) const override { return std::make_unique(std::move(socket)); } std::vector getCertificate(RpcCertificateFormat) const override { return {}; } }; -} // namespace - std::unique_ptr RpcTransportCtxFactoryRaw::newServerCtx() const { return std::make_unique(); } diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp index d5a6da2e3d894643d0e0180458777bcda659c788..3819fb6472b11ad9eeb20ae8440df15723995314 100644 --- a/libs/binder/RpcTransportTipcAndroid.cpp +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -26,13 +26,12 @@ #include "RpcState.h" #include "RpcTransportUtils.h" -using android::base::Error; -using android::base::Result; +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; namespace android { -namespace { - // RpcTransport for writing Trusty IPC clients in Android. class RpcTransportTipcAndroid : public RpcTransport { public: @@ -77,9 +76,8 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - const std::vector>* ancillaryFds) - override { + const std::optional>& altPoll, + const std::vector>* ancillaryFds) override { auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { // TODO: send ancillaryFds. For now, we just abort if anyone tries // to send any. @@ -95,9 +93,8 @@ public: status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - std::vector>* /*ancillaryFds*/) - override { + const std::optional>& altPoll, + std::vector>* /*ancillaryFds*/) override { auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t { // Fill the read buffer at most once per readFn call, then try to // return as much of it as possible. If the input iovecs are spread @@ -217,8 +214,6 @@ public: std::vector getCertificate(RpcCertificateFormat) const override { return {}; } }; -} // namespace - std::unique_ptr RpcTransportCtxFactoryTipcAndroid::newServerCtx() const { return std::make_unique(); } diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index 3e98ecca9bac3c17171dcf2248c6bd3c6076d619..579694c321449ac486d51a71e9c23cccd3140028 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -29,6 +30,8 @@ #include "RpcState.h" #include "Utils.h" +#include + #define SHOULD_LOG_TLS_DETAIL false #if SHOULD_LOG_TLS_DETAIL @@ -38,6 +41,11 @@ #endif namespace android { + +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; + namespace { // Implement BIO for socket that ignores SIGPIPE. @@ -51,7 +59,7 @@ int socketFree(BIO* bio) { return 1; } int socketRead(BIO* bio, char* buf, int size) { - android::base::borrowed_fd fd(static_cast(reinterpret_cast(BIO_get_data(bio)))); + borrowed_fd fd(static_cast(reinterpret_cast(BIO_get_data(bio)))); int ret = TEMP_FAILURE_RETRY(::recv(fd.get(), buf, size, MSG_NOSIGNAL)); BIO_clear_retry_flags(bio); if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -61,7 +69,7 @@ int socketRead(BIO* bio, char* buf, int size) { } int socketWrite(BIO* bio, const char* buf, int size) { - android::base::borrowed_fd fd(static_cast(reinterpret_cast(BIO_get_data(bio)))); + borrowed_fd fd(static_cast(reinterpret_cast(BIO_get_data(bio)))); int ret = TEMP_FAILURE_RETRY(::send(fd.get(), buf, size, MSG_NOSIGNAL)); BIO_clear_retry_flags(bio); if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -71,13 +79,13 @@ int socketWrite(BIO* bio, const char* buf, int size) { } long socketCtrl(BIO* bio, int cmd, long num, void*) { // NOLINT - android::base::borrowed_fd fd(static_cast(reinterpret_cast(BIO_get_data(bio)))); + borrowed_fd fd(static_cast(reinterpret_cast(BIO_get_data(bio)))); if (cmd == BIO_CTRL_FLUSH) return 1; LOG_ALWAYS_FATAL("sockCtrl(fd=%d, %d, %ld)", fd.get(), cmd, num); return 0; } -bssl::UniquePtr newSocketBio(android::base::borrowed_fd fd) { +bssl::UniquePtr newSocketBio(borrowed_fd fd) { static const BIO_METHOD* gMethods = ([] { auto methods = BIO_meth_new(BIO_get_new_index(), "socket_no_signal"); LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_write(methods, socketWrite), "BIO_meth_set_write"); @@ -181,10 +189,9 @@ public: // |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( - const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, - const char* fnString, int additionalEvent, - const std::optional>& altPoll) { + status_t pollForSslError(const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, + const char* fnString, int additionalEvent, + const std::optional>& altPoll) { switch (sslError) { case SSL_ERROR_WANT_READ: return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); @@ -200,7 +207,7 @@ private: status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger, const char* fnString, - const std::optional>& altPoll) { + const std::optional>& altPoll) { status_t ret; if (altPoll) { ret = (*altPoll)(); @@ -275,6 +282,8 @@ private: bssl::UniquePtr mSsl; }; +} // namespace + class RpcTransportTls : public RpcTransport { public: RpcTransportTls(RpcTransportFd socket, Ssl ssl) @@ -282,15 +291,14 @@ public: status_t pollRead(void) override; status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - const std::vector>* ancillaryFds) - override; + const std::optional>& altPoll, + const std::vector>* ancillaryFds) override; status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - std::vector>* ancillaryFds) override; + const std::optional>& altPoll, + std::vector>* ancillaryFds) override; - bool isWaiting() { return mSocket.isInPollingState(); }; + bool isWaiting() override { return mSocket.isInPollingState(); }; private: android::RpcTransportFd mSocket; @@ -318,8 +326,8 @@ status_t RpcTransportTls::pollRead(void) { status_t RpcTransportTls::interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - const std::vector>* ancillaryFds) { + const std::optional>& altPoll, + const std::vector>* ancillaryFds) { (void)ancillaryFds; MAYBE_WAIT_IN_FLAKE_MODE; @@ -364,8 +372,8 @@ status_t RpcTransportTls::interruptableWriteFully( status_t RpcTransportTls::interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional>& altPoll, - std::vector>* ancillaryFds) { + const std::optional>& altPoll, + std::vector>* ancillaryFds) { (void)ancillaryFds; MAYBE_WAIT_IN_FLAKE_MODE; @@ -411,7 +419,8 @@ status_t RpcTransportTls::interruptableReadFully( } // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|. -bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, FdTrigger* fdTrigger) { +static bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, + FdTrigger* fdTrigger) { bssl::UniquePtr bio = newSocketBio(socket.fd); TEST_AND_RETURN(false, bio != nullptr); auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get()); @@ -540,8 +549,6 @@ protected: } }; -} // namespace - std::unique_ptr RpcTransportCtxFactoryTls::newServerCtx() const { return android::RpcTransportCtxTls::create(mCertVerifier, mAuth.get()); diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h index 32f0db805d9fc5eab91c6ed1d0f44a7c91983a24..fcf6675588700fc409eea6642573a847f4973112 100644 --- a/libs/binder/RpcTransportUtils.h +++ b/libs/binder/RpcTransportUtils.h @@ -15,7 +15,7 @@ */ #pragma once -#include +#include #include #include "FdTrigger.h" @@ -27,7 +27,7 @@ template status_t interruptableReadOrWrite( const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs, SendOrReceive sendOrReceiveFun, const char* funName, int16_t event, - const std::optional>& altPoll) { + const std::optional>& altPoll) { MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) { diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp index 3b53b0599146eedacfd602795cd7ef0caba5a4c8..a445196ca901fd565489f75232d5cf7f91a1b0d6 100644 --- a/libs/binder/RpcTrusty.cpp +++ b/libs/binder/RpcTrusty.cpp @@ -16,15 +16,14 @@ #define LOG_TAG "RpcTrusty" -#include -#include #include #include +#include #include namespace android { -using android::base::unique_fd; +using android::binder::unique_fd; sp RpcTrustyConnectWithSessionInitializer( const char* device, const char* port, @@ -35,13 +34,13 @@ sp RpcTrustyConnectWithSessionInitializer( auto request = [=] { int tipcFd = tipc_connect(device, port); if (tipcFd < 0) { - LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd; + ALOGE("Failed to connect to Trusty service. Error code: %d", tipcFd); return unique_fd(); } return unique_fd(tipcFd); }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { - LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Trusty client. Error: %s", statusToString(status).c_str()); return nullptr; } return session; diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp index 2b67f030e0959e510761e20cae86971dc0d3cb12..9482e3e1184546c88c2b83533a126c0c8607fdef 100644 --- a/libs/binder/ServiceManagerHost.cpp +++ b/libs/binder/ServiceManagerHost.cpp @@ -56,16 +56,16 @@ public: [[nodiscard]] const std::optional& hostPort() const { return mPort; } private: - DISALLOW_COPY_AND_ASSIGN(AdbForwarder); + AdbForwarder(const AdbForwarder&) = delete; + void operator=(const AdbForwarder&) = delete; explicit AdbForwarder(unsigned int port) : mPort(port) {} std::optional mPort; }; std::optional AdbForwarder::forward(unsigned int devicePort) { auto result = execute({"adb", "forward", "tcp:0", "tcp:" + std::to_string(devicePort)}, nullptr); - if (!result.ok()) { - ALOGE("Unable to run `adb forward tcp:0 tcp:%d`: %s", devicePort, - result.error().message().c_str()); + if (!result.has_value()) { + ALOGE("Unable to run `adb forward tcp:0 tcp:%d`", devicePort); return std::nullopt; } // Must end with exit code 0 (`has_value() && value() == 0`) @@ -94,9 +94,8 @@ AdbForwarder::~AdbForwarder() { if (!mPort.has_value()) return; auto result = execute({"adb", "forward", "--remove", "tcp:" + std::to_string(*mPort)}, nullptr); - if (!result.ok()) { - ALOGE("Unable to run `adb forward --remove tcp:%d`: %s", *mPort, - result.error().message().c_str()); + if (!result.has_value()) { + ALOGE("Unable to run `adb forward --remove tcp:%d`", *mPort); return; } // Must end with exit code 0 (`has_value() && value() == 0`) @@ -130,8 +129,7 @@ sp getDeviceService(std::vector&& serviceDispatcherArgs, serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end()); auto result = execute(std::move(serviceDispatcherArgs), &CommandResult::stdoutEndsWithNewLine); - if (!result.ok()) { - ALOGE("%s", result.error().message().c_str()); + if (!result.has_value()) { return nullptr; } diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h index c5310dac2045480ac84b77f468aa20c0b18cca02..941ba3a7577da874de39ec30b2024860bc424f70 100644 --- a/libs/binder/ServiceManagerHost.h +++ b/libs/binder/ServiceManagerHost.h @@ -16,7 +16,6 @@ #pragma once -#include #include namespace android { diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp index 2d05fb2505cab866316928c4940bd2212667d80b..c432b3af293f0253d30aa1bdc0cf222c3406f383 100644 --- a/libs/binder/Stability.cpp +++ b/libs/binder/Stability.cpp @@ -75,7 +75,7 @@ void Stability::tryMarkCompilationUnit(IBinder* binder) { Stability::Level Stability::getLocalLevel() { #ifdef __ANDROID_APEX__ -#error APEX can't use libbinder (must use libbinder_ndk) +#error "APEX can't use libbinder (must use libbinder_ndk)" #endif #ifdef __ANDROID_VNDK__ diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 0e8e18747bb6012fd0ec16ff06caccac2735be34..2b3ff4407a1ff0783fdc8784148b375e8db01029 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -15,9 +15,15 @@ { "name": "binderDriverInterfaceTest" }, + { + "name": "binderRecordReplayTest" + }, { "name": "binderHostDeviceTest" }, + { + "name": "binderParcelBenchmark" + }, { "name": "binderTextOutputTest" }, @@ -57,6 +63,9 @@ { "name": "libbinderthreadstateutils_test" }, + { + "name": "fuzz_service_test" + }, { "name": "CtsOsTestCases", "options": [ diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp index 0314b0fea7d018ec7753de28b12474d0035527b7..d9a96af80a85277afe8a0f50c43011ceac7d482a 100644 --- a/libs/binder/Utils.cpp +++ b/libs/binder/Utils.cpp @@ -24,4 +24,22 @@ void zeroMemory(uint8_t* data, size_t size) { memset(data, 0, size); } +std::string HexString(const void* bytes, size_t len) { + LOG_ALWAYS_FATAL_IF(len > 0 && bytes == nullptr, "%p %zu", bytes, len); + + // b/132916539: Doing this the 'C way', std::setfill triggers ubsan implicit conversion + const uint8_t* bytes8 = static_cast(bytes); + const char chars[] = "0123456789abcdef"; + std::string result; + result.resize(len * 2); + + for (size_t i = 0; i < len; i++) { + const auto c = bytes8[i]; + result[2 * i] = chars[c >> 4]; + result[2 * i + 1] = chars[c & 0xf]; + } + + return result; +} + } // namespace android diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index e04199c75a40de87ffbc9a88fa8159faff10bb06..eec09eb859185342e9db1f2b27ecd8fccebba47f 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include #include #include @@ -22,6 +24,30 @@ #include #include +#define PLOGE(fmt, ...) \ + do { \ + auto savedErrno = errno; \ + ALOGE(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \ + } while (0) +#define PLOGF(fmt, ...) \ + do { \ + auto savedErrno = errno; \ + LOG_ALWAYS_FATAL(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \ + } while (0) + +/* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) \ + ({ \ + __typeof__(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) +#endif + #define TEST_AND_RETURN(value, expr) \ do { \ if (!(expr)) { \ @@ -32,6 +58,17 @@ namespace android { +/** + * Get the size of a statically initialized array. + * + * \param N the array to get the size of. + * \return the size of the array. + */ +template +constexpr size_t countof(T (&)[N]) { + return N; +} + // avoid optimizations void zeroMemory(uint8_t* data, size_t size); @@ -70,4 +107,10 @@ struct Span { } }; +// Converts binary data into a hexString. +// +// Hex values are printed in order, e.g. 0xDEAD will result in 'adde' because +// Android is little-endian. +std::string HexString(const void* bytes, size_t len); + } // namespace android diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp index 52b8f69d362cef48bebf4314cf2ee186f0383334..ae1a6c44d758ef89c5deefcbeb1dc3ee9c00d36b 100644 --- a/libs/binder/UtilsHost.cpp +++ b/libs/binder/UtilsHost.cpp @@ -25,8 +25,13 @@ #include +#include "FdUtils.h" +#include "Utils.h" + namespace android { +using android::binder::unique_fd; + CommandResult::~CommandResult() { if (!pid.has_value()) return; if (*pid == 0) { @@ -72,8 +77,8 @@ std::string CommandResult::toString() const { return ss.str(); } -android::base::Result execute(std::vector argStringVec, - const std::function& end) { +std::optional execute(std::vector argStringVec, + const std::function& end) { // turn vector into null-terminated char* vector. std::vector argv; argv.reserve(argStringVec.size() + 1); @@ -81,15 +86,22 @@ android::base::Result execute(std::vector argStringV argv.push_back(nullptr); CommandResult ret; - android::base::unique_fd outWrite; - if (!android::base::Pipe(&ret.outPipe, &outWrite)) - return android::base::ErrnoError() << "pipe() for outPipe"; - android::base::unique_fd errWrite; - if (!android::base::Pipe(&ret.errPipe, &errWrite)) - return android::base::ErrnoError() << "pipe() for errPipe"; + unique_fd outWrite; + if (!binder::Pipe(&ret.outPipe, &outWrite)) { + PLOGE("pipe() for outPipe"); + return {}; + } + unique_fd errWrite; + if (!binder::Pipe(&ret.errPipe, &errWrite)) { + PLOGE("pipe() for errPipe"); + return {}; + } int pid = fork(); - if (pid == -1) return android::base::ErrnoError() << "fork()"; + if (pid == -1) { + PLOGE("fork()"); + return {}; + } if (pid == 0) { // child ret.outPipe.reset(); @@ -111,7 +123,7 @@ android::base::Result execute(std::vector argStringV errWrite.reset(); ret.pid = pid; - auto handlePoll = [](android::base::unique_fd* fd, const pollfd* pfd, std::string* s) { + auto handlePoll = [](unique_fd* fd, const pollfd* pfd, std::string* s) { if (!fd->ok()) return true; if (pfd->revents & POLLIN) { char buf[1024]; @@ -140,12 +152,19 @@ android::base::Result execute(std::vector argStringV *errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN}; } int pollRet = poll(fds, nfds, 1000 /* ms timeout */); - if (pollRet == -1) return android::base::ErrnoError() << "poll()"; + if (pollRet == -1) { + PLOGE("poll()"); + return {}; + } - if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) - return android::base::ErrnoError() << "read(stdout)"; - if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) - return android::base::ErrnoError() << "read(stderr)"; + if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) { + PLOGE("read(stdout)"); + return {}; + } + if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) { + PLOGE("read(stderr)"); + return {}; + } if (end && end(ret)) return ret; } @@ -154,7 +173,10 @@ android::base::Result execute(std::vector argStringV while (ret.pid.has_value()) { int status; auto exitPid = waitpid(pid, &status, 0); - if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")"; + if (exitPid == -1) { + PLOGE("waitpid(%d)", pid); + return {}; + } if (exitPid == pid) { if (WIFEXITED(status)) { ret.pid = std::nullopt; diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h index 98ac4e0c48a9d571ed1408f4d5b6a48a7cf92f79..b582f17c1acd2d22a2be6a70363e4af7b3139780 100644 --- a/libs/binder/UtilsHost.h +++ b/libs/binder/UtilsHost.h @@ -23,8 +23,8 @@ #include #include -#include -#include +#include +#include /** * Log a lot more information about host-device binder communication, when debugging issues. @@ -46,8 +46,8 @@ struct CommandResult { std::string stdoutStr; std::string stderrStr; - android::base::unique_fd outPipe; - android::base::unique_fd errPipe; + binder::unique_fd outPipe; + binder::unique_fd errPipe; CommandResult() = default; CommandResult(CommandResult&& other) noexcept { (*this) = std::move(other); } @@ -67,7 +67,8 @@ struct CommandResult { } private: - DISALLOW_COPY_AND_ASSIGN(CommandResult); + CommandResult(const CommandResult&) = delete; + void operator=(const CommandResult&) = delete; }; std::ostream& operator<<(std::ostream& os, const CommandResult& res); @@ -94,6 +95,6 @@ std::ostream& operator<<(std::ostream& os, const CommandResult& res); // // If the parent process has encountered any errors for system calls, return ExecuteError with // the proper errno set. -android::base::Result execute(std::vector argStringVec, - const std::function& end); +std::optional execute(std::vector argStringVec, + const std::function& end); } // namespace android diff --git a/libs/binder/file.cpp b/libs/binder/file.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e450b90b72539260822c4bbebe9e65c3c040660 --- /dev/null +++ b/libs/binder/file.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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 "file.h" + +#ifdef BINDER_NO_LIBBASE + +#include "Utils.h" + +#include + +// clang-format off + +namespace android::binder { + +bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) { + uint8_t* p = reinterpret_cast(data); + size_t remaining = byte_count; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining)); + if (n == 0) { // EOF + errno = ENODATA; + return false; + } + if (n == -1) return false; + p += n; + remaining -= n; + } + return true; +} + +bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) { + const uint8_t* p = reinterpret_cast(data); + size_t remaining = byte_count; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining)); + if (n == -1) return false; + p += n; + remaining -= n; + } + return true; +} + +} // namespace android::binder + +#endif // BINDER_NO_LIBBASE diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/binder/file.h similarity index 52% rename from libs/renderengine/include/renderengine/Framebuffer.h rename to libs/binder/file.h index 65111278e0c719601942f9dd74f7fb3667ce5152..bcbfa31836d7e965851794a75ddea3a60a78e12f 100644 --- a/libs/renderengine/include/renderengine/Framebuffer.h +++ b/libs/binder/file.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright (C) 2023 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. @@ -16,20 +16,26 @@ #pragma once -#include +#ifndef BINDER_NO_LIBBASE -struct ANativeWindowBuffer; +#include -namespace android { -namespace renderengine { +namespace android::binder { +using android::base::ReadFully; +using android::base::WriteFully; +} // namespace android::binder -class Framebuffer { -public: - virtual ~Framebuffer() = default; +#else // BINDER_NO_LIBBASE - virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, - const bool useFramebufferCache) = 0; -}; +#include -} // namespace renderengine -} // namespace android +#include + +namespace android::binder { + +bool ReadFully(borrowed_fd fd, void* data, size_t byte_count); +bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count); + +} // namespace android::binder + +#endif // BINDER_NO_LIBBASE diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index d960a0b894735d6cfff8eef01534e230ade725d6..7a65ff4e45fa6cef74b23e97bd557c8cd366faf9 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -102,15 +102,9 @@ public: // to another process. void setParceled(); - [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd, + [[nodiscard]] status_t setRpcClientDebug(binder::unique_fd clientFd, const sp& keepAliveBinder); - // Start recording transactions to the unique_fd in data. - // See RecordedTransaction.h for more details. - [[nodiscard]] status_t startRecordingTransactions(const Parcel& data); - // Stop the current recording. - [[nodiscard]] status_t stopRecordingTransactions(); - protected: virtual ~BBinder(); @@ -131,6 +125,8 @@ private: [[nodiscard]] status_t setRpcClientDebug(const Parcel& data); void removeRpcServerLink(const sp& link); + [[nodiscard]] status_t startRecordingTransactions(const Parcel& data); + [[nodiscard]] status_t stopRecordingTransactions(); std::atomic mExtras; diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 5496d61b708455ac1794e344237ff8cd49451671..89a4d273ad8a00c8ceb73015bea7e2f8640b6513 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -16,11 +16,12 @@ #pragma once -#include #include -#include +#include +#include #include +#include #include #include @@ -87,12 +88,13 @@ public: static void setCountByUidEnabled(bool enable); static void setLimitCallback(binder_proxy_limit_callback cb); static void setBinderProxyCountWatermarks(int high, int low); + static uint32_t getBinderProxyCount(); std::optional getDebugBinderHandle() const; // Start recording transactions to the unique_fd. // See RecordedTransaction.h for more details. - status_t startRecordingBinder(const android::base::unique_fd& fd); + status_t startRecordingBinder(const binder::unique_fd& fd); // Stop the current recording. status_t stopRecordingBinder(); @@ -191,7 +193,7 @@ private: void reportOneDeath(const Obituary& obit); bool isDescriptorCached() const; - mutable Mutex mLock; + mutable RpcMutex mLock; volatile int32_t mAlive; volatile int32_t mObitsSent; Vector* mObituaries; @@ -199,7 +201,7 @@ private: mutable String16 mDescriptorCache; int32_t mTrackedUid; - static Mutex sTrackingLock; + static RpcMutex sTrackingLock; static std::unordered_map sTrackingMap; static int sNumTrackedUids; static std::atomic_bool sCountByUidEnabled; @@ -208,6 +210,8 @@ private: static uint32_t sBinderProxyCountLowWatermark; static bool sBinderProxyThrottleCreate; static std::unordered_map sLastLimitCallbackMap; + static std::atomic sBinderProxyCount; + static std::atomic sBinderProxyCountWarned; }; } // namespace android diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h index 8b3fc1cc10ca9ae612393c60db3313ca9f588a34..ad5a6a3e88e28ba4da33afedb0038812ec9902a2 100644 --- a/libs/binder/include/binder/Delegate.h +++ b/libs/binder/include/binder/Delegate.h @@ -18,6 +18,11 @@ #include +#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT) +#include +#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message) +#endif + #ifndef __BIONIC__ #ifndef __assert diff --git a/libs/binder/include/binder/Functional.h b/libs/binder/include/binder/Functional.h new file mode 100644 index 0000000000000000000000000000000000000000..08e3b214da1e8a932d0f1e9f65e4f9aa76197be7 --- /dev/null +++ b/libs/binder/include/binder/Functional.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 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 +#include + +namespace android::binder::impl { + +template +constexpr void assert_small_callable() { + // While this buffer (std::function::__func::__buf_) is an implementation detail generally not + // accessible to users, it's a good bet to assume its size to be around 3 pointers. + constexpr size_t kFunctionBufferSize = 3 * sizeof(void*); + + static_assert(sizeof(F) <= kFunctionBufferSize, + "Supplied callable is larger than std::function optimization buffer. " + "Try using std::ref, but make sure lambda lives long enough to be called."); +} + +template +std::unique_ptr> make_scope_guard(F&& f) { + assert_small_callable(); + return {reinterpret_cast(true), std::bind(f)}; +} + +template +class SmallFunction : public std::function { +public: + template + SmallFunction(F&& f) : std::function(f) { + assert_small_callable(); + } +}; + +} // namespace android::binder::impl diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index e75d548bd873aae159ecd5a4b5636e885045513e..dad9a1782db931aa80bbde37b09153367a08f467 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -16,7 +16,7 @@ #pragma once -#include +#include #include #include #include @@ -175,7 +175,7 @@ public: * * On death of @a keepAliveBinder, the RpcServer shuts down. */ - [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd socketFd, + [[nodiscard]] status_t setRpcClientDebug(binder::unique_fd socketFd, const sp& keepAliveBinder); // NOLINTNEXTLINE(google-default-arguments) diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index dc572ac953470e1aeedefb8c82f50d4c42516e59..ac845bc003924ed2cc82bd4894f60e06cdaa84a7 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -119,8 +119,8 @@ public: "The preferred way to add interfaces is to define " \ "an .aidl file to auto-generate the interface. If " \ "an interface must be manually written, add its " \ - "name to the whitelist."); \ - DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + "name to the allowlist."); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) #else @@ -305,10 +305,10 @@ constexpr bool equals(const char* a, const char* b) { return equals(a + 1, b + 1); } -constexpr bool inList(const char* a, const char* const* whitelist) { - if (*whitelist == nullptr) return false; - if (equals(a, *whitelist)) return true; - return inList(a, whitelist + 1); +constexpr bool inList(const char* a, const char* const* allowlist) { + if (*allowlist == nullptr) return false; + if (equals(a, *allowlist)) return true; + return inList(a, allowlist + 1); } constexpr bool allowedManualInterface(const char* name) { diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index d261c2143c53362bee900970edc26898fd8ffc16..9347ce47a5c407e1f6e55b57e7ea2478384eaa7d 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -147,7 +147,12 @@ public: void flushCommands(); bool flushIfNeeded(); - // For main functions - dangerous for libraries to use + // Adds the current thread into the binder threadpool. + // + // This is in addition to any threads which are started + // with startThreadPool. Libraries should not call this + // function, as they may be loaded into processes which + // try to configure the threadpool differently. void joinThreadPool(bool isMain = true); // Stop the local process. diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h index 2e22b84ff01da2db63d8db8e3fd25ad2c4715a8f..bda3d19ee11b9729e44f353da06afa5abc37efdd 100644 --- a/libs/binder/include/binder/LazyServiceRegistrar.h +++ b/libs/binder/include/binder/LazyServiceRegistrar.h @@ -93,7 +93,17 @@ class LazyServiceRegistrar { */ void reRegister(); - private: + /** + * Create a second instance of lazy service registrar. + * + * WARNING: dangerous! DO NOT USE THIS - LazyServiceRegistrar + * should be single-instanced, so that the service will only + * shut down when all services are unused. A separate instance + * is only used to test race conditions. + */ + static LazyServiceRegistrar createExtraTestInstance(); + + private: std::shared_ptr mClientCC; LazyServiceRegistrar(); }; diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 162cd406dc163345d515759d7ea04be100243d1a..09da6e3c4a0d4b5a5991cde309940bdc395dc0a2 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -17,30 +17,28 @@ #pragma once #include +#include #include // for legacy reasons +#include #include #include #include #include -#include +#include +#ifndef BINDER_DISABLE_NATIVE_HANDLE #include +#endif #include #include #include #include -#include #include #include -#ifdef BINDER_IPC_32BIT -//NOLINTNEXTLINE(google-runtime-int) b/173188702 -typedef unsigned int binder_size_t; -#else //NOLINTNEXTLINE(google-runtime-int) b/173188702 typedef unsigned long long binder_size_t; -#endif struct flat_binder_object; @@ -154,6 +152,11 @@ public: // This Api is used by fuzzers to skip dataAvail checks. void setEnforceNoDataAvail(bool enforceNoDataAvail); + // When fuzzing, we want to remove certain ABI checks that cause significant + // lost coverage, and we also want to avoid logs that cost too much to write. + void setServiceFuzzing(); + bool isServiceFuzzing() const; + void freeData(); size_t objectsCount() const; @@ -266,7 +269,8 @@ public: status_t writeEnumVector(const std::optional>& val) { return writeData(val); } template && std::is_same_v,int8_t>, bool> = 0> - status_t writeEnumVector(const std::unique_ptr>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeEnumVector(const std::unique_ptr>& val) { return writeData(val); } // Write an Enum vector with underlying type != int8_t. template && !std::is_same_v,int8_t>, bool> = 0> @@ -276,17 +280,20 @@ public: status_t writeEnumVector(const std::optional>& val) { return writeData(val); } template && !std::is_same_v,int8_t>, bool> = 0> - status_t writeEnumVector(const std::unique_ptr>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeEnumVector(const std::unique_ptr>& val) { return writeData(val); } template status_t writeParcelableVector(const std::optional>>& val) { return writeData(val); } template - status_t writeParcelableVector(const std::unique_ptr>>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeParcelableVector(const std::unique_ptr>>& val) { return writeData(val); } template - status_t writeParcelableVector(const std::shared_ptr>>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeParcelableVector(const std::shared_ptr>>& val) { return writeData(val); } template status_t writeParcelableVector(const std::shared_ptr>>& val) @@ -318,11 +325,13 @@ public: template status_t writeVectorSize(const std::unique_ptr>& val) __attribute__((deprecated("use std::optional version instead"))); +#ifndef BINDER_DISABLE_NATIVE_HANDLE // Place a native_handle into the parcel (the native_handle's file- // descriptors are dup'ed, so it is safe to delete the native_handle // when this function returns). // Doesn't take ownership of the native_handle. status_t writeNativeHandle(const native_handle* handle); +#endif // Place a file descriptor into the parcel. The given fd must remain // valid for the lifetime of the parcel. @@ -345,17 +354,16 @@ public: // Place a file descriptor into the parcel. This will not affect the // semantics of the smart file descriptor. A new descriptor will be // created, and will be closed when the parcel is destroyed. - status_t writeUniqueFileDescriptor( - const base::unique_fd& fd); + status_t writeUniqueFileDescriptor(const binder::unique_fd& fd); // Place a vector of file desciptors into the parcel. Each descriptor is // dup'd as in writeDupFileDescriptor - status_t writeUniqueFileDescriptorVector( - const std::optional>& val); - status_t writeUniqueFileDescriptorVector( - const std::unique_ptr>& val) __attribute__((deprecated("use std::optional version instead"))); - status_t writeUniqueFileDescriptorVector( - const std::vector& val); + status_t writeUniqueFileDescriptorVector( + const std::optional>& val); + status_t writeUniqueFileDescriptorVector( + const std::unique_ptr>& val) + __attribute__((deprecated("use std::optional version instead"))); + status_t writeUniqueFileDescriptorVector(const std::vector& val); // Writes a blob to the parcel. // If the blob is small, then it is stored in-place, otherwise it is @@ -422,7 +430,8 @@ public: status_t readEnumVector(std::vector* val) const { return readData(val); } template && std::is_same_v,int8_t>, bool> = 0> - status_t readEnumVector(std::unique_ptr>* val) const __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t readEnumVector(std::unique_ptr>* val) const { return readData(val); } template && std::is_same_v,int8_t>, bool> = 0> status_t readEnumVector(std::optional>* val) const @@ -432,7 +441,8 @@ public: status_t readEnumVector(std::vector* val) const { return readData(val); } template && !std::is_same_v,int8_t>, bool> = 0> - status_t readEnumVector(std::unique_ptr>* val) const __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t readEnumVector(std::unique_ptr>* val) const { return readData(val); } template && !std::is_same_v,int8_t>, bool> = 0> status_t readEnumVector(std::optional>* val) const @@ -443,8 +453,9 @@ public: std::optional>>* val) const { return readData(val); } template + [[deprecated("use std::optional version instead")]] // status_t readParcelableVector( - std::unique_ptr>>* val) const __attribute__((deprecated("use std::optional version instead"))) + std::unique_ptr>>* val) const { return readData(val); } template status_t readParcelableVector(std::vector* val) const @@ -550,13 +561,14 @@ public: // response headers rather than doing it by hand. int32_t readExceptionCode() const; +#ifndef BINDER_DISABLE_NATIVE_HANDLE // Retrieve native_handle from the parcel. This returns a copy of the // parcel's native_handle (the caller takes ownership). The caller - // must free the native_handle with native_handle_close() and + // must free the native_handle with native_handle_close() and // native_handle_delete(). native_handle* readNativeHandle() const; +#endif - // Retrieve a file descriptor from the parcel. This returns the raw fd // in the parcel, which you do not own -- use dup() to get your own copy. int readFileDescriptor() const; @@ -566,20 +578,17 @@ public: int readParcelFileDescriptor() const; // Retrieve a smart file descriptor from the parcel. - status_t readUniqueFileDescriptor( - base::unique_fd* val) const; + status_t readUniqueFileDescriptor(binder::unique_fd* val) const; // Retrieve a Java "parcel file descriptor" from the parcel. - status_t readUniqueParcelFileDescriptor(base::unique_fd* val) const; - + status_t readUniqueParcelFileDescriptor(binder::unique_fd* val) const; // Retrieve a vector of smart file descriptors from the parcel. - status_t readUniqueFileDescriptorVector( - std::optional>* val) const; - status_t readUniqueFileDescriptorVector( - std::unique_ptr>* val) const __attribute__((deprecated("use std::optional version instead"))); - status_t readUniqueFileDescriptorVector( - std::vector* val) const; + status_t readUniqueFileDescriptorVector( + std::optional>* val) const; + status_t readUniqueFileDescriptorVector(std::unique_ptr>* val) + const __attribute__((deprecated("use std::optional version instead"))); + status_t readUniqueFileDescriptorVector(std::vector* val) const; // Reads a blob from the parcel. // The caller should call release() on the blob after reading its contents. @@ -616,7 +625,7 @@ private: status_t rpcSetDataReference( const sp& session, const uint8_t* data, size_t dataSize, const uint32_t* objectTable, size_t objectTableSize, - std::vector>&& ancillaryFds, + std::vector>&& ancillaryFds, release_func relFunc); status_t finishWrite(size_t len); @@ -693,7 +702,7 @@ private: // 5) Nullable objects contained in std::optional, std::unique_ptr, or std::shared_ptr. // // And active objects from the Android ecosystem such as: - // 6) File descriptors, base::unique_fd (kernel object handles) + // 6) File descriptors, unique_fd (kernel object handles) // 7) Binder objects, sp (active Android RPC handles) // // Objects from (1) through (5) serialize into the mData buffer. @@ -944,9 +953,7 @@ private: return writeUtf8AsUtf16(t); } - status_t writeData(const base::unique_fd& t) { - return writeUniqueFileDescriptor(t); - } + status_t writeData(const binder::unique_fd& t) { return writeUniqueFileDescriptor(t); } status_t writeData(const Parcelable& t) { // std::is_base_of_v // implemented here. writeParcelable() calls this. @@ -1093,9 +1100,7 @@ private: return readUtf8FromUtf16(t); } - status_t readData(base::unique_fd* t) const { - return readUniqueFileDescriptor(t); - } + status_t readData(binder::unique_fd* t) const { return readUniqueFileDescriptor(t); } status_t readData(Parcelable* t) const { // std::is_base_of_v // implemented here. readParcelable() calls this. @@ -1280,6 +1285,7 @@ private: // Fields only needed when parcelling for "kernel Binder". struct KernelFields { + KernelFields() {} binder_size_t* mObjects = nullptr; size_t mObjectsSize = 0; size_t mObjectsCapacity = 0; @@ -1314,7 +1320,7 @@ private: // same order as `mObjectPositions`. // // Boxed to save space. Lazy allocated. - std::unique_ptr>> mFds; + std::unique_ptr>> mFds; }; std::variant mVariantFields; @@ -1335,6 +1341,7 @@ private: // Set this to false to skip dataAvail checks. bool mEnforceNoDataAvail; + bool mServiceFuzzing; release_func mOwner; diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h index 08d8e43106af03a1141d03a0aad24767a0463d6e..c4ef3547e9ef59176b8479612677f19e08040616 100644 --- a/libs/binder/include/binder/ParcelFileDescriptor.h +++ b/libs/binder/include/binder/ParcelFileDescriptor.h @@ -16,9 +16,9 @@ #pragma once -#include #include #include +#include namespace android { namespace os { @@ -29,14 +29,14 @@ namespace os { class ParcelFileDescriptor : public android::Parcelable { public: ParcelFileDescriptor(); - explicit ParcelFileDescriptor(android::base::unique_fd fd); + explicit ParcelFileDescriptor(binder::unique_fd fd); ParcelFileDescriptor(ParcelFileDescriptor&& other) noexcept : mFd(std::move(other.mFd)) { } ParcelFileDescriptor& operator=(ParcelFileDescriptor&& other) noexcept = default; ~ParcelFileDescriptor() override; int get() const { return mFd.get(); } - android::base::unique_fd release() { return std::move(mFd); } - void reset(android::base::unique_fd fd = android::base::unique_fd()) { mFd = std::move(fd); } + binder::unique_fd release() { return std::move(mFd); } + void reset(binder::unique_fd fd = binder::unique_fd()) { mFd = std::move(fd); } // android::Parcelable override: android::status_t writeToParcel(android::Parcel* parcel) const override; @@ -62,7 +62,7 @@ public: return mFd.get() >= rhs.mFd.get(); } private: - android::base::unique_fd mFd; + binder::unique_fd mFd; }; } // namespace os diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index ce578e3f5c221d5c469587555339be93e71e56a1..3672702fe1553a3f889bedd0c31940ecc2ed7bc8 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -17,13 +17,13 @@ #pragma once #include -#include -#include #include #include #include +#include + // --------------------------------------------------------------------------- namespace android { @@ -52,10 +52,29 @@ public: sp getContextObject(const sp& caller); - // For main functions - dangerous for libraries to use + // This should be called before startThreadPool at the beginning + // of a program, and libraries should never call it because programs + // should configure their own threadpools. The threadpool size can + // never be decreased. + // + // The 'maxThreads' value refers to the total number of threads + // that will be started by the kernel. This is in addition to any + // threads started by 'startThreadPool' or 'joinRpcThreadpool'. + status_t setThreadPoolMaxThreadCount(size_t maxThreads); + + // Libraries should not call this, as processes should configure + // threadpools themselves. Should be called in the main function + // directly before any code executes or joins the threadpool. + // + // Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount, + // PLUS those manually requested in joinThreadPool. + // + // For instance, if setThreadPoolMaxCount(3) is called and + // startThreadpPool (+1 thread) and joinThreadPool (+1 thread) + // are all called, then up to 5 threads can be started. void startThreadPool(); - bool becomeContextManager(); + [[nodiscard]] bool becomeContextManager(); sp getStrongProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); @@ -63,8 +82,6 @@ public: // TODO: deprecate. void spawnPooledThread(bool isMain); - // For main functions - dangerous for libraries to use - status_t setThreadPoolMaxThreadCount(size_t maxThreads); status_t enableOnewaySpamDetection(bool enable); // Set the name of the current thread to look like a threadpool @@ -161,7 +178,7 @@ private: // Time when thread pool was emptied int64_t mStarvationStartTimeMs; - mutable Mutex mLock; // protects everything below. + mutable std::mutex mLock; // protects everything below. Vector mHandleToObject; diff --git a/libs/binder/include/binder/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h index eb765fe8ec9ebf9ba75fee7ac5c90ba59220b9e3..505c1992b9590c380cd47d0ceaa7636f3883272a 100644 --- a/libs/binder/include/binder/RecordedTransaction.h +++ b/libs/binder/include/binder/RecordedTransaction.h @@ -16,8 +16,8 @@ #pragma once -#include #include +#include #include namespace android { @@ -31,7 +31,8 @@ namespace binder::debug { class RecordedTransaction { public: // Filled with the first transaction from fd. - static std::optional fromFile(const android::base::unique_fd& fd); + + static std::optional fromFile(const binder::unique_fd& fd); // Filled with the arguments. static std::optional fromDetails(const String16& interfaceName, uint32_t code, uint32_t flags, @@ -39,7 +40,7 @@ public: const Parcel& reply, status_t err); RecordedTransaction(RecordedTransaction&& t) noexcept; - [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const; + [[nodiscard]] status_t dumpToFile(const binder::unique_fd& fd) const; const std::string& getInterfaceName() const; uint32_t getCode() const; @@ -53,8 +54,8 @@ public: private: RecordedTransaction() = default; - android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType, - size_t byteCount, const uint8_t* data) const; + android::status_t writeChunk(const binder::borrowed_fd, uint32_t chunkType, size_t byteCount, + const uint8_t* data) const; #pragma clang diagnostic push #pragma clang diagnostic error "-Wpadded" diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 1001b64ede63679be1108594ee319928b264baf8..a07880dd650a8f8b80f687c0f55dc8df2c32956a 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -15,14 +15,15 @@ */ #pragma once -#include #include #include #include #include +#include #include #include +#include #include #include @@ -58,7 +59,7 @@ public: * to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client * session can be created from the client end of the pair. */ - [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(base::unique_fd serverFd); + [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(binder::unique_fd serverFd); /** * This represents a session for responses, e.g.: @@ -78,7 +79,7 @@ public: * This method is used in the libbinder_rpc_unstable API * RunInitUnixDomainRpcServer(). */ - [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd); + [[nodiscard]] status_t setupRawSocketServer(binder::unique_fd socket_fd); /** * Creates an RPC server binding to the given CID at the given port. @@ -110,13 +111,13 @@ public: /** * If hasServer(), return the server FD. Otherwise return invalid FD. */ - [[nodiscard]] base::unique_fd releaseServer(); + [[nodiscard]] binder::unique_fd releaseServer(); /** * Set up server using an external FD previously set up by releaseServer(). * Return false if there's already a server. */ - [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd); + [[nodiscard]] status_t setupExternalServer(binder::unique_fd serverFd); /** * This must be called before adding a client session. This corresponds @@ -137,7 +138,7 @@ public: * used. However, this can be used in order to prevent newer protocol * versions from ever being used. This is expected to be useful for testing. */ - void setProtocolVersion(uint32_t version); + [[nodiscard]] bool setProtocolVersion(uint32_t version); /** * Set the supported transports for sending and receiving file descriptors. @@ -163,14 +164,18 @@ public: * 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*. + * The callable takes three arguments: + * - a weak pointer to the session. If you want to hold onto this in the root object, then + * you should keep a weak pointer, and promote it when needed. For instance, if you refer + * to this from the root object, then you could get ahold of transport-specific information. + * - a type-erased pointer to an OS- and transport-specific address structure, e.g., + * sockaddr_vm for vsock + * - 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(const void*, size_t)>&& object); + void setPerSessionRootObject( + std::function(wp session, const void*, size_t)>&& object); sp getRootObject(); /** @@ -183,6 +188,13 @@ public: */ void setConnectionFilter(std::function&& filter); + /** + * Set optional modifier of each newly created server socket. + * + * The only argument is a successfully created file descriptor, not bound to an address yet. + */ + void setServerSocketModifier(std::function&& modifier); + /** * See RpcTransportCtx::getCertificate */ @@ -237,7 +249,7 @@ private: void onSessionIncomingThreadEnded() override; status_t setupExternalServer( - base::unique_fd serverFd, + binder::unique_fd serverFd, std::function&& acceptFn); static constexpr size_t kRpcAddressSize = 128; @@ -265,8 +277,9 @@ private: sp mRootObject; wp mRootObjectWeak; - std::function(const void*, size_t)> mRootObjectFactory; + std::function(wp, const void*, size_t)> mRootObjectFactory; std::function mConnectionFilter; + std::function mServerSocketModifier; std::map, sp> mSessions; std::unique_ptr mShutdownTrigger; RpcConditionVariable mShutdownCv; diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index cb6460398d5fa06480bca06d9784965c0b431197..11fbde9ace4f5e5ee21b8d3325115bbb5480a06b 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -15,11 +15,10 @@ */ #pragma once -#include -#include #include #include #include +#include #include #include @@ -124,7 +123,7 @@ public: /** * Connects to an RPC server over a nameless Unix domain socket pair. */ - [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(base::unique_fd bootstrap); + [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(binder::unique_fd bootstrap); /** * Connects to an RPC server at the CVD & port. @@ -146,8 +145,8 @@ public: * * For future compatibility, 'request' should not reference any stack data. */ - [[nodiscard]] status_t setupPreconnectedClient(base::unique_fd fd, - std::function&& request); + [[nodiscard]] status_t setupPreconnectedClient(binder::unique_fd fd, + std::function&& request); /** * For debugging! diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h index 8abf04eaf0ba5ff956f1e4708ee77b96967a7992..d25f29277c1eb0741f3e819b78b3d581a361027e 100644 --- a/libs/binder/include/binder/RpcThreads.h +++ b/libs/binder/include/binder/RpcThreads.h @@ -17,8 +17,7 @@ #include -#include - +#include #include #include #include @@ -120,10 +119,6 @@ static inline RpcMaybeThread::id get_id() { } } // namespace rpc_this_thread -static inline uint64_t rpcGetThreadId() { - return 0; -} - static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) { t.join(); } @@ -135,10 +130,6 @@ 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 diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index fd52a3a1a90b1f71736083ac8c2e203ef4f2919b..a50cdc1db037cd849bbebe284d71fce4030a6348 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -25,12 +25,12 @@ #include #include -#include -#include #include +#include #include #include +#include #include @@ -39,6 +39,16 @@ namespace android { class FdTrigger; struct RpcTransportFd; +// for 'friend' +class RpcTransportRaw; +class RpcTransportTls; +class RpcTransportTipcAndroid; +class RpcTransportTipcTrusty; +class RpcTransportCtxRaw; +class RpcTransportCtxTls; +class RpcTransportCtxTipcAndroid; +class RpcTransportCtxTipcTrusty; + // Represents a socket connection. // No thread-safety is guaranteed for these APIs. class RpcTransport { @@ -75,13 +85,14 @@ public: * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( - FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional> &altPoll, - const std::vector> *ancillaryFds) = 0; + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional>& altPoll, + const std::vector>* + ancillaryFds) = 0; [[nodiscard]] virtual status_t interruptableReadFully( - FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional> &altPoll, - std::vector> *ancillaryFds) = 0; + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional>& altPoll, + std::vector>* ancillaryFds) = 0; /** * Check whether any threads are blocked while polling the transport @@ -92,7 +103,21 @@ public: */ [[nodiscard]] virtual bool isWaiting() = 0; -protected: +private: + // limit the classes which can implement RpcTransport. Being able to change this + // interface is important to allow development of RPC binder. In the past, we + // changed this interface to use iovec for efficiency, and we added FDs to the + // interface. If another transport is needed, it should be added directly here. + // non-socket FDs likely also need changes in RpcSession in order to get + // connected, and similarly to how addrinfo was type-erased from RPC binder + // interfaces when RpcTransportTipc* was added, other changes may be needed + // to add more transports. + + friend class ::android::RpcTransportRaw; + friend class ::android::RpcTransportTls; + friend class ::android::RpcTransportTipcAndroid; + friend class ::android::RpcTransportTipcTrusty; + RpcTransport() = default; }; @@ -117,7 +142,13 @@ public: [[nodiscard]] virtual std::vector getCertificate( RpcCertificateFormat format) const = 0; -protected: +private: + // see comment on RpcTransport + friend class ::android::RpcTransportCtxRaw; + friend class ::android::RpcTransportCtxTls; + friend class ::android::RpcTransportCtxTipcAndroid; + friend class ::android::RpcTransportCtxTipcTrusty; + RpcTransportCtx() = default; }; @@ -140,17 +171,17 @@ protected: RpcTransportCtxFactory() = default; }; -struct RpcTransportFd { +struct RpcTransportFd final { private: mutable bool isPolling{false}; void setPollingState(bool state) const { isPolling = state; } public: - base::unique_fd fd; + binder::unique_fd fd; RpcTransportFd() = default; - explicit RpcTransportFd(base::unique_fd &&descriptor) + explicit RpcTransportFd(binder::unique_fd&& descriptor) : isPolling(false), fd(std::move(descriptor)) {} RpcTransportFd(RpcTransportFd &&transportFd) noexcept @@ -162,7 +193,7 @@ public: return *this; } - RpcTransportFd &operator=(base::unique_fd &&descriptor) noexcept { + RpcTransportFd& operator=(binder::unique_fd&& descriptor) noexcept { fd = std::move(descriptor); isPolling = false; return *this; diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index 5fa2ff6a874c84d00c5eec2a05387be0e4a6a097..96b9733c94249c51a221db5e264d45c30be5445f 100644 --- a/libs/binder/include/binder/SafeInterface.h +++ b/libs/binder/include/binder/SafeInterface.h @@ -18,7 +18,6 @@ #include #include -#include // Set to 1 to enable CallStacks when logging errors #define SI_DUMP_CALLSTACKS 0 @@ -218,7 +217,7 @@ private: template status_t callParcel(const char* name, Function f) const { status_t error = f(); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error)); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); @@ -265,7 +264,7 @@ protected: data.writeInterfaceToken(this->getInterfaceDescriptor()); status_t error = writeInputs(&data, std::forward(args)...); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by writeInputs return error; } @@ -273,7 +272,7 @@ protected: // Send the data Parcel to the remote and retrieve the reply parcel Parcel reply; error = this->remote()->transact(static_cast(tag), data, &reply); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); @@ -283,7 +282,7 @@ protected: // Read the outputs from the reply Parcel into the output arguments error = readOutputs(reply, std::forward(args)...); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by readOutputs return error; } @@ -291,7 +290,7 @@ protected: // Retrieve the result code from the reply Parcel status_t result = NO_ERROR; error = reply.readInt32(&result); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { ALOG(LOG_ERROR, mLogTag, "Failed to obtain result"); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); @@ -315,7 +314,7 @@ protected: Parcel data; data.writeInterfaceToken(this->getInterfaceDescriptor()); status_t error = writeInputs(&data, std::forward(args)...); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by writeInputs return; } @@ -324,7 +323,7 @@ protected: Parcel reply; error = this->remote()->transact(static_cast(tag), data, &reply, IBinder::FLAG_ONEWAY); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); @@ -406,7 +405,7 @@ private: template status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const { status_t error = writeIfInput(data, std::forward(t)); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by writeIfInput return error; } @@ -429,7 +428,7 @@ private: template status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const { status_t error = readIfOutput(reply, std::forward(t)); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by readIfOutput return error; } @@ -458,7 +457,7 @@ protected: // Read the inputs from the data Parcel into the argument tuple status_t error = InputReader{mLogTag}.readInputs(data, &rawArgs); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by read return error; } @@ -468,14 +467,14 @@ protected: // Extract the outputs from the argument tuple and write them into the reply Parcel error = OutputWriter{mLogTag}.writeOutputs(reply, &rawArgs); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by write return error; } // Return the result code in the reply Parcel error = reply->writeInt32(result); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { ALOG(LOG_ERROR, mLogTag, "Failed to write result"); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); @@ -500,7 +499,7 @@ protected: // Read the inputs from the data Parcel into the argument tuple status_t error = InputReader{mLogTag}.readInputs(data, &rawArgs); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged by read return error; } @@ -596,7 +595,7 @@ private: typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg( const Parcel& data, RawTuple* args) { status_t error = readIfInput(data, args); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged in read return error; } @@ -694,7 +693,7 @@ private: typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg( Parcel* reply, RawTuple* args) { status_t error = writeIfOutput(reply, args); - if (CC_UNLIKELY(error != NO_ERROR)) { + if (error != NO_ERROR) [[unlikely]] { // A message will have been logged in read return error; } diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h index eb98042c422504b6fc866b8bec82706badc62ec3..50158c30726a50b7e8c0e21bbabb2d16e6ba2804 100644 --- a/libs/binder/include/binder/TextOutput.h +++ b/libs/binder/include/binder/TextOutput.h @@ -147,7 +147,7 @@ inline TextOutput& operator<<(TextOutput& to, const bool &val) inline TextOutput& operator<<(TextOutput& to, const String16& val) { - to << String8(val).string(); + to << String8(val).c_str(); return to; } diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h index 99378428ad3c768c9d2633d8f040d7ff6002cc63..95318b2bf6c63966e2944ad36e6d492caed669ce 100644 --- a/libs/binder/include/binder/Trace.h +++ b/libs/binder/include/binder/Trace.h @@ -16,22 +16,36 @@ #pragma once -#include #include +#if __has_include() +#include +#endif + +#ifdef ATRACE_TAG_AIDL +#if ATRACE_TAG_AIDL != (1 << 24) +#error "Mismatched ATRACE_TAG_AIDL definitions" +#endif +#else +#define ATRACE_TAG_AIDL (1 << 24) +#endif + namespace android { namespace binder { +// Forward declarations from internal OS.h +namespace os { // Trampoline functions allowing generated aidls to trace binder transactions without depending on // libcutils/libutils -void atrace_begin(uint64_t tag, const char* name); -void atrace_end(uint64_t tag); +void trace_begin(uint64_t tag, const char* name); +void trace_end(uint64_t tag); +} // namespace os class ScopedTrace { public: - inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); } + inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { os::trace_begin(mTag, name); } - inline ~ScopedTrace() { atrace_end(mTag); } + inline ~ScopedTrace() { os::trace_end(mTag); } private: uint64_t mTag; diff --git a/libs/binder/include/binder/unique_fd.h b/libs/binder/include/binder/unique_fd.h new file mode 100644 index 0000000000000000000000000000000000000000..439b8a2e4e4fcdc9b696cea83bdcf9ac1dd6186f --- /dev/null +++ b/libs/binder/include/binder/unique_fd.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 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 + +#ifndef BINDER_NO_LIBBASE + +#include + +namespace android::binder { +using android::base::borrowed_fd; +using android::base::unique_fd; +} // namespace android::binder + +#else // BINDER_NO_LIBBASE + +#include +#include // not needed for unique_fd, but a lot of users depend on open(3) +#include + +namespace android::binder { + +// Container for a file descriptor that automatically closes the descriptor as +// it goes out of scope. +// +// unique_fd ufd(open("/some/path", "r")); +// if (!ufd.ok()) return error; +// +// // Do something useful with ufd.get(), possibly including early 'return'. +// +// return 0; // Descriptor is closed for you. +// +class unique_fd final { +public: + unique_fd() {} + + explicit unique_fd(int fd) { reset(fd); } + ~unique_fd() { reset(); } + + unique_fd(const unique_fd&) = delete; + void operator=(const unique_fd&) = delete; + unique_fd(unique_fd&& other) noexcept { reset(other.release()); } + unique_fd& operator=(unique_fd&& s) noexcept { + int fd = s.fd_; + s.fd_ = -1; + reset(fd); + return *this; + } + + [[clang::reinitializes]] void reset(int new_value = -1) { + int previous_errno = errno; + + if (fd_ != -1) { + ::close(fd_); + } + + fd_ = new_value; + errno = previous_errno; + } + + int get() const { return fd_; } + + bool ok() const { return get() >= 0; } + + [[nodiscard]] int release() { + int ret = fd_; + fd_ = -1; + return ret; + } + +private: + int fd_ = -1; +}; + +// A wrapper type that can be implicitly constructed from either int or +// unique_fd. This supports cases where you don't actually own the file +// descriptor, and can't take ownership, but are temporarily acting as if +// you're the owner. +// +// One example would be a function that needs to also allow +// STDERR_FILENO, not just a newly-opened fd. Another example would be JNI code +// that's using a file descriptor that's actually owned by a +// ParcelFileDescriptor or whatever on the Java side, but where the JNI code +// would like to enforce this weaker sense of "temporary ownership". +// +// If you think of unique_fd as being like std::string in that represents +// ownership, borrowed_fd is like std::string_view (and int is like const +// char*). +struct borrowed_fd { + /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT + /* implicit */ borrowed_fd(const unique_fd& ufd) : fd_(ufd.get()) {} // NOLINT + + int get() const { return fd_; } + +private: + int fd_ = -1; +}; + +} // namespace android::binder + +#endif // BINDER_NO_LIBBASE diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index a157792156f3b9e8034e1e118027f15453a7f05e..7d0acd18437eb5584a12209e6522496c21e728fd 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -40,12 +40,13 @@ enum class ARpcSession_FileDescriptorTransportMode { [[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port); -// Starts a Unix domain RPC server with a given init-managed Unix domain `name` +// Starts a Unix domain RPC server with an open raw socket file descriptor // and a given root IBinder object. -// The socket should be created in init.rc with the same `name`. +// The socket should be created and bound to an address. // Returns an opaque handle to the running server instance, or null if the server // could not be started. -[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name); +// The socket will be closed by the server once the server goes out of scope. +[[nodiscard]] ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd); // Starts an RPC server that bootstraps sessions using an existing Unix domain // socket pair, with a given root IBinder object. diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index a167f235d534cfccacf5016bdad1e6b172e0a543..cb44c58c2cc3c0b36fa5ca5311813cfb80ce1d52 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -16,13 +16,18 @@ #include -#include -#include #include #include #include +#include + +#ifndef __TRUSTY__ #include +#endif + +#ifdef __linux__ #include +#endif // __linux__ using android::OK; using android::RpcServer; @@ -30,7 +35,7 @@ using android::RpcSession; using android::sp; using android::status_t; using android::statusToString; -using android::base::unique_fd; +using android::binder::unique_fd; // Opaque handle for RpcServer. struct ARpcServer {}; @@ -75,6 +80,7 @@ RpcSession::FileDescriptorTransportMode toTransportMode( extern "C" { +#ifndef __TRUSTY__ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) { auto server = RpcServer::make(); @@ -85,8 +91,8 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in } if (status_t status = server->setupVsockServer(bindCid, port); status != OK) { - LOG(ERROR) << "Failed to set up vsock server with port " << port - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up vsock server with port %u error: %s", port, + statusToString(status).c_str()); return nullptr; } if (cid != VMADDR_CID_ANY) { @@ -95,7 +101,7 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in const sockaddr_vm* vaddr = reinterpret_cast(addr); LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock"); if (cid != vaddr->svm_cid) { - LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid; + ALOGE("Rejected vsock connection from CID %u", vaddr->svm_cid); return false; } return true; @@ -105,23 +111,16 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in return createObjectHandle(server); } -ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) { +ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd) { auto server = RpcServer::make(); - auto fd = unique_fd(android_get_control_socket(name)); + auto fd = unique_fd(socketFd); if (!fd.ok()) { - LOG(ERROR) << "Failed to get fd for the socket:" << name; + ALOGE("Invalid socket fd %d", socketFd); return nullptr; } - // Control socket fds are inherited from init, so they don't have O_CLOEXEC set. - // But we don't want any child processes to inherit the socket we are running - // the server on, so attempt to set the flag now. - if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { - LOG(WARNING) << "Failed to set CLOEXEC on control socket with name " << name - << " error: " << errno; - } if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up RPC server with fd %d error: %s", socketFd, + statusToString(status).c_str()); return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); @@ -132,13 +131,13 @@ ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd auto server = RpcServer::make(); auto fd = unique_fd(bootstrapFd); if (!fd.ok()) { - LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd; + ALOGE("Invalid bootstrap fd %d", bootstrapFd); return nullptr; } if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Unix Domain RPC server with bootstrap fd %d error: %s", bootstrapFd, + statusToString(status).c_str()); return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); @@ -148,13 +147,14 @@ ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned int port) { auto server = RpcServer::make(); if (status_t status = server->setupInetServer(address, port, nullptr); status != OK) { - LOG(ERROR) << "Failed to set up inet RPC server with address " << address << " and port " - << port << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up inet RPC server with address %s and port %u error: %s", address, + port, statusToString(status).c_str()); return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); return createObjectHandle(server); } +#endif // __TRUSTY__ void ARpcServer_setSupportedFileDescriptorTransportModes( ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[], @@ -195,11 +195,12 @@ void ARpcSession_free(ARpcSession* handle) { freeObjectHandle(handle); } +#ifndef __TRUSTY__ AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) { auto session = handleToStrongPointer(handle); if (status_t status = session->setupVsockClient(cid, port); status != OK) { - LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up vsock client with CID %u and port %u error: %s", cid, port, + statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -210,8 +211,8 @@ AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* nam pathname = ANDROID_SOCKET_DIR "/" + pathname; auto session = handleToStrongPointer(handle); if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Unix Domain RPC client with path: %s error: %s", pathname.c_str(), + statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -221,13 +222,13 @@ AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bo auto session = handleToStrongPointer(handle); auto fd = unique_fd(dup(bootstrapFd)); if (!fd.ok()) { - LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd; + ALOGE("Invalid bootstrap fd %d", bootstrapFd); return nullptr; } if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Unix Domain RPC client with bootstrap fd: %d error: %s", + bootstrapFd, statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -236,19 +237,20 @@ AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bo AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) { auto session = handleToStrongPointer(handle); if (status_t status = session->setupInetClient(address, port); status != OK) { - LOG(ERROR) << "Failed to set up inet RPC client with address " << address << " and port " - << port << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up inet RPC client with address %s and port %u error: %s", address, + port, statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); } +#endif // __TRUSTY__ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param), void* param) { auto session = handleToStrongPointer(handle); auto request = [=] { return unique_fd{requestFd(param)}; }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { - LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str(); + ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt index 63679c28d0a4282022fe1df42f5dfdf736e3b13f..50f7deb7d96f357fbb82be61b7faa2e88a6d36ce 100644 --- a/libs/binder/libbinder_rpc_unstable.map.txt +++ b/libs/binder/libbinder_rpc_unstable.map.txt @@ -3,7 +3,7 @@ LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only ARpcServer_free; ARpcServer_join; ARpcServer_newInet; - ARpcServer_newInitUnixDomain; + ARpcServer_newBoundSocket; ARpcServer_newVsock; ARpcServer_shutdown; ARpcServer_start; diff --git a/libs/binder/liblog_stub/Android.bp b/libs/binder/liblog_stub/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..f2ca22fdb0ec2dbf393fbfe2cc5f4f4051849c07 --- /dev/null +++ b/libs/binder/liblog_stub/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_headers { + name: "liblog_stub", + export_include_dirs: ["include"], + + host_supported: true, + native_bridge_supported: true, + product_available: true, + recovery_available: true, + vendor_available: true, + + target: { + windows: { + enabled: true, + }, + }, + + visibility: [ + "//frameworks/native/libs/binder:__subpackages__", + "//system/core/libutils/binder", + ], +} diff --git a/libs/binder/liblog_stub/include/android/log.h b/libs/binder/liblog_stub/include/android/log.h new file mode 100644 index 0000000000000000000000000000000000000000..9dcd9262db879ca452128d167461f4920a701992 --- /dev/null +++ b/libs/binder/liblog_stub/include/android/log.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 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 + +extern "C" { + +/** + * Android log priority values, in increasing order of priority. + */ +typedef enum android_LogPriority { + /** For internal use only. */ + ANDROID_LOG_UNKNOWN = 0, + /** The default priority, for internal use only. */ + ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ + /** Verbose logging. Should typically be disabled for a release apk. */ + ANDROID_LOG_VERBOSE, + /** Debug logging. Should typically be disabled for a release apk. */ + ANDROID_LOG_DEBUG, + /** Informational logging. Should typically be disabled for a release apk. */ + ANDROID_LOG_INFO, + /** Warning logging. For use with recoverable failures. */ + ANDROID_LOG_WARN, + /** Error logging. For use with unrecoverable failures. */ + ANDROID_LOG_ERROR, + /** Fatal logging. For use when aborting. */ + ANDROID_LOG_FATAL, + /** For internal use only. */ + ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ +} android_LogPriority; + +typedef void (*__android_logger_function)(const struct __android_log_message* log_message); +inline void __android_log_set_logger(__android_logger_function) {} +inline void __android_log_stderr_logger(const struct __android_log_message*) {} + +} // extern "C" diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h new file mode 100644 index 0000000000000000000000000000000000000000..91c9632c1ba501c9c40aafe5847bad7690f73484 --- /dev/null +++ b/libs/binder/liblog_stub/include/log/log.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 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 +#include + +#include + +extern "C" { + +#ifndef ANDROID_LOG_STUB_MIN_PRIORITY +#define ANDROID_LOG_STUB_MIN_PRIORITY ANDROID_LOG_INFO +#endif + +#ifndef LOG_TAG +#define LOG_TAG "" +#endif + +constexpr bool __android_log_stub_is_loggable(android_LogPriority priority) { + return ANDROID_LOG_STUB_MIN_PRIORITY <= priority; +} + +#ifdef ANDROID_LOG_STUB_WEAK_PRINT +#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT __android_log_print +#define __ANDROID_LOG_STUB_PRINT_ATTR __attribute__((weak)) +#else +#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT true +#define __ANDROID_LOG_STUB_PRINT_ATTR +#endif + +int __android_log_print(int prio, const char* tag, const char* fmt, ...) + __attribute__((format(printf, 3, 4))) __ANDROID_LOG_STUB_PRINT_ATTR; + +#define IF_ALOG(priority, tag) \ + if (__android_log_stub_is_loggable(ANDROID_##priority) && __ANDROID_LOG_STUB_IS_PRINT_PRESENT) +#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) +#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) +#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) +#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) +#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) + +#define ALOG(priority, tag, fmt, ...) \ + do { \ + if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \ + std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + IF_ALOG(priority, tag) { \ + __android_log_print(ANDROID_##priority, tag, \ + tag ": " fmt "\n" __VA_OPT__(, ) __VA_ARGS__); \ + } \ + if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \ + } while (false) +#define ALOGV(...) ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__) +#define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOG_FATAL(...) ALOG(LOG_FATAL, LOG_TAG, __VA_ARGS__) +#define LOG_ALWAYS_FATAL LOG_FATAL + +#define ALOG_IF(cond, priority, tag, ...) \ + if (cond) [[unlikely]] \ + ALOG(priority, tag, #cond ": " __VA_ARGS__) +#define ALOGV_IF(cond, ...) ALOG_IF(cond, LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define ALOGD_IF(cond, ...) ALOG_IF(cond, LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define ALOGI_IF(cond, ...) ALOG_IF(cond, LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGW_IF(cond, ...) ALOG_IF(cond, LOG_WARN, LOG_TAG, __VA_ARGS__) +#define ALOGE_IF(cond, ...) ALOG_IF(cond, LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOG_FATAL_IF(cond, ...) ALOG_IF(cond, LOG_FATAL, LOG_TAG, __VA_ARGS__) +#define LOG_ALWAYS_FATAL_IF LOG_FATAL_IF +#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) + +inline int android_errorWriteLog(int tag, const char* subTag) { + ALOGE("android_errorWriteLog(%x, %s)", tag, subTag); + return 0; +} + +} // extern "C" diff --git a/libs/binder/ndk/.clang-format b/libs/binder/ndk/.clang-format index 9a9d936f1516f044fe9e1724d8e1e56730850ab2..60774143ee9c82b3412f2b4cd01229f478404a90 100644 --- a/libs/binder/ndk/.clang-format +++ b/libs/binder/ndk/.clang-format @@ -2,9 +2,7 @@ BasedOnStyle: Google ColumnLimit: 100 IndentWidth: 4 ContinuationIndentWidth: 8 -PointerAlignment: Left TabWidth: 4 AllowShortFunctionsOnASingleLine: Inline PointerAlignment: Left -TabWidth: 4 UseTab: Never diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 58ed4188e2dc7fb6019bc26ec32e2e027966d6c8..ccf3ce891faf77d2c402d716a06a8264cf68898a 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -60,6 +60,7 @@ cc_library { "libbinder.cpp", "parcel.cpp", "parcel_jni.cpp", + "persistable_bundle.cpp", "process.cpp", "stability.cpp", "status.cpp", @@ -138,6 +139,7 @@ cc_library { "performance*", "portability*", ], + afdo: true, } cc_library_headers { diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index d0de7b96b5c128cc239fc2e46ab1968ca9f1c194..bf7a0ba5f07d79811369f93fbd3ef909666aa089 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -14,14 +14,15 @@ * limitations under the License. */ -#include #include #include #include #include #include #include +#if __has_include() #include +#endif #include "ibinder_internal.h" #include "parcel_internal.h" @@ -46,8 +47,8 @@ static void* kValue = static_cast(new bool{true}); void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */}; static void attach(const sp& binder) { - // can only attach once - CHECK_EQ(nullptr, binder->attachObject(kId, kValue, nullptr /*cookie*/, clean)); + auto alreadyAttached = binder->attachObject(kId, kValue, nullptr /*cookie*/, clean); + LOG_ALWAYS_FATAL_IF(alreadyAttached != nullptr, "can only attach once"); } static bool has(const sp& binder) { return binder != nullptr && binder->findObject(kId) == kValue; @@ -63,9 +64,9 @@ struct Value { }; void clean(const void* id, void* obj, void* cookie) { // be weary of leaks! - // LOG(INFO) << "Deleting an ABpBinder"; + // ALOGI("Deleting an ABpBinder"); - CHECK(id == kId) << id << " " << obj << " " << cookie; + LOG_ALWAYS_FATAL_IF(id != kId, "%p %p %p", id, obj, cookie); delete static_cast(obj); }; @@ -119,14 +120,13 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (mClazz != nullptr && !asABpBinder()) { const String16& currentDescriptor = mClazz->getInterfaceDescriptor(); if (newDescriptor == currentDescriptor) { - LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor - << "' match during associateClass, but they are different class objects (" - << clazz << " vs " << mClazz << "). Class descriptor collision?"; + ALOGE("Class descriptors '%s' match during associateClass, but they are different class" + " objects (%p vs %p). Class descriptor collision?", + String8(currentDescriptor).c_str(), clazz, mClazz); } else { - LOG(ERROR) << __func__ - << ": Class cannot be associated on object which already has a class. " - "Trying to associate to '" - << newDescriptor << "' but already set to '" << currentDescriptor << "'."; + ALOGE("%s: Class cannot be associated on object which already has a class. " + "Trying to associate to '%s' but already set to '%s'.", + __func__, String8(newDescriptor).c_str(), String8(currentDescriptor).c_str()); } // always a failure because we know mClazz != clazz @@ -137,15 +137,14 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { // since it's an error condition. Do the comparison after we take the lock and // check the pointer equality fast path. By always taking the lock, it's also // more flake-proof. However, the check is not dependent on the lock. - if (descriptor != newDescriptor) { + if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) { if (getBinder()->isBinderAlive()) { - LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor - << "' but descriptor is actually '" << SanitizeString(descriptor) << "'."; + ALOGE("%s: Expecting binder to have class '%s' but descriptor is actually '%s'.", + __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str()); } else { // b/155793159 - LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor - << "' to dead binder with cached descriptor '" << SanitizeString(descriptor) - << "'."; + ALOGE("%s: Cannot associate class '%s' to dead binder with cached descriptor '%s'.", + __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str()); } return false; } @@ -162,7 +161,7 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) : AIBinder(clazz), BBinder(), mUserData(userData) { - CHECK(clazz != nullptr); + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "clazz == nullptr"); } ABBinder::~ABBinder() { getClass()->onDestroy(mUserData); @@ -182,7 +181,7 @@ status_t ABBinder::dump(int fd, const ::android::Vector& args) { // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be // null in Java if (args.size() > INT32_MAX) { - LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size(); + ALOGE("ABBinder::dump received too many arguments: %zu", args.size()); return STATUS_BAD_VALUE; } @@ -229,7 +228,11 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce // Shell commands should only be callable by ADB. uid_t uid = AIBinder_getCallingUid(); - if (uid != AID_ROOT && uid != AID_SHELL) { + if (uid != 0 /* root */ +#ifdef AID_SHELL + && uid != AID_SHELL +#endif + ) { if (resultReceiver != nullptr) { resultReceiver->send(-1); } @@ -257,7 +260,7 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) : AIBinder(nullptr /*clazz*/), mRemote(binder) { - CHECK(binder != nullptr); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); } ABpBinder::~ABpBinder() {} @@ -367,27 +370,27 @@ AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, } void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { - CHECK(clazz != nullptr) << "setOnDump requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setOnDump requires non-null clazz"); // this is required to be called before instances are instantiated clazz->onDump = onDump; } void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) { - CHECK(clazz != nullptr) << "disableInterfaceTokenHeader requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "disableInterfaceTokenHeader requires non-null clazz"); clazz->writeHeader = false; } void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, AIBinder_handleShellCommand handleShellCommand) { - CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setHandleShellCommand requires non-null clazz"); clazz->handleShellCommand = handleShellCommand; } const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) { - CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "getDescriptor requires non-null clazz"); return clazz->getInterfaceDescriptorUtf8(); } @@ -399,8 +402,8 @@ AIBinder_DeathRecipient::TransferDeathRecipient::~TransferDeathRecipient() { } void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp& who) { - CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get() - << " (" << mWho.get_refs() << ")"; + LOG_ALWAYS_FATAL_IF(who != mWho, "%p (%p) vs %p (%p)", who.unsafe_get(), who.get_refs(), + mWho.unsafe_get(), mWho.get_refs()); mOnDied(mCookie); @@ -411,7 +414,7 @@ void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wpunlinkToDeath(strongWho, mCookie); if (result != ::android::DEAD_OBJECT) { - LOG(WARNING) << "Unlinking to dead binder resulted in: " << result; + ALOGW("Unlinking to dead binder resulted in: %d", result); } } @@ -420,7 +423,7 @@ void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp& binder, void* cookie) { - CHECK(binder != nullptr); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); std::lock_guard l(mDeathRecipientsMutex); @@ -453,7 +456,7 @@ binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp& binder, } binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp& binder, void* cookie) { - CHECK(binder != nullptr); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); std::lock_guard l(mDeathRecipientsMutex); @@ -465,9 +468,8 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp& binder status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { - LOG(ERROR) << __func__ - << ": removed reference to death recipient but unlink failed: " - << statusToString(status); + ALOGE("%s: removed reference to death recipient but unlink failed: %s", __func__, + statusToString(status).c_str()); } return PruneStatusT(status); } @@ -484,7 +486,7 @@ void AIBinder_DeathRecipient::setOnUnlinked(AIBinder_DeathRecipient_onBinderUnli AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) { if (clazz == nullptr) { - LOG(ERROR) << __func__ << ": Must provide class to construct local binder."; + ALOGE("%s: Must provide class to construct local binder.", __func__); return nullptr; } @@ -548,8 +550,7 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" - << recipient << ")"; + ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient); return STATUS_UNEXPECTED_NULL; } @@ -560,8 +561,7 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" - << recipient << ")"; + ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient); return STATUS_UNEXPECTED_NULL; } @@ -590,7 +590,7 @@ void AIBinder_incStrong(AIBinder* binder) { } void AIBinder_decStrong(AIBinder* binder) { if (binder == nullptr) { - LOG(ERROR) << __func__ << ": on null binder"; + ALOGE("%s: on null binder", __func__); return; } @@ -598,7 +598,7 @@ void AIBinder_decStrong(AIBinder* binder) { } int32_t AIBinder_debugGetRefCount(AIBinder* binder) { if (binder == nullptr) { - LOG(ERROR) << __func__ << ": on null binder"; + ALOGE("%s: on null binder", __func__); return -1; } @@ -636,15 +636,14 @@ void* AIBinder_getUserData(AIBinder* binder) { binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { if (binder == nullptr || in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder - << ") and in (" << in << ")."; + ALOGE("%s: requires non-null parameters binder (%p) and in (%p).", __func__, binder, in); return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); if (clazz == nullptr) { - LOG(ERROR) << __func__ - << ": Class must be defined for a remote binder transaction. See " - "AIBinder_associateClass."; + ALOGE("%s: Class must be defined for a remote binder transaction. See " + "AIBinder_associateClass.", + __func__); return STATUS_INVALID_OPERATION; } @@ -677,7 +676,7 @@ static void DestroyParcel(AParcel** parcel) { binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in, AParcel** out, binder_flags_t flags) { if (in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null in parameter"; + ALOGE("%s: requires non-null in parameter", __func__); return STATUS_UNEXPECTED_NULL; } @@ -687,27 +686,26 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AutoParcelDestroyer forIn(in, DestroyParcel); if (!isUserCommand(code)) { - LOG(ERROR) << __func__ - << ": Only user-defined transactions can be made from the NDK, but requested: " - << code; + ALOGE("%s: Only user-defined transactions can be made from the NDK, but requested: %d", + __func__, code); return STATUS_UNKNOWN_TRANSACTION; } constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF; if ((flags & ~kAllFlags) != 0) { - LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags; + ALOGE("%s: Unrecognized flags sent: %d", __func__, flags); return STATUS_BAD_VALUE; } if (binder == nullptr || *in == nullptr || out == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in (" - << in << "), and out (" << out << ")."; + ALOGE("%s: requires non-null parameters binder (%p), in (%p), and out (%p).", __func__, + binder, in, out); return STATUS_UNEXPECTED_NULL; } if ((*in)->getBinder() != binder) { - LOG(ERROR) << __func__ << ": parcel is associated with binder object " << binder - << " but called with " << (*in)->getBinder(); + ALOGE("%s: parcel is associated with binder object %p but called with %p", __func__, binder, + (*in)->getBinder()); return STATUS_BAD_VALUE; } @@ -727,7 +725,7 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( AIBinder_DeathRecipient_onBinderDied onBinderDied) { if (onBinderDied == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter."; + ALOGE("%s: requires non-null onBinderDied parameter.", __func__); return nullptr; } auto ret = new AIBinder_DeathRecipient(onBinderDied); @@ -793,9 +791,8 @@ binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) { void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) { ABBinder* localBinder = binder->asABBinder(); - if (localBinder == nullptr) { - LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder"; - } + LOG_ALWAYS_FATAL_IF(localBinder == nullptr, + "AIBinder_setRequestingSid must be called on a local binder"); localBinder->setRequestingSid(requestingSid); } @@ -810,9 +807,8 @@ void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) { ABBinder* localBinder = binder->asABBinder(); - if (localBinder == nullptr) { - LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder"; - } + LOG_ALWAYS_FATAL_IF(localBinder == nullptr, + "AIBinder_setInheritRt must be called on a local binder"); localBinder->setInheritRt(inheritRt); } diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 67bb092f0f0fca112fa750f9f36c21629054c97d..9d5368f674ced64c0eb352c4d42dc0502aed5a57 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -104,10 +104,14 @@ struct ABpBinder : public AIBinder { ::android::sp<::android::IBinder> getBinder() override { return mRemote; } ABpBinder* asABpBinder() override { return this; } + bool isServiceFuzzing() const { return mServiceFuzzing; } + void setServiceFuzzing() { mServiceFuzzing = true; } + private: friend android::sp; explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); ::android::sp<::android::IBinder> mRemote; + bool mServiceFuzzing = false; }; struct AIBinder_Class { diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index d6937c2c52652f08afb0e0bb6146343a6a404f43..18769b145449463c63ee24370ad27ac412655a05 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -115,17 +116,29 @@ class SpAIBinder { */ AIBinder** getR() { return &mBinder; } - bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); } - bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); } - bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); } - bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); } - bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); } - bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); } - private: AIBinder* mBinder = nullptr; }; +#define SP_AIBINDER_COMPARE(_op_) \ + static inline bool operator _op_(const SpAIBinder& lhs, const SpAIBinder& rhs) { \ + return lhs.get() _op_ rhs.get(); \ + } \ + static inline bool operator _op_(const SpAIBinder& lhs, const AIBinder* rhs) { \ + return lhs.get() _op_ rhs; \ + } \ + static inline bool operator _op_(const AIBinder* lhs, const SpAIBinder& rhs) { \ + return lhs _op_ rhs.get(); \ + } + +SP_AIBINDER_COMPARE(!=) +SP_AIBINDER_COMPARE(<) +SP_AIBINDER_COMPARE(<=) +SP_AIBINDER_COMPARE(==) +SP_AIBINDER_COMPARE(>) +SP_AIBINDER_COMPARE(>=) +#undef SP_AIBINDER_COMPARE + namespace impl { /** 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 9949de2aac39e2d6180853563e06f2d57877c454..62738041bab2203d7e7e955dff9515782bd4fcd0 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -138,6 +138,8 @@ class ICInterface : public SharedRefBase { /** * Dumps information about the interface. By default, dumps nothing. + * + * This method is not given ownership of the FD. */ virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs); diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h new file mode 100644 index 0000000000000000000000000000000000000000..a0e4f7b63b7539319aee7c865a48c9da64802dbc --- /dev/null +++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2023 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 +#include +#include + +#include +#include + +namespace aidl::android::os { + +/** + * Wrapper class that enables interop with AIDL NDK generation + * Takes ownership of the APersistableBundle* given to it in reset() and will automatically + * destroy it in the destructor, similar to a smart pointer container + */ +class PersistableBundle { + public: + PersistableBundle() noexcept { + if (__builtin_available(android __ANDROID_API_V__, *)) { + mPBundle = APersistableBundle_new(); + } + } + // takes ownership of the APersistableBundle* + PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {} + // takes ownership of the APersistableBundle* + PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {} + // duplicates, does not take ownership of the APersistableBundle* + PersistableBundle(const PersistableBundle& other) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + mPBundle = APersistableBundle_dup(other.mPBundle); + } + } + // duplicates, does not take ownership of the APersistableBundle* + PersistableBundle& operator=(const PersistableBundle& other) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + mPBundle = APersistableBundle_dup(other.mPBundle); + } + return *this; + } + + ~PersistableBundle() { reset(); } + + binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { + reset(); + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_readFromParcel(parcel, &mPBundle); + } else { + return STATUS_FAILED_TRANSACTION; + } + } + + binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { + if (!mPBundle) { + return STATUS_BAD_VALUE; + } + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_writeToParcel(mPBundle, parcel); + } else { + return STATUS_FAILED_TRANSACTION; + } + } + + /** + * Destroys any currently owned APersistableBundle* and takes ownership of the given + * APersistableBundle* + * + * @param pBundle The APersistableBundle to take ownership of + */ + void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept { + if (mPBundle) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_delete(mPBundle); + } + mPBundle = nullptr; + } + mPBundle = pBundle; + } + + /** + * Check the actual contents of the bundle for equality. This is typically + * what should be used to check for equality. + */ + bool deepEquals(const PersistableBundle& rhs) const { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_isEqual(get(), rhs.get()); + } else { + return false; + } + } + + /** + * NOTE: This does NOT check the contents of the PersistableBundle. This is + * implemented for ordering. Use deepEquals() to check for equality between + * two different PersistableBundle objects. + */ + inline bool operator==(const PersistableBundle& rhs) const { return get() == rhs.get(); } + inline bool operator!=(const PersistableBundle& rhs) const { return get() != rhs.get(); } + + inline bool operator<(const PersistableBundle& rhs) const { return get() < rhs.get(); } + inline bool operator>(const PersistableBundle& rhs) const { return get() > rhs.get(); } + inline bool operator>=(const PersistableBundle& rhs) const { return !(*this < rhs); } + inline bool operator<=(const PersistableBundle& rhs) const { return !(*this > rhs); } + + PersistableBundle& operator=(PersistableBundle&& other) noexcept { + reset(other.release()); + return *this; + } + + /** + * Stops managing any contained APersistableBundle*, returning it to the caller. Ownership + * is released. + * @return APersistableBundle* or null if this was empty + */ + [[nodiscard]] APersistableBundle* _Nullable release() noexcept { + APersistableBundle* _Nullable ret = mPBundle; + mPBundle = nullptr; + return ret; + } + + inline std::string toString() const { + if (!mPBundle) { + return ""; + } else if (__builtin_available(android __ANDROID_API_V__, *)) { + std::ostringstream os; + os << ""; + return os.str(); + } + return ""; + } + + int32_t size() const { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_size(mPBundle); + } else { + return 0; + } + } + + int32_t erase(const std::string& key) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_erase(mPBundle, key.c_str()); + } else { + return 0; + } + } + + void putBoolean(const std::string& key, bool val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_putBoolean(mPBundle, key.c_str(), val); + } + } + + void putInt(const std::string& key, int32_t val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_putInt(mPBundle, key.c_str(), val); + } + } + + void putLong(const std::string& key, int64_t val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_putLong(mPBundle, key.c_str(), val); + } + } + + void putDouble(const std::string& key, double val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_putDouble(mPBundle, key.c_str(), val); + } + } + + void putString(const std::string& key, const std::string& val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_putString(mPBundle, key.c_str(), val.c_str()); + } + } + + void putBooleanVector(const std::string& key, const std::vector& vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + // std::vector has no ::data(). + int32_t num = vec.size(); + if (num > 0) { + bool* newVec = (bool*)malloc(num * sizeof(bool)); + if (newVec) { + for (int32_t i = 0; i < num; i++) { + newVec[i] = vec[i]; + } + APersistableBundle_putBooleanVector(mPBundle, key.c_str(), newVec, num); + free(newVec); + } + } + } + } + + void putIntVector(const std::string& key, const std::vector& vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t num = vec.size(); + if (num > 0) { + APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num); + } + } + } + void putLongVector(const std::string& key, const std::vector& vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t num = vec.size(); + if (num > 0) { + APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num); + } + } + } + void putDoubleVector(const std::string& key, const std::vector& vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t num = vec.size(); + if (num > 0) { + APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num); + } + } + } + void putStringVector(const std::string& key, const std::vector& vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t num = vec.size(); + if (num > 0) { + char** inVec = (char**)malloc(num * sizeof(char*)); + if (inVec) { + for (int32_t i = 0; i < num; i++) { + inVec[i] = strdup(vec[i].c_str()); + } + APersistableBundle_putStringVector(mPBundle, key.c_str(), inVec, num); + free(inVec); + } + } + } + } + void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle); + } + } + + bool getBoolean(const std::string& key, bool* _Nonnull val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_getBoolean(mPBundle, key.c_str(), val); + } else { + return false; + } + } + + bool getInt(const std::string& key, int32_t* _Nonnull val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_getInt(mPBundle, key.c_str(), val); + } else { + return false; + } + } + + bool getLong(const std::string& key, int64_t* _Nonnull val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_getLong(mPBundle, key.c_str(), val); + } else { + return false; + } + } + + bool getDouble(const std::string& key, double* _Nonnull val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return APersistableBundle_getDouble(mPBundle, key.c_str(), val); + } else { + return false; + } + } + + static char* _Nullable stringAllocator(int32_t bufferSizeBytes, void* _Nullable) { + return (char*)malloc(bufferSizeBytes); + } + + bool getString(const std::string& key, std::string* _Nonnull val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + char* outString = nullptr; + bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString, + &stringAllocator, nullptr); + if (ret && outString) { + *val = std::string(outString); + } + return ret; + } else { + return false; + } + } + + template + bool getVecInternal(int32_t (*_Nonnull getVec)(const APersistableBundle* _Nonnull, + const char* _Nonnull, T* _Nullable, int32_t), + const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + std::vector* _Nonnull vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t bytes = 0; + // call first with nullptr to get required size in bytes + bytes = getVec(pBundle, key, nullptr, 0); + if (bytes > 0) { + T* newVec = (T*)malloc(bytes); + if (newVec) { + bytes = getVec(pBundle, key, newVec, bytes); + int32_t elements = bytes / sizeof(T); + vec->clear(); + for (int32_t i = 0; i < elements; i++) { + vec->push_back(newVec[i]); + } + free(newVec); + return true; + } + } + } + return false; + } + + bool getBooleanVector(const std::string& key, std::vector* _Nonnull vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), + vec); + } + return false; + } + bool getIntVector(const std::string& key, std::vector* _Nonnull vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal(&APersistableBundle_getIntVector, mPBundle, key.c_str(), + vec); + } + return false; + } + bool getLongVector(const std::string& key, std::vector* _Nonnull vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal(&APersistableBundle_getLongVector, mPBundle, key.c_str(), + vec); + } + return false; + } + bool getDoubleVector(const std::string& key, std::vector* _Nonnull vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal(&APersistableBundle_getDoubleVector, mPBundle, + key.c_str(), vec); + } + return false; + } + + // Takes ownership of and frees the char** and its elements. + // Creates a new set or vector based on the array of char*. + template + T moveStringsInternal(char* _Nullable* _Nonnull strings, int32_t bufferSizeBytes) { + if (strings && bufferSizeBytes > 0) { + int32_t num = bufferSizeBytes / sizeof(char*); + T ret; + for (int32_t i = 0; i < num; i++) { + ret.insert(ret.end(), std::string(strings[i])); + free(strings[i]); + } + free(strings); + return ret; + } + return T(); + } + + bool getStringVector(const std::string& key, std::vector* _Nonnull vec) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, + &stringAllocator, nullptr); + if (bytes > 0) { + char** strings = (char**)malloc(bytes); + if (strings) { + bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, + bytes, &stringAllocator, nullptr); + *vec = moveStringsInternal>(strings, bytes); + return true; + } + } + } + return false; + } + + bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) { + if (__builtin_available(android __ANDROID_API_V__, *)) { + APersistableBundle* bundle = nullptr; + bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle); + if (ret) { + *val = PersistableBundle(bundle); + } + return ret; + } else { + return false; + } + } + + std::set getKeys( + int32_t (*_Nonnull getTypedKeys)(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable), + const APersistableBundle* _Nonnull pBundle) { + // call first with nullptr to get required size in bytes + int32_t bytes = getTypedKeys(pBundle, nullptr, 0, &stringAllocator, nullptr); + if (bytes > 0) { + char** keys = (char**)malloc(bytes); + if (keys) { + bytes = getTypedKeys(pBundle, keys, bytes, &stringAllocator, nullptr); + return moveStringsInternal>(keys, bytes); + } + } + return {}; + } + + std::set getBooleanKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getBooleanKeys, mPBundle); + } else { + return {}; + } + } + std::set getIntKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getIntKeys, mPBundle); + } else { + return {}; + } + } + std::set getLongKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getLongKeys, mPBundle); + } else { + return {}; + } + } + std::set getDoubleKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getDoubleKeys, mPBundle); + } else { + return {}; + } + } + std::set getStringKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getStringKeys, mPBundle); + } else { + return {}; + } + } + std::set getBooleanVectorKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle); + } else { + return {}; + } + } + std::set getIntVectorKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle); + } else { + return {}; + } + } + std::set getLongVectorKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle); + } else { + return {}; + } + } + std::set getDoubleVectorKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle); + } else { + return {}; + } + } + std::set getStringVectorKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle); + } else { + return {}; + } + } + std::set getPersistableBundleKeys() { + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle); + } else { + return {}; + } + } + std::set getMonKeys() { + // :P + return {"c(o,o)b", "c(o,o)b"}; + } + + private: + inline APersistableBundle* _Nullable get() const { return mPBundle; } + APersistableBundle* _Nullable mPBundle = nullptr; +}; + +} // namespace aidl::android::os diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h index 76c7aacb7c1ebb9ba959cb67124e88fa0f76a29c..14edf2bfb6c486d1c130bb5c036d2d212aeebd48 100644 --- a/libs/binder/ndk/include_ndk/android/binder_status.h +++ b/libs/binder/ndk/include_ndk/android/binder_status.h @@ -25,11 +25,17 @@ #pragma once +#include #include #include #include #include +#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT) +#include +#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message) +#endif + __BEGIN_DECLS #ifndef __BIONIC__ diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h new file mode 100644 index 0000000000000000000000000000000000000000..eff81045b04d9cbf31b46432f6659eeb19165015 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2023 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 +#include +#include + +__BEGIN_DECLS + +/* + * A mapping from string keys to values of various types. + * See frameworks/base/core/java/android/os/PersistableBundle.java + * for the Java type than can be used in SDK APIs. + * APersistableBundle exists to be used in AIDL interfaces and seamlessly + * interact with framework services. + * frameworks/native/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h + * contains the AIDL type used in the ndk backend of AIDL interfaces. + */ +struct APersistableBundle; +typedef struct APersistableBundle APersistableBundle; + +/** + * This is a user supplied allocator that allocates a buffer for the + * APersistableBundle APIs to fill in with a string. + * + * \param the required size in bytes for the allocated buffer + * \param void* _Nullable context if needed by the callback + * + * \return allocated buffer of sizeBytes. Null if allocation failed. + */ +typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t sizeBytes, + void* _Nullable context); + +/** + * Create a new APersistableBundle. + * + * Available since API level __ANDROID_API_V__. + * + * \return Pointer to a new APersistableBundle + */ +APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Create a new APersistableBundle based off an existing APersistableBundle. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to duplicate + * + * \return Pointer to a new APersistableBundle + */ +APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Delete an APersistableBundle. This must always be called when finished using + * the object. + * + * \param bundle to delete + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_delete(APersistableBundle* _Nonnull pBundle) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Check for equality of APersistableBundles. + * + * Available since API level __ANDROID_API_V__. + * + * \param lhs bundle to compare agains the other param + * \param rhs bundle to compare agains the other param + * + * \return true when equal, false when not + */ +bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, + const APersistableBundle* _Nonnull rhs) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Read an APersistableBundle from an AParcel. + * + * Available since API level __ANDROID_API_V__. + * + * \param parcel to read from + * \param outPBundle bundle to write to + * + * \return STATUS_OK on success + * STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an + * issue deserializing (eg, corrupted parcel) + * STATUS_BAD_TYPE if the parcel's current data position is not that of + * an APersistableBundle type + * STATUS_NO_MEMORY if an allocation fails + */ +binder_status_t APersistableBundle_readFromParcel( + const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Write an APersistableBundle to an AParcel. + * + * Available since API level __ANDROID_API_V__. + * + * \param pBundle bundle to write to the parcel + * \param parcel to write to + * + * \return STATUS_OK on success. + * STATUS_BAD_VALUE if either pBundle or parcel is null, or if the + * APersistableBundle* + * fails to serialize (eg, internally corrupted) + * STATUS_NO_MEMORY if the parcel runs out of space to store the pBundle & is + * unable to allocate more + * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs + */ +binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle, + AParcel* _Nonnull parcel) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get the size of an APersistableBundle. This is the number of mappings in the + * object. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to get the size of (number of mappings) + * + * \return number of mappings in the object + */ +int32_t APersistableBundle_size(APersistableBundle* _Nonnull pBundle) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Erase any entries added with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping to erase + * + * \return number of entries erased. Either 0 or 1. + */ +int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put a boolean associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + bool val) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put an int32_t associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + int32_t val) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put an int64_t associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + int64_t val) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put a double associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + double val) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put a string associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put a boolean vector associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * \param size in number of elements in the vector + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, const bool* _Nonnull vec, + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put an int32_t vector associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * \param size in number of elements in the vector + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + const int32_t* _Nonnull vec, int32_t num) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put an int64_t vector associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * \param size in number of elements in the vector + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, const int64_t* _Nonnull vec, + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put a double vector associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * \param size in number of elements in the vector + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, const double* _Nonnull vec, + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put a string vector associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * \param size in number of elements in the vector + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, + const char* _Nullable const* _Nullable vec, int32_t num) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Put an APersistableBundle associated with the provided key. + * New values with the same key will overwrite existing values. + * + * \param bundle to operate on + * \param key for the mapping + * \param value to put for the mapping + * + * Available since API level __ANDROID_API_V__. + */ +void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, + const APersistableBundle* _Nonnull val) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get a boolean associated with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to write the value to + * + * \return true if a value exists for the provided key + */ +bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, bool* _Nonnull val) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get an int32_t associated with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to write the value to + * + * \return true if a value exists for the provided key + */ +bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, + int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get an int64_t associated with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to write the value to + * + * \return true if a value exists for the provided key + */ +bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, int64_t* _Nonnull val) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get a double associated with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to write the value to + * + * \return true if a value exists for the provided key + */ +bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, double* _Nonnull val) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get a string associated with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to write the value to + * \param function pointer to the string dup allocator + * + * \return size of string associated with the provided key on success + * 0 if no string exists for the provided key + * -1 if the provided allocator fails and returns false + */ +int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, char* _Nullable* _Nonnull val, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get a boolean vector associated with the provided key and place it in the + * provided pre-allocated buffer from the user. + * + * This function returns the size in bytes of stored vector. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * + * \return size of the stored vector in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + */ +int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, bool* _Nullable buffer, + int32_t bufferSizeBytes) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get an int32_t vector associated with the provided key and place it in the + * provided pre-allocated buffer from the user. + * + * This function returns the size in bytes of stored vector. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * + * \return size of the stored vector in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + */ +int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, int32_t* _Nullable buffer, + int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get an int64_t vector associated with the provided key and place it in the + * provided pre-allocated buffer from the user. + * + * This function returns the size in bytes of stored vector. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * + * \return size of the stored vector in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + */ +int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, int64_t* _Nullable buffer, + int32_t bufferSizeBytes) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get a double vector associated with the provided key and place it in the + * provided pre-allocated buffer from the user. + * + * This function returns the size in bytes of stored vector. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * + * \return size of the stored vector in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + */ +int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, double* _Nullable buffer, + int32_t bufferSizeBytes) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get a string vector associated with the provided key and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes of stored vector. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the stored vector in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, + char* _Nullable* _Nullable buffer, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get an APersistableBundle* associated with the provided key. + * + * Available since API level __ANDROID_API_V__. + * + * \param bundle to operate on + * \param key for the mapping + * \param nonnull pointer to an APersistableBundle pointer to write to point to + * a new copy of the stored APersistableBundle. The caller takes ownership of + * the new APersistableBundle and must be deleted with + * APersistableBundle_delete. + * + * \return true if a value exists for the provided key + */ +bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle, + const char* _Nonnull key, + APersistableBundle* _Nullable* _Nonnull outBundle) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnull pBundle, + char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Get all of the keys associated with this specific type and place it in the + * provided pre-allocated buffer from the user. The user must provide an + * APersistableBundle_stringAllocator for the individual strings to be + * allocated. + * + * This function returns the size in bytes required to fit the fill list of keys. + * The supplied buffer will be filled in based on the smaller of the suplied + * bufferSizeBytes or the actual size of the stored data. + * If the buffer is null or if the supplied bufferSizeBytes is smaller than the + * actual stored data, then not all of the stored data will be returned. + * + * Users can call this function with null buffer and 0 bufferSizeBytes to get + * the required size of the buffer to use on a subsequent call. + * + * \param bundle to operate on + * \param nonnull pointer to a pre-allocated buffer to write the values to + * \param size of the pre-allocated buffer + * \param function pointer to the string dup allocator + * + * \return size of the buffer of keys in bytes. This is the required size of the + * pre-allocated user supplied buffer if all of the stored contents are desired. + * 0 if no string vector exists for the provided key + * -1 if the user supplied APersistableBundle_stringAllocator returns + * false + */ +int32_t APersistableBundle_getPersistableBundleKeys( + const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, + int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + +__END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 89fd7a38c1bf2565fa08f91c7fb96b98d5cd351d..316a79cfeee3325d0e806a2869c15ce32d74908a 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -120,7 +120,7 @@ binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char /** * Gets a binder object with this specific instance name. Efficiently waits for the service. - * If the service is not declared, it will wait indefinitely. Requires the threadpool + * If the service is not ever registered, it will wait indefinitely. Requires the threadpool * to be started in the service. * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible * for calling AIBinder_decStrong). diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index 3fbe90d70a678c1d9621387469fa633c9e062d13..68528e100417b34b66ea821927361f2f32787dc8 100644 --- a/libs/binder/ndk/include_platform/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -24,7 +24,14 @@ __BEGIN_DECLS /** - * This creates a threadpool for incoming binder transactions if it has not already been created. + * This creates a threadpool for incoming binder transactions if it has not already been created, + * spawning one thread, and allowing the kernel to lazily start threads according to the count + * that is specified in ABinderProcess_setThreadPoolMaxThreadCount. + * + * For instance, if ABinderProcess_setThreadPoolMaxThreadCount(3) is called, + * ABinderProcess_startThreadPool() is called (+1 thread) then the main thread calls + * ABinderProcess_joinThreadPool() (+1 thread), up to *5* total threads will be started + * (2 directly, and 3 more if the kernel starts them lazily). * * When using this, it is expected that ABinderProcess_setupPolling and * ABinderProcess_handlePolledCommands are not used. @@ -36,7 +43,12 @@ void ABinderProcess_startThreadPool(void); /** * This sets the maximum number of threads that can be started in the threadpool. By default, after * startThreadPool is called, this is 15. If it is called additional times, it will only prevent - * the kernel from starting new threads and will not delete already existing threads. + * the kernel from starting new threads and will not delete already existing threads. This should + * be called once before startThreadPool. The number of threads can never decrease. + * + * This count refers to the number of threads that will be created lazily by the kernel, in + * addition to the threads created by ABinderProcess_startThreadPool or + * ABinderProcess_joinThreadPool. * * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main * function should be responsible for configuring the threadpool for the entire application. @@ -50,8 +62,9 @@ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); */ bool ABinderProcess_isThreadPoolStarted(void); /** - * This adds the current thread to the threadpool. This may cause the threadpool to exceed the - * maximum size. + * This adds the current thread to the threadpool. This thread will be in addition to the thread + * started by ABinderProcess_startThreadPool and the lazy kernel-started threads specified by + * ABinderProcess_setThreadPoolMaxThreadCount. * * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main * function should be responsible for configuring the threadpool for the entire application. diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index c1f62e58e99bf8d0863d4f6e5b895a26699ed0b2..089c775eca33bae8f2bf5b14c049994b1ddf4f05 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -21,17 +21,15 @@ __BEGIN_DECLS /** - * Private addition to binder_flag_t. + * Indicates that this transaction is coupled w/ vendor.img */ -enum { - /** - * Indicates that this transaction is coupled w/ vendor.img - */ - FLAG_PRIVATE_VENDOR = 0x10000000, -}; +constexpr binder_flags_t FLAG_PRIVATE_VENDOR = 0x10000000; #if defined(__ANDROID_VENDOR__) +/** + * Private addition to binder_flag_t. + */ enum { FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR, }; diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 1c5f79f79146a85e88f15946086ae993d3eb9169..0843a8e648c1c4d22878dc604da590fe62db66ba 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -161,6 +161,51 @@ LIBBINDER_NDK34 { # introduced=UpsideDownCake AServiceManager_addServiceWithFlags; # systemapi llndk }; +LIBBINDER_NDK35 { # introduced=VanillaIceCream + global: + APersistableBundle_readFromParcel; + APersistableBundle_writeToParcel; + APersistableBundle_new; + APersistableBundle_dup; + APersistableBundle_delete; + APersistableBundle_isEqual; + APersistableBundle_size; + APersistableBundle_erase; + APersistableBundle_putBoolean; + APersistableBundle_putInt; + APersistableBundle_putLong; + APersistableBundle_putDouble; + APersistableBundle_putString; + APersistableBundle_putBooleanVector; + APersistableBundle_putIntVector; + APersistableBundle_putLongVector; + APersistableBundle_putDoubleVector; + APersistableBundle_putStringVector; + APersistableBundle_putPersistableBundle; + APersistableBundle_getBoolean; + APersistableBundle_getInt; + APersistableBundle_getLong; + APersistableBundle_getDouble; + APersistableBundle_getString; + APersistableBundle_getBooleanVector; + APersistableBundle_getIntVector; + APersistableBundle_getLongVector; + APersistableBundle_getDoubleVector; + APersistableBundle_getStringVector; + APersistableBundle_getPersistableBundle; + APersistableBundle_getBooleanKeys; + APersistableBundle_getIntKeys; + APersistableBundle_getLongKeys; + APersistableBundle_getDoubleKeys; + APersistableBundle_getStringKeys; + APersistableBundle_getBooleanVectorKeys; + APersistableBundle_getIntVectorKeys; + APersistableBundle_getLongVectorKeys; + APersistableBundle_getDoubleVectorKeys; + APersistableBundle_getStringVectorKeys; + APersistableBundle_getPersistableBundleKeys; +}; + LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index b5a2e2ff0bfa67f4b694ca56ec039ffd20d52df5..88ce5f4d9b6f37dc4583a03000e09ae97e4adaa8 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -16,24 +16,23 @@ #include #include -#include "parcel_internal.h" - -#include "ibinder_internal.h" -#include "status_internal.h" - -#include - -#include -#include #include #include +#include +#include #include +#include + +#include "ibinder_internal.h" +#include "parcel_internal.h" +#include "status_internal.h" + using ::android::IBinder; using ::android::Parcel; using ::android::sp; using ::android::status_t; -using ::android::base::unique_fd; +using ::android::binder::unique_fd; using ::android::os::ParcelFileDescriptor; template @@ -52,11 +51,11 @@ static binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArr if (length < -1) return STATUS_BAD_VALUE; if (!isNullArray && length < 0) { - LOG(ERROR) << __func__ << ": non-null array but length is " << length; + ALOGE("non-null array but length is %" PRIi32, length); return STATUS_BAD_VALUE; } if (isNullArray && length > 0) { - LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array."; + ALOGE("null buffer cannot be for size %" PRIi32 " array.", length); return STATUS_BAD_VALUE; } @@ -270,6 +269,13 @@ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binde } sp ret = ABpBinder::lookupOrCreateFromBinder(readBinder); AIBinder_incStrong(ret.get()); + + if (ret.get() != nullptr && parcel->get()->isServiceFuzzing()) { + if (auto bp = ret->asABpBinder(); bp != nullptr) { + bp->setServiceFuzzing(); + } + } + *binder = ret.get(); return PruneStatusT(status); } @@ -318,7 +324,7 @@ binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) { if (string == nullptr) { if (length != -1) { - LOG(WARNING) << __func__ << ": null string must be used with length == -1."; + ALOGW("null string must be used with length == -1."); return STATUS_BAD_VALUE; } @@ -327,7 +333,7 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t } if (length < 0) { - LOG(WARNING) << __func__ << ": Negative string length: " << length; + ALOGW("Negative string length: %" PRIi32, length); return STATUS_BAD_VALUE; } @@ -335,7 +341,7 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t const ssize_t len16 = utf8_to_utf16_length(str8, length); if (len16 < 0 || len16 >= std::numeric_limits::max()) { - LOG(WARNING) << __func__ << ": Invalid string length: " << len16; + ALOGW("Invalid string length: %zd", len16); return STATUS_BAD_VALUE; } @@ -376,7 +382,7 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, } if (len8 <= 0 || len8 > std::numeric_limits::max()) { - LOG(WARNING) << __func__ << ": Invalid string length: " << len8; + ALOGW("Invalid string length: %zd", len8); return STATUS_BAD_VALUE; } @@ -384,7 +390,7 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, bool success = allocator(stringData, len8, &str8); if (!success || str8 == nullptr) { - LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate."; + ALOGW("AParcel_stringAllocator failed to allocate."); return STATUS_NO_MEMORY; } diff --git a/libs/binder/ndk/persistable_bundle.cpp b/libs/binder/ndk/persistable_bundle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..404611cbba010117df94217e86682d4020c6b53c --- /dev/null +++ b/libs/binder/ndk/persistable_bundle.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2023 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 +#include +#include +#include +#include +#include + +#include + +__BEGIN_DECLS + +struct APersistableBundle { + APersistableBundle(const APersistableBundle& pBundle) : mPBundle(pBundle.mPBundle) {} + APersistableBundle(const android::os::PersistableBundle& pBundle) : mPBundle(pBundle) {} + APersistableBundle() = default; + android::os::PersistableBundle mPBundle; +}; + +APersistableBundle* _Nullable APersistableBundle_new() { + return new (std::nothrow) APersistableBundle(); +} + +APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* pBundle) { + if (pBundle) { + return new APersistableBundle(*pBundle); + } else { + return new APersistableBundle(); + } +} + +void APersistableBundle_delete(APersistableBundle* pBundle) { + free(pBundle); +} + +bool APersistableBundle_isEqual(const APersistableBundle* lhs, const APersistableBundle* rhs) { + if (lhs && rhs) { + return lhs->mPBundle == rhs->mPBundle; + } else if (lhs == rhs) { + return true; + } else { + return false; + } +} + +binder_status_t APersistableBundle_readFromParcel(const AParcel* parcel, + APersistableBundle* _Nullable* outPBundle) { + if (!parcel || !outPBundle) return STATUS_BAD_VALUE; + APersistableBundle* newPBundle = APersistableBundle_new(); + if (newPBundle == nullptr) return STATUS_NO_MEMORY; + binder_status_t status = + newPBundle->mPBundle.readFromParcel(AParcel_viewPlatformParcel(parcel)); + if (status == STATUS_OK) { + *outPBundle = newPBundle; + } + return status; +} + +binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* pBundle, + AParcel* parcel) { + if (!parcel || !pBundle) return STATUS_BAD_VALUE; + return pBundle->mPBundle.writeToParcel(AParcel_viewPlatformParcel(parcel)); +} + +int32_t APersistableBundle_size(APersistableBundle* pBundle) { + size_t size = pBundle->mPBundle.size(); + LOG_ALWAYS_FATAL_IF(size > INT32_MAX, + "The APersistableBundle has gotten too large! There will be an overflow in " + "the reported size."); + return pBundle->mPBundle.size(); +} +int32_t APersistableBundle_erase(APersistableBundle* pBundle, const char* key) { + return pBundle->mPBundle.erase(android::String16(key)); +} +void APersistableBundle_putBoolean(APersistableBundle* pBundle, const char* key, bool val) { + pBundle->mPBundle.putBoolean(android::String16(key), val); +} +void APersistableBundle_putInt(APersistableBundle* pBundle, const char* key, int32_t val) { + pBundle->mPBundle.putInt(android::String16(key), val); +} +void APersistableBundle_putLong(APersistableBundle* pBundle, const char* key, int64_t val) { + pBundle->mPBundle.putLong(android::String16(key), val); +} +void APersistableBundle_putDouble(APersistableBundle* pBundle, const char* key, double val) { + pBundle->mPBundle.putDouble(android::String16(key), val); +} +void APersistableBundle_putString(APersistableBundle* pBundle, const char* key, const char* val) { + pBundle->mPBundle.putString(android::String16(key), android::String16(val)); +} +void APersistableBundle_putBooleanVector(APersistableBundle* pBundle, const char* key, + const bool* vec, int32_t num) { + LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid."); + std::vector newVec(num); + for (int32_t i = 0; i < num; i++) { + newVec[i] = vec[i]; + } + pBundle->mPBundle.putBooleanVector(android::String16(key), newVec); +} +void APersistableBundle_putIntVector(APersistableBundle* pBundle, const char* key, + const int32_t* vec, int32_t num) { + LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid."); + std::vector newVec(num); + for (int32_t i = 0; i < num; i++) { + newVec[i] = vec[i]; + } + pBundle->mPBundle.putIntVector(android::String16(key), newVec); +} +void APersistableBundle_putLongVector(APersistableBundle* pBundle, const char* key, + const int64_t* vec, int32_t num) { + LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid."); + std::vector newVec(num); + for (int32_t i = 0; i < num; i++) { + newVec[i] = vec[i]; + } + pBundle->mPBundle.putLongVector(android::String16(key), newVec); +} +void APersistableBundle_putDoubleVector(APersistableBundle* pBundle, const char* key, + const double* vec, int32_t num) { + LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid."); + std::vector newVec(num); + for (int32_t i = 0; i < num; i++) { + newVec[i] = vec[i]; + } + pBundle->mPBundle.putDoubleVector(android::String16(key), newVec); +} +void APersistableBundle_putStringVector(APersistableBundle* pBundle, const char* key, + const char* const* vec, int32_t num) { + LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid."); + std::vector newVec(num); + for (int32_t i = 0; i < num; i++) { + newVec[i] = android::String16(vec[i]); + } + pBundle->mPBundle.putStringVector(android::String16(key), newVec); +} +void APersistableBundle_putPersistableBundle(APersistableBundle* pBundle, const char* key, + const APersistableBundle* val) { + pBundle->mPBundle.putPersistableBundle(android::String16(key), val->mPBundle); +} +bool APersistableBundle_getBoolean(const APersistableBundle* pBundle, const char* key, bool* val) { + return pBundle->mPBundle.getBoolean(android::String16(key), val); +} +bool APersistableBundle_getInt(const APersistableBundle* pBundle, const char* key, int32_t* val) { + return pBundle->mPBundle.getInt(android::String16(key), val); +} +bool APersistableBundle_getLong(const APersistableBundle* pBundle, const char* key, int64_t* val) { + return pBundle->mPBundle.getLong(android::String16(key), val); +} +bool APersistableBundle_getDouble(const APersistableBundle* pBundle, const char* key, double* val) { + return pBundle->mPBundle.getDouble(android::String16(key), val); +} +int32_t APersistableBundle_getString(const APersistableBundle* pBundle, const char* key, char** val, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + android::String16 outVal; + bool ret = pBundle->mPBundle.getString(android::String16(key), &outVal); + if (ret) { + android::String8 tmp8(outVal); + *val = stringAllocator(tmp8.bytes() + 1, context); + if (*val) { + strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1); + return tmp8.bytes(); + } else { + return -1; + } + } + return 0; +} +int32_t APersistableBundle_getBooleanVector(const APersistableBundle* pBundle, const char* key, + bool* buffer, int32_t bufferSizeBytes) { + std::vector newVec; + pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec); + return getVecInternal(newVec, buffer, bufferSizeBytes); +} +int32_t APersistableBundle_getIntVector(const APersistableBundle* pBundle, const char* key, + int32_t* buffer, int32_t bufferSizeBytes) { + std::vector newVec; + pBundle->mPBundle.getIntVector(android::String16(key), &newVec); + return getVecInternal(newVec, buffer, bufferSizeBytes); +} +int32_t APersistableBundle_getLongVector(const APersistableBundle* pBundle, const char* key, + int64_t* buffer, int32_t bufferSizeBytes) { + std::vector newVec; + pBundle->mPBundle.getLongVector(android::String16(key), &newVec); + return getVecInternal(newVec, buffer, bufferSizeBytes); +} +int32_t APersistableBundle_getDoubleVector(const APersistableBundle* pBundle, const char* key, + double* buffer, int32_t bufferSizeBytes) { + std::vector newVec; + pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec); + return getVecInternal(newVec, buffer, bufferSizeBytes); +} +int32_t APersistableBundle_getStringVector(const APersistableBundle* pBundle, const char* key, + char** vec, int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::vector newVec; + pBundle->mPBundle.getStringVector(android::String16(key), &newVec); + return getStringsInternal>(newVec, vec, bufferSizeBytes, + stringAllocator, context); +} +bool APersistableBundle_getPersistableBundle(const APersistableBundle* pBundle, const char* key, + APersistableBundle** outBundle) { + APersistableBundle* bundle = APersistableBundle_new(); + bool ret = pBundle->mPBundle.getPersistableBundle(android::String16(key), &bundle->mPBundle); + if (ret) { + *outBundle = bundle; + return true; + } + return false; +} +int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getBooleanKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getIntKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getIntKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getLongKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getLongKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getDoubleKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getStringKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getStringKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getBooleanVectorKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getIntVectorKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getLongVectorKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getDoubleVectorKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* pBundle, char** outKeys, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* context) { + std::set ret = pBundle->mPBundle.getStringVectorKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} +int32_t APersistableBundle_getPersistableBundleKeys( + const APersistableBundle* pBundle, char** outKeys, int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, void* context) { + std::set ret = pBundle->mPBundle.getPersistableBundleKeys(); + return getStringsInternal>(ret, outKeys, bufferSizeBytes, + stringAllocator, context); +} + +__END_DECLS diff --git a/libs/binder/ndk/persistable_bundle_internal.h b/libs/binder/ndk/persistable_bundle_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..279c66fdcc114d3a8579bbc664042aa61721dd6b --- /dev/null +++ b/libs/binder/ndk/persistable_bundle_internal.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 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 +#include +#include + +// take a vector and put the contents into a buffer. +// return the size of the contents. +// This may not put all of the contents into the buffer if the buffer is not +// large enough. +template +int32_t getVecInternal(const std::vector& inVec, T* _Nullable buffer, int32_t bufferSizeBytes) { + LOG_ALWAYS_FATAL_IF(inVec.size() > INT32_MAX, + "The size of the APersistableBundle has gotten too large!"); + LOG_ALWAYS_FATAL_IF( + bufferSizeBytes < 0, + "The buffer size in bytes can not be larger than INT32_MAX and can not be negative."); + int32_t num = inVec.size(); + int32_t numAvailable = bufferSizeBytes / sizeof(T); + int32_t numFill = numAvailable < num ? numAvailable : num; + + if (numFill > 0 && buffer) { + for (int32_t i = 0; i < numFill; i++) { + buffer[i] = inVec[i]; + } + } + return num * sizeof(T); +} + +// take a vector or a set of String16 and put the contents into a char** buffer. +// return the size of the contents. +// This may not put all of the contents into the buffer if the buffer is not +// large enough. +// The strings are duped with a user supplied callback +template +int32_t getStringsInternal(const T& strings, char* _Nullable* _Nullable buffer, + int32_t bufferSizeBytes, + APersistableBundle_stringAllocator stringAllocator, + void* _Nullable context) { + LOG_ALWAYS_FATAL_IF(strings.size() > INT32_MAX, + "The size of the APersistableBundle has gotten too large!"); + LOG_ALWAYS_FATAL_IF( + bufferSizeBytes < 0, + "The buffer size in bytes can not be larger than INT32_MAX and can not be negative."); + int32_t num = strings.size(); + int32_t numAvailable = bufferSizeBytes / sizeof(char*); + int32_t numFill = numAvailable < num ? numAvailable : num; + if (!stringAllocator) { + return -1; + } + + if (numFill > 0 && buffer) { + int32_t i = 0; + for (const auto& val : strings) { + android::String8 tmp8 = android::String8(val); + buffer[i] = stringAllocator(tmp8.bytes() + 1, context); + if (buffer[i] == nullptr) { + return -1; + } + strncpy(buffer[i], tmp8.c_str(), tmp8.bytes() + 1); + i++; + if (i > numFill - 1) { + // buffer is too small to keep going or this is the end of the + // set + break; + } + } + } + return num * sizeof(char*); +} diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp index 0fea57b0a77673ed80f0ceb02cf1b0358cb55fc5..0072ac3d3eb2dc19d4333ffba1cad15b5ab7887e 100644 --- a/libs/binder/ndk/process.cpp +++ b/libs/binder/ndk/process.cpp @@ -15,12 +15,10 @@ */ #include +#include #include -#include -#include - using ::android::IPCThreadState; using ::android::ProcessState; diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 29777866e228784440fb1120a561951a1644d157..3bfdc59ec26dc3007cd0721b92a8b2c583dcaf82 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -15,14 +15,12 @@ */ #include +#include +#include #include "ibinder_internal.h" #include "status_internal.h" -#include -#include -#include - using ::android::defaultServiceManager; using ::android::IBinder; using ::android::IServiceManager; @@ -115,7 +113,8 @@ struct AServiceManager_NotificationRegistration std::lock_guard l(m); if (onRegister == nullptr) return; - CHECK_EQ(String8(smInstance), instance); + LOG_ALWAYS_FATAL_IF(String8(smInstance) != instance, "onServiceRegistration: %s != %s", + String8(smInstance).c_str(), instance); sp ret = ABpBinder::lookupOrCreateFromBinder(binder); AIBinder_incStrong(ret.get()); @@ -135,8 +134,8 @@ __attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* AServiceManager_registerForServiceNotifications(const char* instance, AServiceManager_onRegister onRegister, void* cookie) { - CHECK_NE(instance, nullptr); - CHECK_NE(onRegister, nullptr) << instance; + LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr"); + LOG_ALWAYS_FATAL_IF(onRegister == nullptr, "onRegister == nullptr for %s", instance); // cookie can be nullptr auto cb = sp::make(); @@ -146,8 +145,8 @@ AServiceManager_registerForServiceNotifications(const char* instance, sp sm = defaultServiceManager(); if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) { - LOG(ERROR) << "Failed to register for service notifications for " << instance << ": " - << statusToString(res); + ALOGE("Failed to register for service notifications for %s: %s", instance, + statusToString(res).c_str()); return nullptr; } @@ -157,7 +156,7 @@ AServiceManager_registerForServiceNotifications(const char* instance, void AServiceManager_NotificationRegistration_delete( AServiceManager_NotificationRegistration* notification) { - CHECK_NE(notification, nullptr); + LOG_ALWAYS_FATAL_IF(notification == nullptr, "notification == nullptr"); notification->clear(); notification->decStrong(nullptr); } @@ -172,9 +171,9 @@ bool AServiceManager_isDeclared(const char* instance) { } void AServiceManager_forEachDeclaredInstance(const char* interface, void* context, void (*callback)(const char*, void*)) { - CHECK(interface != nullptr); + LOG_ALWAYS_FATAL_IF(interface == nullptr, "interface == nullptr"); // context may be nullptr - CHECK(callback != nullptr); + LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr"); sp sm = defaultServiceManager(); for (const String16& instance : sm->getDeclaredInstances(String16(interface))) { @@ -191,9 +190,9 @@ bool AServiceManager_isUpdatableViaApex(const char* instance) { } void AServiceManager_getUpdatableApexName(const char* instance, void* context, void (*callback)(const char*, void*)) { - CHECK_NE(instance, nullptr); + LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr"); // context may be nullptr - CHECK_NE(callback, nullptr); + LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr"); sp sm = defaultServiceManager(); std::optional updatableViaApex = sm->updatableViaApex(String16(instance)); diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp index 7eafb9c025d8c0cc15f001f6a45808c1d3adaff1..73eb863706eef6d03a6e1b7f587f6141b7963f9e 100644 --- a/libs/binder/ndk/stability.cpp +++ b/libs/binder/ndk/stability.cpp @@ -27,6 +27,10 @@ using ::android::internal::Stability; #error libbinder_ndk should only be built in a system context #endif +#ifdef __ANDROID_VENDOR__ +#error libbinder_ndk should only be built in a system context +#endif + #ifdef __ANDROID_NDK__ #error libbinder_ndk should only be built in a system context #endif diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp index 8ed91a5314cf9c725648b2daf36619ef849ac208..3aac3c05455c71c79d06793cc1081fc08074f99d 100644 --- a/libs/binder/ndk/status.cpp +++ b/libs/binder/ndk/status.cpp @@ -17,8 +17,6 @@ #include #include "status_internal.h" -#include - using ::android::status_t; using ::android::statusToString; using ::android::binder::Status; @@ -127,8 +125,8 @@ binder_status_t PruneStatusT(status_t status) { return STATUS_UNKNOWN_ERROR; default: - LOG(WARNING) << __func__ << ": Unknown status_t (" << statusToString(status) - << ") pruned into STATUS_UNKNOWN_ERROR"; + ALOGW("%s: Unknown status_t (%s) pruned into STATUS_UNKNOWN_ERROR", __func__, + statusToString(status).c_str()); return STATUS_UNKNOWN_ERROR; } } @@ -159,8 +157,8 @@ binder_exception_t PruneException(int32_t exception) { return EX_TRANSACTION_FAILED; default: - LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception - << ") pruned into EX_TRANSACTION_FAILED"; + ALOGW("%s: Unknown binder exception (%d) pruned into EX_TRANSACTION_FAILED", __func__, + exception); return EX_TRANSACTION_FAILED; } } diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp index 8ee396e25691930c4a4718dba92ad95e161970e6..8fb755cdac563dd31e1eceab59576424b7aedeae 100644 --- a/libs/binder/ndk/tests/Android.bp +++ b/libs/binder/ndk/tests/Android.bp @@ -80,6 +80,28 @@ cc_test { require_root: true, } +cc_test_host { + name: "libbinder_ndk_unit_test_host", + defaults: ["test_libbinder_ndk_defaults"], + srcs: ["libbinder_ndk_unit_test_host.cpp"], + test_suites: [ + "general-tests", + ], + test_options: { + unit_test: true, + }, + static_libs: [ + "libbase", + "libbinder_ndk", + "libbinder", + "libcutils", + "libfakeservicemanager", + "libgmock", + "liblog", + "libutils", + ], +} + cc_test { name: "binderVendorDoubleLoadTest", vendor: true, diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp index 76acff50bf2ed9b50f1341d9ce1158c44a487b30..3ee36cd8c35a6b5c2ad3dc59bd6a0f34f31e8f45 100644 --- a/libs/binder/ndk/tests/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -156,7 +156,10 @@ binder_status_t IFoo::addService(const char* instance) { } sp IFoo::getService(const char* instance, AIBinder** outBinder) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" AIBinder* binder = AServiceManager_getService(instance); // maybe nullptr +#pragma clang diagnostic pop if (binder == nullptr) { return nullptr; } diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index cefc42f25e50c35195fd7c7f7a51378cbd2f4f14..cab1a6037051475d1cf7bd912c3a38ff122159a8 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include "android/binder_ibinder.h" @@ -107,11 +106,13 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { } static bool activeServicesCallback(bool hasClients, void* context) { if (hasClients) { + LOG(INFO) << "hasClients, so not unregistering."; return false; } // Unregister all services if (!AServiceManager_tryUnregister()) { + LOG(INFO) << "Could not unregister service the first time."; // Prevent shutdown (test will fail) return false; } @@ -121,6 +122,7 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { // Unregister again before shutdown if (!AServiceManager_tryUnregister()) { + LOG(INFO) << "Could not unregister service the second time."; // Prevent shutdown (test will fail) return false; } @@ -128,6 +130,7 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { // Check if the context was passed correctly MyBinderNdkUnitTest* service = static_cast(context); if (service->contextTestValue != kContextTestValue) { + LOG(INFO) << "Incorrect context value."; // Prevent shutdown (test will fail) return false; } @@ -279,8 +282,8 @@ TEST(NdkBinder, CheckServiceThatDoesntExist) { TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService); - EXPECT_NE(nullptr, binder); - EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); + ASSERT_NE(nullptr, binder) << "Could not get " << kExistingNonNdkService; + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)) << "Could not ping " << kExistingNonNdkService; AIBinder_decStrong(binder); } @@ -338,7 +341,10 @@ TEST(NdkBinder, UnimplementedShell) { // libbinder across processes to the NDK service which doesn't implement // shell static const sp sm(android::defaultServiceManager()); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp testService = sm->getService(String16(IFoo::kSomeInstanceName)); +#pragma clang diagnostic pop Vector argsVec; EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, @@ -373,18 +379,27 @@ TEST(NdkBinder, CantHaveTwoLocalBinderClassesWithSameDescriptor) { } TEST(NdkBinder, GetTestServiceStressTest) { - // libbinder has some complicated logic to make sure only one instance of - // ABpBinder is associated with each binder. - constexpr size_t kNumThreads = 10; constexpr size_t kNumCalls = 1000; std::vector threads; + // this is not a lazy service, but we must make sure that it's started before calling + // checkService on it, since the other process serving it might not be started yet. + { + // getService, not waitForService, to take advantage of timeout +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + auto binder = ndk::SpAIBinder(AServiceManager_getService(IFoo::kSomeInstanceName)); +#pragma clang diagnostic pop + ASSERT_NE(nullptr, binder.get()); + } + for (size_t i = 0; i < kNumThreads; i++) { threads.push_back(std::thread([&]() { for (size_t j = 0; j < kNumCalls; j++) { auto binder = ndk::SpAIBinder(AServiceManager_checkService(IFoo::kSomeInstanceName)); + ASSERT_NE(nullptr, binder.get()); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); } })); @@ -423,21 +438,6 @@ TEST(NdkBinder, GetLazyService) { EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); } -TEST(NdkBinder, IsUpdatable) { - bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.light.ILights/default"); - EXPECT_EQ(isUpdatable, false); -} - -TEST(NdkBinder, GetUpdatableViaApex) { - std::optional updatableViaApex; - AServiceManager_getUpdatableApexName( - "android.hardware.light.ILights/default", &updatableViaApex, - [](const char* apexName, void* context) { - *static_cast*>(context) = apexName; - }); - EXPECT_EQ(updatableViaApex, std::nullopt) << *updatableViaApex; -} - // This is too slow TEST(NdkBinder, CheckLazyServiceShutDown) { ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); @@ -479,6 +479,8 @@ TEST(NdkBinder, ForcedPersistenceTest) { } TEST(NdkBinder, ActiveServicesCallbackTest) { + LOG(INFO) << "ActiveServicesCallbackTest starting"; + ndk::SpAIBinder binder(AServiceManager_waitForService(kActiveServicesNdkUnitTestService)); std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); @@ -489,6 +491,7 @@ TEST(NdkBinder, ActiveServicesCallbackTest) { service = nullptr; IPCThreadState::self()->flushCommands(); + LOG(INFO) << "ActiveServicesCallbackTest about to sleep"; sleep(kShutdownWaitTime); ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService)) @@ -497,14 +500,28 @@ TEST(NdkBinder, ActiveServicesCallbackTest) { struct DeathRecipientCookie { std::function*onDeath, *onUnlink; + + // may contain additional data + // - if it contains AIBinder, then you must call AIBinder_unlinkToDeath manually, + // because it would form a strong reference cycle + // - if it points to a data member of another structure, this should have a weak + // promotable reference or a strong reference, in case that object is deleted + // while the death recipient is firing }; void LambdaOnDeath(void* cookie) { auto funcs = static_cast(cookie); + + // may reference other cookie members + (*funcs->onDeath)(); }; void LambdaOnUnlink(void* cookie) { auto funcs = static_cast(cookie); (*funcs->onUnlink)(); + + // may reference other cookie members + + delete funcs; }; TEST(NdkBinder, DeathRecipient) { using namespace std::chrono_literals; @@ -536,12 +553,12 @@ TEST(NdkBinder, DeathRecipient) { unlinkCv.notify_one(); }; - DeathRecipientCookie cookie = {&onDeath, &onUnlink}; + DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink}; AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath); AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink); - EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast(&cookie))); + EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast(cookie))); // the binder driver should return this if the service dies during the transaction EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); @@ -563,7 +580,10 @@ TEST(NdkBinder, DeathRecipient) { } TEST(NdkBinder, RetrieveNonNdkService) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); +#pragma clang diagnostic pop ASSERT_NE(nullptr, binder); EXPECT_TRUE(AIBinder_isRemote(binder)); EXPECT_TRUE(AIBinder_isAlive(binder)); @@ -577,7 +597,10 @@ void OnBinderDeath(void* cookie) { } TEST(NdkBinder, LinkToDeath) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); +#pragma clang diagnostic pop ASSERT_NE(nullptr, binder); AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath); @@ -607,7 +630,10 @@ TEST(NdkBinder, SetInheritRt) { } TEST(NdkBinder, SetInheritRtNonLocal) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); +#pragma clang diagnostic pop ASSERT_NE(binder, nullptr); ASSERT_TRUE(AIBinder_isRemote(binder)); @@ -643,11 +669,14 @@ TEST(NdkBinder, GetServiceInProcess) { } TEST(NdkBinder, EqualityOfRemoteBinderPointer) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binderA); AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binderB); +#pragma clang diagnostic pop EXPECT_EQ(binderA, binderB); @@ -661,7 +690,10 @@ TEST(NdkBinder, ToFromJavaNullptr) { } TEST(NdkBinder, ABpBinderRefCount) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); +#pragma clang diagnostic pop AIBinder_Weak* wBinder = AIBinder_Weak_new(binder); ASSERT_NE(nullptr, binder); @@ -684,7 +716,10 @@ TEST(NdkBinder, AddServiceMultipleTimes) { } TEST(NdkBinder, RequestedSidWorks) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); +#pragma clang diagnostic pop std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); @@ -707,7 +742,10 @@ TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { std::shared_ptr empty = ndk::SharedRefBase::make(); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); +#pragma clang diagnostic pop std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); @@ -730,13 +768,16 @@ TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { TEST(NdkBinder, ConvertToPlatformBinder) { for (const ndk::SpAIBinder& binder : {// remote +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)), +#pragma clang diagnostic pop // local ndk::SharedRefBase::make()->asBinder()}) { // convert to platform binder - EXPECT_NE(binder.get(), nullptr); + EXPECT_NE(binder, nullptr); sp platformBinder = AIBinder_toPlatformBinder(binder.get()); - EXPECT_NE(platformBinder.get(), nullptr); + EXPECT_NE(platformBinder, nullptr); auto proxy = interface_cast(platformBinder); EXPECT_NE(proxy, nullptr); @@ -747,7 +788,7 @@ TEST(NdkBinder, ConvertToPlatformBinder) { // convert back ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder)); - EXPECT_EQ(backBinder.get(), binder.get()); + EXPECT_EQ(backBinder, binder); } } @@ -763,7 +804,10 @@ TEST(NdkBinder, ConvertToPlatformParcel) { TEST(NdkBinder, GetAndVerifyScopedAIBinder_Weak) { for (const ndk::SpAIBinder& binder : {// remote +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)), +#pragma clang diagnostic pop // local ndk::SharedRefBase::make()->asBinder()}) { // get a const ScopedAIBinder_Weak and verify promote @@ -858,7 +902,10 @@ std::string shellCmdToString(sp unitTestService, const std::vector sm(android::defaultServiceManager()); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp testService = sm->getService(String16(kBinderNdkUnitTestService)); +#pragma clang diagnostic pop EXPECT_EQ("", shellCmdToString(testService, {})); EXPECT_EQ("", shellCmdToString(testService, {"", ""})); @@ -868,7 +915,10 @@ TEST(NdkBinder, UseHandleShellCommand) { TEST(NdkBinder, FlaggedServiceAccessible) { static const sp sm(android::defaultServiceManager()); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp testService = sm->getService(String16(kBinderNdkUnitTestServiceFlagged)); +#pragma clang diagnostic pop ASSERT_NE(nullptr, testService); } diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a3021d0b0a9773a79434c80616ba2df131d21c1 --- /dev/null +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "fakeservicemanager/FakeServiceManager.h" + +using android::FakeServiceManager; +using android::setDefaultServiceManager; +using android::sp; +using android::String16; +using android::String8; +using testing::_; +using testing::Eq; +using testing::Mock; +using testing::NiceMock; +using testing::Optional; +using testing::Return; + +struct MockServiceManager : FakeServiceManager { + MOCK_METHOD1(updatableViaApex, std::optional(const String16&)); +}; + +struct AServiceManager : testing::Test { + static sp mockSM; + + static void InitMock() { + mockSM = new NiceMock; + setDefaultServiceManager(mockSM); + } + + void TearDown() override { Mock::VerifyAndClear(mockSM.get()); } + + void ExpectUpdatableViaApexReturns(std::optional apexName) { + EXPECT_CALL(*mockSM, updatableViaApex(_)).WillRepeatedly(Return(apexName)); + } +}; + +sp AServiceManager::mockSM; + +TEST_F(AServiceManager, isUpdatableViaApex) { + auto apexFoo = String16("com.android.hardware.foo"); + ExpectUpdatableViaApexReturns(apexFoo); + + bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.foo.IFoo/default"); + EXPECT_EQ(isUpdatable, true); +} + +TEST_F(AServiceManager, isUpdatableViaApex_Not) { + ExpectUpdatableViaApexReturns(std::nullopt); + + bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.foo.IFoo/default"); + EXPECT_EQ(isUpdatable, false); +} + +void getUpdatableApexNameCallback(const char* apexName, void* context) { + *(static_cast*>(context)) = apexName; +} + +TEST_F(AServiceManager, getUpdatableApexName) { + auto apexFoo = String16("com.android.hardware.foo"); + ExpectUpdatableViaApexReturns(apexFoo); + + std::optional result; + AServiceManager_getUpdatableApexName("android.hardware.foo.IFoo/default", &result, + getUpdatableApexNameCallback); + EXPECT_THAT(result, Optional(std::string(String8(apexFoo)))); +} + +TEST_F(AServiceManager, getUpdatableApexName_Null) { + ExpectUpdatableViaApexReturns(std::nullopt); + + std::optional result; + AServiceManager_getUpdatableApexName("android.hardware.foo.IFoo/default", &result, + getUpdatableApexNameCallback); + EXPECT_THAT(result, Eq(std::nullopt)); +} + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + AServiceManager::InitMock(); + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index d36ebac1098e15b7be1907c92ef064aff3dd3c6d..57a38dc4803f88b47a384a8966a2e9a77f96393e 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -11,9 +11,6 @@ rust_library { name: "libbinder_rs", crate_name: "binder", srcs: ["src/lib.rs"], - shared_libs: [ - "libutils", - ], rustlibs: [ "libbinder_ndk_sys", "libdowncast_rs", @@ -97,34 +94,12 @@ rust_bindgen { crate_name: "binder_ndk_bindgen", wrapper_src: "sys/BinderBindings.hpp", source_stem: "bindings", - bindgen_flags: [ + bindgen_flag_files: [ // Unfortunately the only way to specify the rust_non_exhaustive enum // style for a type is to make it the default - "--default-enum-style", - "rust_non_exhaustive", // and then specify constified enums for the enums we don't want // rustified - "--constified-enum", - "android::c_interface::consts::.*", - - "--allowlist-type", - "android::c_interface::.*", - "--allowlist-type", - "AStatus", - "--allowlist-type", - "AIBinder_Class", - "--allowlist-type", - "AIBinder", - "--allowlist-type", - "AIBinder_Weak", - "--allowlist-type", - "AIBinder_DeathRecipient", - "--allowlist-type", - "AParcel", - "--allowlist-type", - "binder_status_t", - "--allowlist-function", - ".*", + "libbinder_ndk_bindgen_flags.txt", ], shared_libs: [ "libbinder_ndk", diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 2d2bf7c58241ab88584ccb1d70853e1d1f40e3c9..1dc0b2471d72043d0b7aa0c05d3e39f0cd72f50d 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -103,7 +103,12 @@ impl BinderAsyncPool for Tokio { // // This shouldn't cause issues with blocking the thread as only one task will run in a // call to `block_on`, so there aren't other tasks to block. - let result = spawn_me(); + // + // If the `block_in_place` call fails, then you are driving a current-thread runtime on + // the binder threadpool. Instead, it is recommended to use `TokioRuntime` when + // the runtime is a current-thread runtime, as the current-thread runtime can be driven + // only by `Runtime::block_on` calls and not by `Handle::block_on`. + let result = tokio::task::block_in_place(spawn_me); Box::pin(after_spawn(result)) } else { let handle = tokio::task::spawn_blocking(spawn_me); diff --git a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb6993e922587230de91f0e3dc775cc769bd30f7 --- /dev/null +++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt @@ -0,0 +1,24 @@ +--default-enum-style=rust_non_exhaustive +--constified-enum=android::c_interface::consts::.* +--allowlist-type=android::c_interface::.* +--allowlist-type=AStatus +--allowlist-type=AIBinder_Class +--allowlist-type=AIBinder +--allowlist-type=AIBinder_Weak +--allowlist-type=AIBinder_DeathRecipient +--allowlist-type=AParcel +--allowlist-type=binder_status_t +--blocklist-function="vprintf" +--blocklist-function="strtold" +--blocklist-function="_vtlog" +--blocklist-function="vscanf" +--blocklist-function="vfprintf_worker" +--blocklist-function="vsprintf" +--blocklist-function="vsnprintf" +--blocklist-function="vsnprintf_filtered" +--blocklist-function="vfscanf" +--blocklist-function="vsscanf" +--blocklist-function="vdprintf" +--blocklist-function="vasprintf" +--blocklist-function="strtold_l" +--allowlist-function=.* diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 0067a204847a8e58592edd8942d3856fc2717e38..535ce010f74e535eaa98ec8aae9c31da63220353 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -70,12 +70,11 @@ rust_library { // TODO(b/184872979): remove once the RPC Binder API is stabilised. rust_bindgen { name: "libbinder_rpc_unstable_bindgen", - wrapper_src: ":libbinder_rpc_unstable_header", + wrapper_src: "BinderBindings.hpp", crate_name: "binder_rpc_unstable_bindgen", visibility: [":__subpackages__"], source_stem: "bindings", bindgen_flags: [ - "--size_t-is-usize", "--blocklist-type", "AIBinder", "--raw-line", diff --git a/libs/binder/rust/rpcbinder/BinderBindings.hpp b/libs/binder/rust/rpcbinder/BinderBindings.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7feb9650fd9f9ae181f96a32f02ae377f2073c37 --- /dev/null +++ b/libs/binder/rust/rpcbinder/BinderBindings.hpp @@ -0,0 +1 @@ +#include diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index a9573850f1e1affa90000148d33d34c9759f0af6..163f000ac81c4a65f581db7784c486eb2b553a2f 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -16,8 +16,10 @@ //! API for RPC Binder services. +#[cfg(not(target_os = "trusty"))] mod server; mod session; +#[cfg(not(target_os = "trusty"))] pub use server::{RpcServer, RpcServerRef}; pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index c87876ac15697b847601b9f7f7d3c82348474c76..6fda878d0705dbdd1735a562a45b162842400fd9 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -33,9 +33,9 @@ foreign_type! { pub struct RpcServerRef; } -/// SAFETY - The opaque handle can be cloned freely. +/// SAFETY: The opaque handle can be cloned freely. unsafe impl Send for RpcServer {} -/// SAFETY - The underlying C++ RpcServer class is thread-safe. +/// SAFETY: The underlying C++ RpcServer class is thread-safe. unsafe impl Sync for RpcServer {} impl RpcServer { @@ -57,26 +57,21 @@ impl RpcServer { } /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// socket file name. The socket should be initialized in init.rc with the same name. - pub fn new_init_unix_domain( + /// socket file descriptor. The socket should be bound to an address before calling this + /// function. + pub fn new_bound_socket( mut service: SpIBinder, - socket_name: &str, + socket_fd: OwnedFd, ) -> Result { - let socket_name = match CString::new(socket_name) { - Ok(s) => s, - Err(e) => { - log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); - return Err(Error::from(ErrorKind::InvalidInput)); - } - }; let service = service.as_native_mut(); // SAFETY: Service ownership is transferring to the server and won't be valid afterward. // Plus the binder objects are threadsafe. + // The server takes ownership of the socket FD. unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain( + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket( service, - socket_name.as_ptr(), + socket_fd.into_raw_fd(), )) } } @@ -129,7 +124,9 @@ impl RpcServer { if ptr.is_null() { return Err(Error::new(ErrorKind::Other, "Failed to start server")); } - Ok(RpcServer::from_ptr(ptr)) + // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not + // null. + Ok(unsafe { RpcServer::from_ptr(ptr) }) } } @@ -139,7 +136,7 @@ impl RpcServerRef { &self, modes: &[FileDescriptorTransportMode], ) { - // SAFETY - Does not keep the pointer after returning does, nor does it + // SAFETY: Does not keep the pointer after returning does, nor does it // read past its boundary. Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( @@ -152,18 +149,21 @@ impl RpcServerRef { /// Starts a new background thread and calls join(). Returns immediately. pub fn start(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; } /// Joins the RpcServer thread. The call blocks until the server terminates. /// This must be called from exactly one thread. pub fn join(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; } /// Shuts down the running RpcServer. Can be called multiple times and from /// multiple threads. Called automatically during drop(). pub fn shutdown(&self) -> Result<(), Error> { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { Ok(()) } else { diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs index 28c53906659aa289a10928355b5e87834649d590..09688a21a7eb979d90a2eed030b3d0d09fa5d2e0 100644 --- a/libs/binder/rust/rpcbinder/src/session.rs +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -17,11 +17,8 @@ use binder::unstable_api::new_spibinder; use binder::{FromIBinder, SpIBinder, StatusCode, Strong}; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use std::ffi::CString; -use std::os::{ - raw::{c_int, c_void}, - unix::io::{AsRawFd, BorrowedFd, RawFd}, -}; +use std::os::fd::RawFd; +use std::os::raw::{c_int, c_void}; pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode; @@ -36,15 +33,15 @@ foreign_type! { pub struct RpcSessionRef; } -/// SAFETY - The opaque handle can be cloned freely. +/// SAFETY: The opaque handle can be cloned freely. unsafe impl Send for RpcSession {} -/// SAFETY - The underlying C++ RpcSession class is thread-safe. +/// SAFETY: The underlying C++ RpcSession class is thread-safe. unsafe impl Sync for RpcSession {} impl RpcSession { /// Allocates a new RpcSession object. pub fn new() -> RpcSession { - // SAFETY - Takes ownership of the returned handle, which has correct refcount. + // SAFETY: Takes ownership of the returned handle, which has correct refcount. unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) } } } @@ -58,7 +55,7 @@ impl Default for RpcSession { impl RpcSessionRef { /// Sets the file descriptor transport mode for this session. pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) { - // SAFETY - Only passes the 'self' pointer as an opaque handle. + // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode( self.as_ptr(), @@ -69,7 +66,7 @@ impl RpcSessionRef { /// Sets the maximum number of incoming threads. pub fn set_max_incoming_threads(&self, threads: usize) { - // SAFETY - Only passes the 'self' pointer as an opaque handle. + // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads) }; @@ -77,7 +74,7 @@ impl RpcSessionRef { /// Sets the maximum number of outgoing connections. pub fn set_max_outgoing_connections(&self, connections: usize) { - // SAFETY - Only passes the 'self' pointer as an opaque handle. + // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections( self.as_ptr(), @@ -87,6 +84,7 @@ impl RpcSessionRef { } /// Connects to an RPC Binder server over vsock for a particular interface. + #[cfg(not(target_os = "trusty"))] pub fn setup_vsock_client( &self, cid: u32, @@ -106,11 +104,12 @@ impl RpcSessionRef { /// Connects to an RPC Binder server over a names Unix Domain Socket for /// a particular interface. + #[cfg(not(target_os = "trusty"))] pub fn setup_unix_domain_client( &self, socket_name: &str, ) -> Result, StatusCode> { - let socket_name = match CString::new(socket_name) { + let socket_name = match std::ffi::CString::new(socket_name) { Ok(s) => s, Err(e) => { log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); @@ -131,10 +130,12 @@ impl RpcSessionRef { /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket /// for a particular interface. + #[cfg(not(target_os = "trusty"))] pub fn setup_unix_domain_bootstrap_client( &self, - bootstrap_fd: BorrowedFd, + bootstrap_fd: std::os::fd::BorrowedFd, ) -> Result, StatusCode> { + use std::os::fd::AsRawFd; // SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take // ownership of bootstrap_fd. The returned AIBinder has correct // reference count, and the ownership can safely be taken by new_spibinder. @@ -148,12 +149,13 @@ impl RpcSessionRef { } /// Connects to an RPC Binder server over inet socket at the given address and port. + #[cfg(not(target_os = "trusty"))] pub fn setup_inet_client( &self, address: &str, port: u32, ) -> Result, StatusCode> { - let address = match CString::new(address) { + let address = match std::ffi::CString::new(address) { Ok(s) => s, Err(e) => { log::error!("Cannot convert {} to CString. Error: {:?}", address, e); @@ -173,6 +175,22 @@ impl RpcSessionRef { Self::get_interface(service) } + #[cfg(target_os = "trusty")] + pub fn setup_trusty_client( + &self, + port: &std::ffi::CStr, + ) -> Result, StatusCode> { + self.setup_preconnected_client(|| { + let h = tipc::Handle::connect(port) + .expect("Failed to connect to service port {SERVICE_PORT}"); + + // Do not close the handle at the end of the scope + let fd = h.as_raw_fd(); + core::mem::forget(h); + Some(fd) + }) + } + /// Connects to an RPC Binder server, using the given callback to get (and /// take ownership of) file descriptors already connected to it. pub fn setup_preconnected_client( @@ -210,10 +228,10 @@ impl RpcSessionRef { type RequestFd<'a> = &'a mut dyn FnMut() -> Option; unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { + let request_fd_ptr = param as *mut RequestFd; // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the // BinderFdFactory reference, with param being a properly aligned non-null pointer to an // initialized instance. - let request_fd_ptr = param as *mut RequestFd; - let request_fd = request_fd_ptr.as_mut().unwrap(); + let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() }; request_fd().unwrap_or(-1) } diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index d0e35de3f76c20f423ede2c3c5a74d4622a9f76b..e34d31e58f8ffe8609dd58f48f9d09fe7e9a268d 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -21,16 +21,17 @@ use crate::parcel::{BorrowedParcel, Parcel}; use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; +use downcast_rs::{impl_downcast, DowncastSync}; use std::borrow::Borrow; use std::cmp::Ordering; use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; use std::fmt; -use std::fs::File; +use std::io::Write; use std::marker::PhantomData; use std::ops::Deref; +use std::os::fd::AsRawFd; use std::os::raw::c_char; -use std::os::unix::io::AsRawFd; use std::ptr; /// Binder action to perform. @@ -51,7 +52,7 @@ pub type TransactionFlags = u32; /// interfaces) must implement this trait. /// /// This is equivalent `IInterface` in C++. -pub trait Interface: Send + Sync { +pub trait Interface: Send + Sync + DowncastSync { /// Convert this binder object into a generic [`SpIBinder`] reference. fn as_binder(&self) -> SpIBinder { panic!("This object was not a Binder object and cannot be converted into an SpIBinder.") @@ -61,11 +62,13 @@ pub trait Interface: Send + Sync { /// /// This handler is a no-op by default and should be implemented for each /// Binder service struct that wishes to respond to dump transactions. - fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { + fn dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> { Ok(()) } } +impl_downcast!(sync Interface); + /// Implemented by sync interfaces to specify what the associated async interface is. /// Generic to handle the fact that async interfaces are generic over a thread pool. /// @@ -97,8 +100,8 @@ where /// Interface stability promise /// -/// An interface can promise to be a stable vendor interface ([`Vintf`]), or -/// makes no stability guarantees ([`Local`]). [`Local`] is +/// An interface can promise to be a stable vendor interface ([`Stability::Vintf`]), +/// or makes no stability guarantees ([`Stability::Local`]). [`Stability::Local`] is /// currently the default stability. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum Stability { @@ -139,11 +142,11 @@ impl TryFrom for Stability { /// via `Binder::new(object)`. /// /// This is a low-level interface that should normally be automatically -/// generated from AIDL via the [`declare_binder_interface!`] macro. When using -/// the AIDL backend, users need only implement the high-level AIDL-defined +/// generated from AIDL via the [`crate::declare_binder_interface!`] macro. +/// When using the AIDL backend, users need only implement the high-level AIDL-defined /// interface. The AIDL compiler then generates a container struct that wraps /// the user-defined service and implements `Remotable`. -pub trait Remotable: Send + Sync { +pub trait Remotable: Send + Sync + 'static { /// The Binder interface descriptor string. /// /// This string is a unique identifier for a Binder interface, and should be @@ -162,7 +165,7 @@ pub trait Remotable: Send + Sync { /// Handle a request to invoke the dump transaction on this /// object. - fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>; + fn on_dump(&self, file: &mut dyn Write, args: &[&CStr]) -> Result<()>; /// Retrieve the class of this remote object. /// @@ -260,7 +263,14 @@ pub trait IBinder { /// Trying to use this function on a local binder will result in an /// INVALID_OPERATION code being returned and nothing happening. /// - /// This link always holds a weak reference to its recipient. + /// This link only holds a weak reference to its recipient. If the + /// `DeathRecipient` is dropped then it will be unlinked. + /// + /// Note that the notifications won't work if you don't first start at least + /// one Binder thread by calling + /// [`ProcessState::start_thread_pool`](crate::ProcessState::start_thread_pool) + /// or + /// [`ProcessState::join_thread_pool`](crate::ProcessState::join_thread_pool). fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>; /// Remove a previously registered death notification. @@ -290,18 +300,17 @@ impl InterfaceClass { /// Note: the returned pointer will not be constant. Calling this method /// multiple times for the same type will result in distinct class /// pointers. A static getter for this value is implemented in - /// [`declare_binder_interface!`]. + /// [`crate::declare_binder_interface!`]. pub fn new() -> InterfaceClass { let descriptor = CString::new(I::get_descriptor()).unwrap(); + // Safety: `AIBinder_Class_define` expects a valid C string, and three + // valid callback functions, all non-null pointers. The C string is + // copied and need not be valid for longer than the call, so we can drop + // it after the call. We can safely assign null to the onDump and + // handleShellCommand callbacks as long as the class pointer was + // non-null. Rust None for a Option is guaranteed to be a NULL + // pointer. Rust retains ownership of the pointer after it is defined. let ptr = unsafe { - // Safety: `AIBinder_Class_define` expects a valid C string, and - // three valid callback functions, all non-null pointers. The C - // string is copied and need not be valid for longer than the call, - // so we can drop it after the call. We can safely assign null to - // the onDump and handleShellCommand callbacks as long as the class - // pointer was non-null. Rust None for a Option is guaranteed to - // be a NULL pointer. Rust retains ownership of the pointer after it - // is defined. let class = sys::AIBinder_Class_define( descriptor.as_ptr(), Some(I::on_create), @@ -331,13 +340,12 @@ impl InterfaceClass { /// Get the interface descriptor string of this class. pub fn get_descriptor(&self) -> String { + // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor is + // always a two-byte null terminated sequence of u16s. Thus, we can + // continue reading from the pointer until we hit a null value, and this + // pointer can be a valid slice if the slice length is <= the number of + // u16 elements before the null terminator. unsafe { - // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor - // is always a two-byte null terminated sequence of u16s. Thus, we - // can continue reading from the pointer until we hit a null value, - // and this pointer can be a valid slice if the slice length is <= - // the number of u16 elements before the null terminator. - let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0); CStr::from_ptr(raw_descriptor) .to_str() @@ -431,7 +439,7 @@ impl Ord for Strong { impl PartialOrd for Strong { fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_binder().partial_cmp(&other.0.as_binder()) + Some(self.cmp(other)) } } @@ -478,7 +486,7 @@ impl Ord for Weak { impl PartialOrd for Weak { fn partial_cmp(&self, other: &Self) -> Option { - self.weak_binder.partial_cmp(&other.weak_binder) + Some(self.cmp(other)) } } @@ -535,17 +543,15 @@ macro_rules! binder_fn_get_class { static CLASS_INIT: std::sync::Once = std::sync::Once::new(); static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None; + // Safety: This assignment is guarded by the `CLASS_INIT` `Once` + // variable, and therefore is thread-safe, as it can only occur + // once. CLASS_INIT.call_once(|| unsafe { - // Safety: This assignment is guarded by the `CLASS_INIT` `Once` - // variable, and therefore is thread-safe, as it can only occur - // once. CLASS = Some($constructor); }); - unsafe { - // Safety: The `CLASS` variable can only be mutated once, above, - // and is subsequently safe to read from any thread. - CLASS.unwrap() - } + // Safety: The `CLASS` variable can only be mutated once, above, and + // is subsequently safe to read from any thread. + unsafe { CLASS.unwrap() } } }; } @@ -657,6 +663,8 @@ pub unsafe trait AsNative { fn as_native_mut(&mut self) -> *mut T; } +// Safety: If V is a valid Android C++ type then we can either use that or a +// null pointer. unsafe impl> AsNative for Option { fn as_native(&self) -> *const T { self.as_ref().map_or(ptr::null(), |v| v.as_native()) @@ -888,6 +896,23 @@ macro_rules! declare_binder_interface { $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid); $crate::Strong::new(Box::new(binder)) } + + /// Tries to downcast the interface to another type. + /// When receiving this object from a binder call, make sure that the object received is + /// a binder native object and that is of the right type for the Downcast: + /// + /// let binder = received_object.as_binder(); + /// if !binder.is_remote() { + /// let binder_native: Binder = binder.try_into()?; + /// let original_object = binder_native.downcast_binder::(); + /// // Check that returned type is not None before using it + /// } + /// + /// Handle the error cases instead of just calling `unwrap` or `expect` to prevent a + /// malicious caller to mount a Denial of Service attack. + pub fn downcast_binder(&self) -> Option<&T> { + self.0.as_any().downcast_ref::() + } } impl $crate::binder_impl::Remotable for $native { @@ -909,23 +934,23 @@ macro_rules! declare_binder_interface { } } - fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> { - self.0.dump(file, args) + fn on_dump(&self, writer: &mut dyn std::io::Write, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> { + self.0.dump(writer, args) } fn get_class() -> $crate::binder_impl::InterfaceClass { static CLASS_INIT: std::sync::Once = std::sync::Once::new(); static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None; + // Safety: This assignment is guarded by the `CLASS_INIT` `Once` + // variable, and therefore is thread-safe, as it can only occur + // once. CLASS_INIT.call_once(|| unsafe { - // Safety: This assignment is guarded by the `CLASS_INIT` `Once` - // variable, and therefore is thread-safe, as it can only occur - // once. CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>()); }); + // Safety: The `CLASS` variable can only be mutated once, above, + // and is subsequently safe to read from any thread. unsafe { - // Safety: The `CLASS` variable can only be mutated once, above, - // and is subsequently safe to read from any thread. CLASS.unwrap() } } @@ -999,7 +1024,7 @@ macro_rules! declare_binder_interface { $( // Async interface trait implementations. - impl $crate::FromIBinder for dyn $async_interface

      { + impl $crate::FromIBinder for dyn $async_interface

      { fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong>, $crate::StatusCode> { use $crate::binder_impl::AssociateClass; @@ -1018,44 +1043,34 @@ macro_rules! declare_binder_interface { } if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) { - let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> = - std::convert::TryFrom::try_from(ibinder.clone()); - if let Ok(service) = service { - // We were able to associate with our expected class and - // the service is local. - todo!() - //return Ok($crate::Strong::new(Box::new(service))); - } else { - // Service is remote - return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?))); - } + return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?))); } Err($crate::StatusCode::BAD_TYPE.into()) } } - impl $crate::binder_impl::Serialize for dyn $async_interface

      + '_ { + impl $crate::binder_impl::Serialize for dyn $async_interface

      + '_ { fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> { let binder = $crate::Interface::as_binder(self); parcel.write(&binder) } } - impl $crate::binder_impl::SerializeOption for dyn $async_interface

      + '_ { + impl $crate::binder_impl::SerializeOption for dyn $async_interface

      + '_ { fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> { parcel.write(&this.map($crate::Interface::as_binder)) } } - impl std::fmt::Debug for dyn $async_interface

      + '_ { + impl std::fmt::Debug for dyn $async_interface

      + '_ { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.pad(stringify!($async_interface)) } } /// Convert a &dyn $async_interface to Strong - impl std::borrow::ToOwned for dyn $async_interface

      { + impl std::borrow::ToOwned for dyn $async_interface

      { type Owned = $crate::Strong>; fn to_owned(&self) -> Self::Owned { self.as_binder().into_interface() @@ -1063,11 +1078,11 @@ macro_rules! declare_binder_interface { } } - impl $crate::binder_impl::ToAsyncInterface

      for dyn $interface { + impl $crate::binder_impl::ToAsyncInterface

      for dyn $interface { type Target = dyn $async_interface

      ; } - impl $crate::binder_impl::ToSyncInterface for dyn $async_interface

      { + impl $crate::binder_impl::ToSyncInterface for dyn $async_interface

      { type Target = dyn $interface; } )? @@ -1122,6 +1137,10 @@ macro_rules! declare_binder_enum { } impl $crate::binder_impl::Deserialize for $enum { + type UninitType = Self; + fn uninit() -> Self::UninitType { Self::UninitType::default() } + fn from_init(value: Self) -> Self::UninitType { value } + fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result { parcel.read().map(Self) } diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs index f6b09ed8fe1875985f8af72af18bb36c2ad6c418..eb04cc31534834ec2316baeb5ecca0738d747c39 100644 --- a/libs/binder/rust/src/error.rs +++ b/libs/binder/rust/src/error.rs @@ -20,6 +20,7 @@ use crate::sys; use std::error; use std::ffi::{CStr, CString}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr; use std::result; pub use sys::binder_status_t as status_t; @@ -92,7 +93,7 @@ fn parse_exception_code(code: i32) -> ExceptionCode { /// track of and chain binder errors along with service specific errors. /// /// Used in AIDL transactions to represent failed transactions. -pub struct Status(*mut sys::AStatus); +pub struct Status(ptr::NonNull); // Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the // duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status` @@ -111,43 +112,37 @@ fn to_cstring>(message: T) -> Option { impl Status { /// Create a status object representing a successful transaction. pub fn ok() -> Self { - let ptr = unsafe { - // Safety: `AStatus_newOk` always returns a new, heap allocated - // pointer to an `ASTatus` object, so we know this pointer will be - // valid. - // - // Rust takes ownership of the returned pointer. - sys::AStatus_newOk() - }; - Self(ptr) + // Safety: `AStatus_newOk` always returns a new, heap allocated + // pointer to an `ASTatus` object, so we know this pointer will be + // valid. + // + // Rust takes ownership of the returned pointer. + let ptr = unsafe { sys::AStatus_newOk() }; + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } /// Create a status object from a service specific error pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status { let ptr = if let Some(message) = message { - unsafe { - // Safety: Any i32 is a valid service specific error for the - // error code parameter. We construct a valid, null-terminated - // `CString` from the message, which must be a valid C-style - // string to pass as the message. This function always returns a - // new, heap allocated pointer to an `AStatus` object, so we - // know the returned pointer will be valid. - // - // Rust takes ownership of the returned pointer. - sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) - } + // Safety: Any i32 is a valid service specific error for the + // error code parameter. We construct a valid, null-terminated + // `CString` from the message, which must be a valid C-style + // string to pass as the message. This function always returns a + // new, heap allocated pointer to an `AStatus` object, so we + // know the returned pointer will be valid. + // + // Rust takes ownership of the returned pointer. + unsafe { sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) } } else { - unsafe { - // Safety: Any i32 is a valid service specific error for the - // error code parameter. This function always returns a new, - // heap allocated pointer to an `AStatus` object, so we know the - // returned pointer will be valid. - // - // Rust takes ownership of the returned pointer. - sys::AStatus_fromServiceSpecificError(err) - } + // Safety: Any i32 is a valid service specific error for the + // error code parameter. This function always returns a new, + // heap allocated pointer to an `AStatus` object, so we know the + // returned pointer will be valid. + // + // Rust takes ownership of the returned pointer. + unsafe { sys::AStatus_fromServiceSpecificError(err) } }; - Self(ptr) + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } /// Creates a status object from a service specific error. @@ -158,10 +153,12 @@ impl Status { /// Create a status object from an exception code pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status { if let Some(message) = message { + // Safety: the C string pointer is valid and not retained by the + // function. let ptr = unsafe { sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr()) }; - Self(ptr) + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } else { exception.into() } @@ -181,42 +178,36 @@ impl Status { /// /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`. pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self { - Self(ptr) + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } /// Returns `true` if this status represents a successful transaction. pub fn is_ok(&self) -> bool { - unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_isOk` here. - sys::AStatus_isOk(self.as_native()) - } + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_isOk` here. + unsafe { sys::AStatus_isOk(self.as_native()) } } /// Returns a description of the status. pub fn get_description(&self) -> String { - let description_ptr = unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_getDescription` - // here. - // - // `AStatus_getDescription` always returns a valid pointer to a null - // terminated C string. Rust is responsible for freeing this pointer - // via `AStatus_deleteDescription`. - sys::AStatus_getDescription(self.as_native()) - }; - let description = unsafe { - // Safety: `AStatus_getDescription` always returns a valid C string, - // which can be safely converted to a `CStr`. - CStr::from_ptr(description_ptr) - }; + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getDescription` + // here. + // + // `AStatus_getDescription` always returns a valid pointer to a null + // terminated C string. Rust is responsible for freeing this pointer + // via `AStatus_deleteDescription`. + let description_ptr = unsafe { sys::AStatus_getDescription(self.as_native()) }; + // Safety: `AStatus_getDescription` always returns a valid C string, + // which can be safely converted to a `CStr`. + let description = unsafe { CStr::from_ptr(description_ptr) }; let description = description.to_string_lossy().to_string(); + // Safety: `description_ptr` was returned from + // `AStatus_getDescription` above, and must be freed via + // `AStatus_deleteDescription`. We must not access the pointer after + // this call, so we copy it into an owned string above and return + // that string. unsafe { - // Safety: `description_ptr` was returned from - // `AStatus_getDescription` above, and must be freed via - // `AStatus_deleteDescription`. We must not access the pointer after - // this call, so we copy it into an owned string above and return - // that string. sys::AStatus_deleteDescription(description_ptr); } description @@ -224,12 +215,10 @@ impl Status { /// Returns the exception code of the status. pub fn exception_code(&self) -> ExceptionCode { - let code = unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_getExceptionCode` - // here. - sys::AStatus_getExceptionCode(self.as_native()) - }; + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getExceptionCode` + // here. + let code = unsafe { sys::AStatus_getExceptionCode(self.as_native()) }; parse_exception_code(code) } @@ -240,11 +229,9 @@ impl Status { /// exception or a service specific error. To find out if this transaction /// as a whole is okay, use [`is_ok`](Self::is_ok) instead. pub fn transaction_error(&self) -> StatusCode { - let code = unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_getStatus` here. - sys::AStatus_getStatus(self.as_native()) - }; + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getStatus` here. + let code = unsafe { sys::AStatus_getStatus(self.as_native()) }; parse_status_code(code) } @@ -257,12 +244,10 @@ impl Status { /// find out if this transaction as a whole is okay, use /// [`is_ok`](Self::is_ok) instead. pub fn service_specific_error(&self) -> i32 { - unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to - // `AStatus_getServiceSpecificError` here. - sys::AStatus_getServiceSpecificError(self.as_native()) - } + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to + // `AStatus_getServiceSpecificError` here. + unsafe { sys::AStatus_getServiceSpecificError(self.as_native()) } } /// Calls `op` if the status was ok, otherwise returns an `Err` value of @@ -320,25 +305,21 @@ impl From for Status { impl From for Status { fn from(status: status_t) -> Status { - let ptr = unsafe { - // Safety: `AStatus_fromStatus` expects any `status_t` integer, so - // this is a safe FFI call. Unknown values will be coerced into - // UNKNOWN_ERROR. - sys::AStatus_fromStatus(status) - }; - Self(ptr) + // Safety: `AStatus_fromStatus` expects any `status_t` integer, so + // this is a safe FFI call. Unknown values will be coerced into + // UNKNOWN_ERROR. + let ptr = unsafe { sys::AStatus_fromStatus(status) }; + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } } impl From for Status { fn from(code: ExceptionCode) -> Status { - let ptr = unsafe { - // Safety: `AStatus_fromExceptionCode` expects any - // `binder_exception_t` (i32) integer, so this is a safe FFI call. - // Unknown values will be coerced into EX_TRANSACTION_FAILED. - sys::AStatus_fromExceptionCode(code as i32) - }; - Self(ptr) + // Safety: `AStatus_fromExceptionCode` expects any + // `binder_exception_t` (i32) integer, so this is a safe FFI call. + // Unknown values will be coerced into EX_TRANSACTION_FAILED. + let ptr = unsafe { sys::AStatus_fromExceptionCode(code as i32) }; + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } } @@ -362,30 +343,118 @@ impl From for status_t { impl Drop for Status { fn drop(&mut self) { + // Safety: `Status` manages the lifetime of its inner `AStatus` + // pointee, so we need to delete it here. We know that the pointer + // will be valid here since `Status` always contains a valid pointer + // while it is alive. unsafe { - // Safety: `Status` manages the lifetime of its inner `AStatus` - // pointee, so we need to delete it here. We know that the pointer - // will be valid here since `Status` always contains a valid pointer - // while it is alive. - sys::AStatus_delete(self.0); + sys::AStatus_delete(self.0.as_mut()); } } } -/// # Safety -/// -/// `Status` always contains a valid pointer to an `AStatus` object, so we can -/// trivially convert it to a correctly-typed raw pointer. +/// Safety: `Status` always contains a valid pointer to an `AStatus` object, so +/// we can trivially convert it to a correctly-typed raw pointer. /// /// Care must be taken that the returned pointer is only dereferenced while the /// `Status` object is still alive. unsafe impl AsNative for Status { fn as_native(&self) -> *const sys::AStatus { - self.0 + self.0.as_ptr() } fn as_native_mut(&mut self) -> *mut sys::AStatus { - self.0 + // Safety: The pointer will be valid here since `Status` always contains + // a valid and initialized pointer while it is alive. + unsafe { self.0.as_mut() } + } +} + +/// A conversion from `std::result::Result` to `binder::Result`. If this type is `Ok(T)`, +/// it's returned as is. If this type is `Err(E)`, `E` is converted into `Status` which can be +/// either a general binder exception, or a service-specific exception. +/// +/// # Examples +/// +/// ``` +/// // std::io::Error is formatted as the exception's message +/// fn file_exists(name: &str) -> binder::Result { +/// std::fs::metadata(name) +/// .or_service_specific_exception(NOT_FOUND)? +/// } +/// +/// // A custom function is used to create the exception's message +/// fn file_exists(name: &str) -> binder::Result { +/// std::fs::metadata(name) +/// .or_service_specific_exception_with(NOT_FOUND, +/// |e| format!("file {} not found: {:?}", name, e))? +/// } +/// +/// // anyhow::Error is formatted as the exception's message +/// use anyhow::{Context, Result}; +/// fn file_exists(name: &str) -> binder::Result { +/// std::fs::metadata(name) +/// .context("file {} not found") +/// .or_service_specific_exception(NOT_FOUND)? +/// } +/// +/// // General binder exceptions can be created similarly +/// fn file_exists(name: &str) -> binder::Result { +/// std::fs::metadata(name) +/// .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)? +/// } +/// ``` +pub trait IntoBinderResult { + /// Converts the embedded error into a general binder exception of code `exception`. The + /// message of the exception is set by formatting the error for debugging. + fn or_binder_exception(self, exception: ExceptionCode) -> result::Result; + + /// Converts the embedded error into a general binder exception of code `exception`. The + /// message of the exception is set by lazily evaluating the `op` function. + fn or_binder_exception_with, O: FnOnce(E) -> M>( + self, + exception: ExceptionCode, + op: O, + ) -> result::Result; + + /// Converts the embedded error into a service-specific binder exception. `error_code` is used + /// to distinguish different service-specific binder exceptions. The message of the exception + /// is set by formatting the error for debugging. + fn or_service_specific_exception(self, error_code: i32) -> result::Result; + + /// Converts the embedded error into a service-specific binder exception. `error_code` is used + /// to distinguish different service-specific binder exceptions. The message of the exception + /// is set by lazily evaluating the `op` function. + fn or_service_specific_exception_with, O: FnOnce(E) -> M>( + self, + error_code: i32, + op: O, + ) -> result::Result; +} + +impl IntoBinderResult for result::Result { + fn or_binder_exception(self, exception: ExceptionCode) -> result::Result { + self.or_binder_exception_with(exception, |e| format!("{:?}", e)) + } + + fn or_binder_exception_with, O: FnOnce(E) -> M>( + self, + exception: ExceptionCode, + op: O, + ) -> result::Result { + self.map_err(|e| Status::new_exception_str(exception, Some(op(e)))) + } + + fn or_service_specific_exception(self, error_code: i32) -> result::Result { + self.or_service_specific_exception_with(error_code, |e| format!("{:?}", e)) + } + + fn or_service_specific_exception_with, O: FnOnce(E) -> M>( + self, + error_code: i32, + op: O, + ) -> result::Result { + self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e)))) } } @@ -425,4 +494,66 @@ mod tests { assert_eq!(status.service_specific_error(), 0); assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string()); } + + #[test] + fn convert_to_service_specific_exception() { + let res: std::result::Result<(), Status> = + Err("message").or_service_specific_exception(-42); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string() + ); + } + + #[test] + fn convert_to_service_specific_exception_with() { + let res: std::result::Result<(), Status> = Err("message") + .or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e)); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string() + ); + } + + #[test] + fn convert_to_binder_exception() { + let res: std::result::Result<(), Status> = + Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!( + status.get_description(), + "Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string() + ); + } + + #[test] + fn convert_to_binder_exception_with() { + let res: std::result::Result<(), Status> = Err("message") + .or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| { + format!("outer message: {:?}", e) + }); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!( + status.get_description(), + "Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string() + ); + } } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 0c8b48f1f522714758ec4f9f4e40a63a8314b5d4..7f9348d9139378954327c1c650d3b36a0532f997 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -100,13 +100,14 @@ mod error; mod native; mod parcel; mod proxy; +#[cfg(not(target_os = "trusty"))] mod state; use binder_ndk_sys as sys; pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; -pub use error::{ExceptionCode, Status, StatusCode}; +pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; pub use native::{ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, LazyServiceGuard, @@ -116,6 +117,7 @@ pub use proxy::{ get_declared_instances, get_interface, get_service, is_declared, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder, WpIBinder, }; +#[cfg(not(target_os = "trusty"))] pub use state::{ProcessState, ThreadState}; /// Binder result containing a [`Status`] on error. @@ -144,6 +146,7 @@ pub mod binder_impl { #[doc(hidden)] pub mod unstable_api { pub use crate::binder::AsNative; + pub use crate::error::status_result; pub use crate::proxy::unstable_api::new_spibinder; pub use crate::sys::AIBinder; pub use crate::sys::AParcel; diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index 5557168055fe0870c45cbc807443980dee241ecc..8ae010ea8805b9b5fd22e6e0ad3ac3d68c4dd16f 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -24,12 +24,10 @@ use crate::sys; use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; -use std::fs::File; +use std::io::Write; use std::mem::ManuallyDrop; use std::ops::Deref; use std::os::raw::c_char; -use std::os::unix::io::FromRawFd; -use std::slice; use std::sync::Mutex; /// Rust wrapper around Binder remotable objects. @@ -42,7 +40,7 @@ pub struct Binder { rust_object: *mut T, } -/// # Safety +/// Safety: /// /// A `Binder` is a pair of unique owning pointers to two values: /// * a C++ ABBinder which the C++ API guarantees can be passed between threads @@ -54,7 +52,7 @@ pub struct Binder { /// to how `Box` is `Send` if `T` is `Send`. unsafe impl Send for Binder {} -/// # Safety +/// Safety: /// /// A `Binder` is a pair of unique owning pointers to two values: /// * a C++ ABBinder which is thread-safe, i.e. `Send + Sync` @@ -89,15 +87,13 @@ impl Binder { pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder { let class = T::get_class(); let rust_object = Box::into_raw(Box::new(rust_object)); - let ibinder = unsafe { - // Safety: `AIBinder_new` expects a valid class pointer (which we - // initialize via `get_class`), and an arbitrary pointer - // argument. The caller owns the returned `AIBinder` pointer, which - // is a strong reference to a `BBinder`. This reference should be - // decremented via `AIBinder_decStrong` when the reference lifetime - // ends. - sys::AIBinder_new(class.into(), rust_object as *mut c_void) - }; + // Safety: `AIBinder_new` expects a valid class pointer (which we + // initialize via `get_class`), and an arbitrary pointer + // argument. The caller owns the returned `AIBinder` pointer, which + // is a strong reference to a `BBinder`. This reference should be + // decremented via `AIBinder_decStrong` when the reference lifetime + // ends. + let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) }; let mut binder = Binder { ibinder, rust_object }; binder.mark_stability(stability); binder @@ -176,15 +172,14 @@ impl Binder { /// } /// # } pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> { - let status = unsafe { - // Safety: `AIBinder_setExtension` expects two valid, mutable - // `AIBinder` pointers. We are guaranteed that both `self` and - // `extension` contain valid `AIBinder` pointers, because they - // cannot be initialized without a valid - // pointer. `AIBinder_setExtension` does not take ownership of - // either parameter. - sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) - }; + let status = + // Safety: `AIBinder_setExtension` expects two valid, mutable + // `AIBinder` pointers. We are guaranteed that both `self` and + // `extension` contain valid `AIBinder` pointers, because they + // cannot be initialized without a valid + // pointer. `AIBinder_setExtension` does not take ownership of + // either parameter. + unsafe { sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) }; status_result(status) } @@ -199,9 +194,9 @@ impl Binder { match stability { Stability::Local => self.mark_local_stability(), Stability::Vintf => { + // Safety: Self always contains a valid `AIBinder` pointer, so + // we can always call this C API safely. unsafe { - // Safety: Self always contains a valid `AIBinder` pointer, so - // we can always call this C API safely. sys::AIBinder_markVintfStability(self.as_native_mut()); } } @@ -212,9 +207,9 @@ impl Binder { /// building for android_vendor and system otherwise. #[cfg(android_vendor)] fn mark_local_stability(&mut self) { + // Safety: Self always contains a valid `AIBinder` pointer, so we can + // always call this C API safely. unsafe { - // Safety: Self always contains a valid `AIBinder` pointer, so - // we can always call this C API safely. sys::AIBinder_markVendorStability(self.as_native_mut()); } } @@ -223,9 +218,9 @@ impl Binder { /// building for android_vendor and system otherwise. #[cfg(not(android_vendor))] fn mark_local_stability(&mut self) { + // Safety: Self always contains a valid `AIBinder` pointer, so we can + // always call this C API safely. unsafe { - // Safety: Self always contains a valid `AIBinder` pointer, so - // we can always call this C API safely. sys::AIBinder_markSystemStability(self.as_native_mut()); } } @@ -239,13 +234,13 @@ impl Interface for Binder { /// remotable object, which will prevent the object from being dropped while /// the `SpIBinder` is alive. fn as_binder(&self) -> SpIBinder { + // Safety: `self.ibinder` is guaranteed to always be a valid pointer + // to an `AIBinder` by the `Binder` constructor. We are creating a + // copy of the `self.ibinder` strong reference, but + // `SpIBinder::from_raw` assumes it receives an owned pointer with + // its own strong reference. We first increment the reference count, + // so that the new `SpIBinder` will be tracked as a new reference. unsafe { - // Safety: `self.ibinder` is guaranteed to always be a valid pointer - // to an `AIBinder` by the `Binder` constructor. We are creating a - // copy of the `self.ibinder` strong reference, but - // `SpIBinder::from_raw` assumes it receives an owned pointer with - // its own strong reference. We first increment the reference count, - // so that the new `SpIBinder` will be tracked as a new reference. sys::AIBinder_incStrong(self.ibinder); SpIBinder::from_raw(self.ibinder).unwrap() } @@ -275,10 +270,20 @@ impl InterfaceClassMethods for Binder { reply: *mut sys::AParcel, ) -> status_t { let res = { - let mut reply = BorrowedParcel::from_raw(reply).unwrap(); - let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap(); - let object = sys::AIBinder_getUserData(binder); - let binder: &T = &*(object as *const T); + // Safety: The caller must give us a parcel pointer which is either + // null or valid at least for the duration of this function call. We + // don't keep the resulting value beyond the function. + let mut reply = unsafe { BorrowedParcel::from_raw(reply).unwrap() }; + // Safety: The caller must give us a parcel pointer which is either + // null or valid at least for the duration of this function call. We + // don't keep the resulting value beyond the function. + let data = unsafe { BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap() }; + // Safety: Our caller promised that `binder` is a non-null, valid + // pointer to a local `AIBinder`. + let object = unsafe { sys::AIBinder_getUserData(binder) }; + // Safety: Our caller promised that the binder has a `T` pointer in + // its user data. + let binder: &T = unsafe { &*(object as *const T) }; binder.on_transact(code, &data, &mut reply) }; match res { @@ -295,7 +300,9 @@ impl InterfaceClassMethods for Binder { /// Must be called with a valid pointer to a `T` object. After this call, /// the pointer will be invalid and should not be dereferenced. unsafe extern "C" fn on_destroy(object: *mut c_void) { - drop(Box::from_raw(object as *mut T)); + // Safety: Our caller promised that `object` is a valid pointer to a + // `T`. + drop(unsafe { Box::from_raw(object as *mut T) }); } /// Called whenever a new, local `AIBinder` object is needed of a specific @@ -320,7 +327,8 @@ impl InterfaceClassMethods for Binder { /// Must be called with a non-null, valid pointer to a local `AIBinder` that /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string - /// poiinters with length num_args. + /// pointers with length num_args. + #[cfg(not(target_os = "trusty"))] unsafe extern "C" fn on_dump( binder: *mut sys::AIBinder, fd: i32, @@ -330,8 +338,10 @@ impl InterfaceClassMethods for Binder { if fd < 0 { return StatusCode::UNEXPECTED_NULL as status_t; } - // 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)); + use std::os::fd::FromRawFd; + // Safety: Our caller promised that fd is a file descriptor. We don't + // own this file descriptor, so we need to be careful not to drop it. + let mut file = unsafe { ManuallyDrop::new(std::fs::File::from_raw_fd(fd)) }; if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; @@ -340,21 +350,42 @@ impl InterfaceClassMethods for Binder { 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() + // Safety: Our caller promised that `args` is an array of + // null-terminated string pointers with length `num_args`. + unsafe { + std::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); - let res = binder.on_dump(&file, &args); + // Safety: Our caller promised that `binder` is a non-null, valid + // pointer to a local `AIBinder`. + let object = unsafe { sys::AIBinder_getUserData(binder) }; + // Safety: Our caller promised that the binder has a `T` pointer in its + // user data. + let binder: &T = unsafe { &*(object as *const T) }; + let res = binder.on_dump(&mut *file, &args); match res { Ok(()) => 0, Err(e) => e as status_t, } } + + /// Called to handle the `dump` transaction. + #[cfg(target_os = "trusty")] + unsafe extern "C" fn on_dump( + _binder: *mut sys::AIBinder, + _fd: i32, + _args: *mut *const c_char, + _num_args: u32, + ) -> status_t { + // This operation is not supported on Trusty right now + // because we do not have a uniform way of writing to handles + StatusCode::INVALID_OPERATION as status_t + } } impl Drop for Binder { @@ -363,11 +394,11 @@ impl Drop for Binder { // actually destroys the object, it calls `on_destroy` and we can drop the // `rust_object` then. fn drop(&mut self) { + // Safety: When `self` is dropped, we can no longer access the + // reference, so can decrement the reference count. `self.ibinder` is + // always a valid `AIBinder` pointer, so is valid to pass to + // `AIBinder_decStrong`. unsafe { - // Safety: When `self` is dropped, we can no longer access the - // reference, so can decrement the reference count. `self.ibinder` - // is always a valid `AIBinder` pointer, so is valid to pass to - // `AIBinder_decStrong`. sys::AIBinder_decStrong(self.ibinder); } } @@ -377,14 +408,11 @@ impl Deref for Binder { type Target = T; fn deref(&self) -> &Self::Target { - unsafe { - // Safety: While `self` is alive, the reference count of the - // underlying object is > 0 and therefore `on_destroy` cannot be - // called. Therefore while `self` is alive, we know that - // `rust_object` is still a valid pointer to a heap allocated object - // of type `T`. - &*self.rust_object - } + // Safety: While `self` is alive, the reference count of the underlying + // object is > 0 and therefore `on_destroy` cannot be called. Therefore + // while `self` is alive, we know that `rust_object` is still a valid + // pointer to a heap allocated object of type `T`. + unsafe { &*self.rust_object } } } @@ -405,13 +433,10 @@ impl TryFrom for Binder { if Some(class) != ibinder.get_class() { return Err(StatusCode::BAD_TYPE); } - let userdata = unsafe { - // Safety: `SpIBinder` always holds a valid pointer pointer to an - // `AIBinder`, which we can safely pass to - // `AIBinder_getUserData`. `ibinder` retains ownership of the - // returned pointer. - sys::AIBinder_getUserData(ibinder.as_native_mut()) - }; + // Safety: `SpIBinder` always holds a valid pointer pointer to an + // `AIBinder`, which we can safely pass to `AIBinder_getUserData`. + // `ibinder` retains ownership of the returned pointer. + let userdata = unsafe { sys::AIBinder_getUserData(ibinder.as_native_mut()) }; if userdata.is_null() { return Err(StatusCode::UNEXPECTED_NULL); } @@ -422,12 +447,10 @@ impl TryFrom for Binder { } } -/// # Safety -/// -/// The constructor for `Binder` guarantees that `self.ibinder` will contain a -/// valid, non-null pointer to an `AIBinder`, so this implementation is type -/// safe. `self.ibinder` will remain valid for the entire lifetime of `self` -/// because we hold a strong reference to the `AIBinder` until `self` is +/// Safety: The constructor for `Binder` guarantees that `self.ibinder` will +/// contain a valid, non-null pointer to an `AIBinder`, so this implementation +/// is type safe. `self.ibinder` will remain valid for the entire lifetime of +/// `self` because we hold a strong reference to the `AIBinder` until `self` is /// dropped. unsafe impl AsNative for Binder { fn as_native(&self) -> *const sys::AIBinder { @@ -447,14 +470,12 @@ unsafe impl AsNative for Binder { /// This function will panic if the identifier contains a 0 byte (NUL). pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { let instance = CString::new(identifier).unwrap(); - let status = unsafe { - // Safety: `AServiceManager_addService` expects valid `AIBinder` and C - // string pointers. Caller retains ownership of both - // pointers. `AServiceManager_addService` creates a new strong reference - // and copies the string, so both pointers need only be valid until the - // call returns. - sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) - }; + let status = + // Safety: `AServiceManager_addService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both pointers. + // `AServiceManager_addService` creates a new strong reference and copies + // the string, so both pointers need only be valid until the call returns. + unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) }; status_result(status) } @@ -470,13 +491,12 @@ pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { /// This function will panic if the identifier contains a 0 byte (NUL). pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { let instance = CString::new(identifier).unwrap(); + // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both + // pointers. `AServiceManager_registerLazyService` creates a new strong reference + // and copies the string, so both pointers need only be valid until the + // call returns. let status = unsafe { - // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C - // string pointers. Caller retains ownership of both - // pointers. `AServiceManager_registerLazyService` creates a new strong reference - // and copies the string, so both pointers need only be valid until the - // call returns. - sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr()) }; status_result(status) @@ -491,10 +511,8 @@ pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result< /// /// Consider using [`LazyServiceGuard`] rather than calling this directly. pub fn force_lazy_services_persist(persist: bool) { - unsafe { - // Safety: No borrowing or transfer of ownership occurs here. - sys::AServiceManager_forceLazyServicesPersist(persist) - } + // Safety: No borrowing or transfer of ownership occurs here. + unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) } } /// An RAII object to ensure a process which registers lazy services is not killed. During the @@ -564,7 +582,7 @@ impl Remotable for () { Ok(()) } - fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { + fn on_dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> { Ok(()) } @@ -576,8 +594,6 @@ impl Interface for () {} /// Determine whether the current thread is currently executing an incoming /// transaction. pub fn is_handling_transaction() -> bool { - unsafe { - // Safety: This method is always safe to call. - sys::AIBinder_isHandlingTransaction() - } + // Safety: This method is always safe to call. + unsafe { sys::AIBinder_isHandlingTransaction() } } diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index e4c568eab2d174f8d075a2bc67bf37b4e462cd83..f9f135d572c8cfa712afad8f889e4f87ad26e238 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -52,11 +52,12 @@ pub struct Parcel { ptr: NonNull, } -/// # Safety +/// Safety: This type guarantees that it owns the AParcel and that all access to +/// the AParcel happens through the Parcel, so it is ok to send across threads. /// -/// This type guarantees that it owns the AParcel and that all access to -/// the AParcel happens through the Parcel, so it is ok to send across -/// threads. +/// It would not be okay to implement Sync, because that would allow you to call +/// the reading methods from several threads in parallel, which would be a data +/// race on the cursor position inside the AParcel. unsafe impl Send for Parcel {} /// Container for a message (data and object references) that can be sent @@ -73,11 +74,9 @@ pub struct BorrowedParcel<'a> { impl Parcel { /// Create a new empty `Parcel`. pub fn new() -> Parcel { - let ptr = unsafe { - // Safety: If `AParcel_create` succeeds, it always returns - // a valid pointer. If it fails, the process will crash. - sys::AParcel_create() - }; + // Safety: If `AParcel_create` succeeds, it always returns + // a valid pointer. If it fails, the process will crash. + let ptr = unsafe { sys::AParcel_create() }; Self { ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") } } @@ -171,10 +170,8 @@ impl<'a> BorrowedParcel<'a> { } } -/// # Safety -/// -/// The `Parcel` constructors guarantee that a `Parcel` object will always -/// contain a valid pointer to an `AParcel`. +/// Safety: The `Parcel` constructors guarantee that a `Parcel` object will +/// always contain a valid pointer to an `AParcel`. unsafe impl AsNative for Parcel { fn as_native(&self) -> *const sys::AParcel { self.ptr.as_ptr() @@ -185,10 +182,8 @@ unsafe impl AsNative for Parcel { } } -/// # Safety -/// -/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object -/// will always contain a valid pointer to an `AParcel`. +/// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` +/// object will always contain a valid pointer to an `AParcel`. unsafe impl<'a> AsNative for BorrowedParcel<'a> { fn as_native(&self) -> *const sys::AParcel { self.ptr.as_ptr() @@ -203,10 +198,8 @@ unsafe impl<'a> AsNative for BorrowedParcel<'a> { impl<'a> BorrowedParcel<'a> { /// Data written to parcelable is zero'd before being deleted or reallocated. pub fn mark_sensitive(&mut self) { - unsafe { - // Safety: guaranteed to have a parcel object, and this method never fails - sys::AParcel_markSensitive(self.as_native()) - } + // Safety: guaranteed to have a parcel object, and this method never fails + unsafe { sys::AParcel_markSensitive(self.as_native()) } } /// Write a type that implements [`Serialize`] to the parcel. @@ -265,11 +258,15 @@ impl<'a> BorrowedParcel<'a> { f(&mut subparcel)?; } let end = self.get_data_position(); + // Safety: start is less than the current size of the parcel data + // buffer, because we just got it with `get_data_position`. unsafe { self.set_data_position(start)?; } assert!(end >= start); self.write(&(end - start))?; + // Safety: end is less than the current size of the parcel data + // buffer, because we just got it with `get_data_position`. unsafe { self.set_data_position(end)?; } @@ -278,20 +275,16 @@ impl<'a> BorrowedParcel<'a> { /// Returns the current position in the parcel data. pub fn get_data_position(&self) -> i32 { - unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`, and this call is otherwise safe. - sys::AParcel_getDataPosition(self.as_native()) - } + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. + unsafe { sys::AParcel_getDataPosition(self.as_native()) } } /// Returns the total size of the parcel. pub fn get_data_size(&self) -> i32 { - unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`, and this call is otherwise safe. - sys::AParcel_getDataSize(self.as_native()) - } + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. + unsafe { sys::AParcel_getDataSize(self.as_native()) } } /// Move the current read/write position in the parcel. @@ -304,7 +297,9 @@ impl<'a> BorrowedParcel<'a> { /// accesses are bounds checked, this call is still safe, but we can't rely /// on that. pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { - status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and the caller guarantees that `pos` is within bounds. + status_result(unsafe { sys::AParcel_setDataPosition(self.as_native(), pos) }) } /// Append a subset of another parcel. @@ -317,10 +312,10 @@ impl<'a> BorrowedParcel<'a> { start: i32, size: i32, ) -> Result<()> { + // Safety: `Parcel::appendFrom` from C++ checks that `start` + // and `size` are in bounds, and returns an error otherwise. + // Both `self` and `other` always contain valid pointers. let status = unsafe { - // Safety: `Parcel::appendFrom` from C++ checks that `start` - // and `size` are in bounds, and returns an error otherwise. - // Both `self` and `other` always contain valid pointers. sys::AParcel_appendFrom(other.as_native(), self.as_native_mut(), start, size) }; status_result(status) @@ -418,7 +413,9 @@ impl Parcel { /// accesses are bounds checked, this call is still safe, but we can't rely /// on that. pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { - self.borrowed_ref().set_data_position(pos) + // Safety: We have the same safety requirements as + // `BorrowedParcel::set_data_position`. + unsafe { self.borrowed_ref().set_data_position(pos) } } /// Append a subset of another parcel. @@ -461,7 +458,7 @@ impl<'a> BorrowedParcel<'a> { /// and call a closure with the sub-parcel as its parameter. /// The closure can keep reading data from the sub-parcel /// until it runs out of input data. The closure is responsible - /// for calling [`ReadableSubParcel::has_more_data`] to check for + /// for calling `ReadableSubParcel::has_more_data` to check for /// more data before every read, at least until Rust generators /// are stabilized. /// After the closure returns, skip to the end of the current @@ -504,7 +501,10 @@ impl<'a> BorrowedParcel<'a> { f(subparcel)?; // Advance the data position to the actual end, - // in case the closure read less data than was available + // in case the closure read less data than was available. + // + // Safety: end must be less than the current size of the parcel, because + // we checked above against `get_data_size`. unsafe { self.set_data_position(end)?; } @@ -595,7 +595,7 @@ impl Parcel { /// and call a closure with the sub-parcel as its parameter. /// The closure can keep reading data from the sub-parcel /// until it runs out of input data. The closure is responsible - /// for calling [`ReadableSubParcel::has_more_data`] to check for + /// for calling `ReadableSubParcel::has_more_data` to check for /// more data before every read, at least until Rust generators /// are stabilized. /// After the closure returns, skip to the end of the current @@ -649,17 +649,17 @@ impl Parcel { // Internal APIs impl<'a> BorrowedParcel<'a> { pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`. `AsNative` for `Option will either return + // null or a valid pointer to an `AIBinder`, both of which are + // valid, safe inputs to `AParcel_writeStrongBinder`. + // + // This call does not take ownership of the binder. However, it does + // require a mutable pointer, which we cannot extract from an + // immutable reference, so we clone the binder, incrementing the + // refcount before the call. The refcount will be immediately + // decremented when this temporary is dropped. unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`. `AsNative` for `Option will either return - // null or a valid pointer to an `AIBinder`, both of which are - // valid, safe inputs to `AParcel_writeStrongBinder`. - // - // This call does not take ownership of the binder. However, it does - // require a mutable pointer, which we cannot extract from an - // immutable reference, so we clone the binder, incrementing the - // refcount before the call. The refcount will be immediately - // decremented when this temporary is dropped. status_result(sys::AParcel_writeStrongBinder( self.as_native_mut(), binder.cloned().as_native_mut(), @@ -669,33 +669,28 @@ impl<'a> BorrowedParcel<'a> { pub(crate) fn read_binder(&self) -> Result> { let mut binder = ptr::null_mut(); - let status = unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`. We pass a valid, mutable out pointer to the `binder` - // parameter. After this call, `binder` will be either null or a - // valid pointer to an `AIBinder` owned by the caller. - sys::AParcel_readStrongBinder(self.as_native(), &mut binder) - }; + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`. We pass a valid, mutable out pointer to the `binder` + // parameter. After this call, `binder` will be either null or a + // valid pointer to an `AIBinder` owned by the caller. + let status = unsafe { sys::AParcel_readStrongBinder(self.as_native(), &mut binder) }; status_result(status)?; - Ok(unsafe { - // Safety: `binder` is either null or a valid, owned pointer at this - // point, so can be safely passed to `SpIBinder::from_raw`. - SpIBinder::from_raw(binder) - }) + // Safety: `binder` is either null or a valid, owned pointer at this + // point, so can be safely passed to `SpIBinder::from_raw`. + Ok(unsafe { SpIBinder::from_raw(binder) }) } } impl Drop for Parcel { fn drop(&mut self) { // Run the C++ Parcel complete object destructor - unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. Since we own the parcel, we can safely delete it - // here. - sys::AParcel_delete(self.ptr.as_ptr()) - } + // + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. Since we own the parcel, we can safely delete it + // here. + unsafe { sys::AParcel_delete(self.ptr.as_ptr()) } } } @@ -732,6 +727,8 @@ fn test_read_write() { parcel.write(&1i32).unwrap(); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { parcel.set_data_position(start).unwrap(); } @@ -748,6 +745,8 @@ fn test_read_data() { parcel.write(&b"Hello, Binder!\0"[..]).unwrap(); // Skip over string length + // SAFETY: str_start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(str_start).is_ok()); } @@ -756,42 +755,56 @@ fn test_read_data() { assert!(parcel.read::().unwrap()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 72i8); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 25928); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 1819043144); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 1819043144); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 4764857262830019912); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 4764857262830019912); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -799,6 +812,8 @@ fn test_read_data() { assert_eq!(parcel.read::().unwrap(), 1143139100000000000000000000.0); assert_eq!(parcel.read::().unwrap(), 40.043392); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -806,6 +821,8 @@ fn test_read_data() { assert_eq!(parcel.read::().unwrap(), 34732488246.197815); // Skip back to before the string length + // SAFETY: str_start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(str_start).is_ok()); } @@ -819,15 +836,21 @@ fn test_utf8_utf16_conversions() { let start = parcel.get_data_position(); assert!(parcel.write("Hello, Binder!").is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::>().unwrap().unwrap(), "Hello, Binder!",); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(parcel.write("Embedded null \0 inside a string").is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -835,6 +858,8 @@ fn test_utf8_utf16_conversions() { parcel.read::>().unwrap().unwrap(), "Embedded null \0 inside a string", ); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -849,6 +874,8 @@ fn test_utf8_utf16_conversions() { let s3 = "Some more text here."; assert!(parcel.write(&[s1, s2, s3][..]).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -874,6 +901,8 @@ fn test_sized_write() { assert_eq!(parcel.get_data_position(), start + expected_len); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { parcel.set_data_position(start).unwrap(); } @@ -893,6 +922,8 @@ fn test_append_from() { assert_eq!(4, parcel2.get_data_size()); assert_eq!(Ok(()), parcel2.append_all_from(&parcel1)); assert_eq!(8, parcel2.get_data_size()); + // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not + // empty. unsafe { parcel2.set_data_position(0).unwrap(); } @@ -903,6 +934,8 @@ fn test_append_from() { assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2)); assert_eq!(4, parcel2.get_data_size()); + // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not + // empty. unsafe { parcel2.set_data_position(0).unwrap(); } @@ -911,6 +944,8 @@ fn test_append_from() { let mut parcel2 = Parcel::new(); assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); assert_eq!(2, parcel2.get_data_size()); + // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not + // empty. unsafe { parcel2.set_data_position(0).unwrap(); } diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index de6d64934a01a81d0f7690cf3c07ebb799557603..6afe5ab42b48bce72185002d295c8a722e123b81 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -22,29 +22,28 @@ use crate::binder::AsNative; use crate::error::{status_result, Result, StatusCode}; use crate::sys; -use std::fs::File; -use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; /// Rust version of the Java class android.os.ParcelFileDescriptor #[derive(Debug)] -pub struct ParcelFileDescriptor(File); +pub struct ParcelFileDescriptor(OwnedFd); impl ParcelFileDescriptor { /// Create a new `ParcelFileDescriptor` - pub fn new(file: File) -> Self { - Self(file) + pub fn new>(fd: F) -> Self { + Self(fd.into()) } } -impl AsRef for ParcelFileDescriptor { - fn as_ref(&self) -> &File { +impl AsRef for ParcelFileDescriptor { + fn as_ref(&self) -> &OwnedFd { &self.0 } } -impl From for File { - fn from(file: ParcelFileDescriptor) -> File { - file.0 +impl From for OwnedFd { + fn from(fd: ParcelFileDescriptor) -> OwnedFd { + fd.0 } } @@ -73,14 +72,12 @@ impl Eq for ParcelFileDescriptor {} impl Serialize for ParcelFileDescriptor { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { let fd = self.0.as_raw_fd(); - let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a - // valid file, so we can borrow a valid file - // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take - // ownership of the fd, so we need not duplicate it first. - sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) - }; + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a + // valid file, so we can borrow a valid file + // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take + // ownership of the fd, so we need not duplicate it first. + let status = unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) }; status_result(status) } } @@ -92,13 +89,12 @@ impl SerializeOption for ParcelFileDescriptor { if let Some(f) = this { f.serialize(parcel) } else { - let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the - // value `-1` as the file descriptor to signify serializing a - // null file descriptor. - sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) - }; + let status = + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the + // value `-1` as the file descriptor to signify serializing a + // null file descriptor. + unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) }; status_result(status) } } @@ -107,31 +103,37 @@ impl SerializeOption for ParcelFileDescriptor { impl DeserializeOption for ParcelFileDescriptor { fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result> { let mut fd = -1i32; + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid mutable pointer to an i32, which + // `AParcel_readParcelFileDescriptor` assigns the valid file + // descriptor into, or `-1` if deserializing a null file + // descriptor. The read function passes ownership of the file + // descriptor to its caller if it was non-null, so we must take + // ownership of the file and ensure that it is eventually closed. unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. We pass a valid mutable pointer to an i32, which - // `AParcel_readParcelFileDescriptor` assigns the valid file - // descriptor into, or `-1` if deserializing a null file - // descriptor. The read function passes ownership of the file - // descriptor to its caller if it was non-null, so we must take - // ownership of the file and ensure that it is eventually closed. status_result(sys::AParcel_readParcelFileDescriptor(parcel.as_native(), &mut fd))?; } if fd < 0 { Ok(None) } else { - let file = unsafe { - // Safety: At this point, we know that the file descriptor was - // not -1, so must be a valid, owned file descriptor which we - // can safely turn into a `File`. - File::from_raw_fd(fd) - }; + // Safety: At this point, we know that the file descriptor was + // not -1, so must be a valid, owned file descriptor which we + // can safely turn into a `File`. + let file = unsafe { OwnedFd::from_raw_fd(fd) }; Ok(Some(ParcelFileDescriptor::new(file))) } } } impl Deserialize for ParcelFileDescriptor { + type UninitType = Option; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index 4b658fc74ddf7f982e80d3a2263df76de364a6c1..9008a3cc0e0562cddc596d71d156f16135ff5a94 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::binder::{AsNative, FromIBinder, Stability, Strong}; +use crate::binder::{AsNative, FromIBinder, Interface, Stability, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; use crate::parcel::BorrowedParcel; use crate::proxy::SpIBinder; @@ -22,7 +22,7 @@ use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; -use std::mem::{self, ManuallyDrop, MaybeUninit}; +use std::mem::{self, ManuallyDrop}; use std::os::raw::c_char; use std::ptr; use std::slice; @@ -50,20 +50,40 @@ pub trait Parcelable { fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>; } -/// A struct whose instances can be written to a [`Parcel`]. +/// A struct whose instances can be written to a [`crate::parcel::Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Serialize { - /// Serialize this instance into the given [`Parcel`]. + /// Serialize this instance into the given [`crate::parcel::Parcel`]. fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>; } -/// A struct whose instances can be restored from a [`Parcel`]. +/// A struct whose instances can be restored from a [`crate::parcel::Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Deserialize: Sized { - /// Deserialize an instance from the given [`Parcel`]. + /// Type for the uninitialized value of this type. Will be either `Self` + /// if the type implements `Default`, `Option` otherwise. + type UninitType; + + /// Assert at compile-time that `Self` and `Self::UninitType` have the same + /// size and alignment. This will either fail to compile or evaluate to `true`. + /// The only two macros that work here are `panic!` and `assert!`, so we cannot + /// use `assert_eq!`. + const ASSERT_UNINIT_SIZE_AND_ALIGNMENT: bool = { + assert!(std::mem::size_of::() == std::mem::size_of::()); + assert!(std::mem::align_of::() == std::mem::align_of::()); + true + }; + + /// Return an uninitialized or default-initialized value for this type. + fn uninit() -> Self::UninitType; + + /// Convert an initialized value of type `Self` into `Self::UninitType`. + fn from_init(value: Self) -> Self::UninitType; + + /// Deserialize an instance from the given [`crate::parcel::Parcel`]. fn deserialize(parcel: &BorrowedParcel<'_>) -> Result; - /// Deserialize an instance from the given [`Parcel`] onto the + /// Deserialize an instance from the given [`crate::parcel::Parcel`] onto the /// current object. This operation will overwrite the old value /// partially or completely, depending on how much data is available. fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { @@ -82,8 +102,8 @@ pub trait Deserialize: Sized { pub trait SerializeArray: Serialize + Sized { /// Serialize an array of this type into the given parcel. fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: Safe FFI, slice will always be a safe pointer to pass. let res = unsafe { - // Safety: Safe FFI, slice will always be a safe pointer to pass. sys::AParcel_writeParcelableArray( parcel.as_native_mut(), slice.as_ptr() as *const c_void, @@ -97,7 +117,9 @@ pub trait SerializeArray: Serialize + Sized { /// Callback to serialize an element of a generic parcelable array. /// -/// Safety: We are relying on binder_ndk to not overrun our slice. As long as it +/// # Safety +/// +/// We are relying on binder_ndk to not overrun our slice. As long as it /// doesn't provide an index larger than the length of the original slice in /// serialize_array, this operation is safe. The index provided is zero-based. unsafe extern "C" fn serialize_element( @@ -105,9 +127,14 @@ unsafe extern "C" fn serialize_element( array: *const c_void, index: usize, ) -> status_t { - let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1); - - let mut parcel = match BorrowedParcel::from_raw(parcel) { + // Safety: The caller guarantees that `array` is a valid pointer of the + // appropriate type. + let slice: &[T] = unsafe { slice::from_raw_parts(array.cast(), index + 1) }; + + // Safety: The caller must give us a parcel pointer which is either null or + // valid at least for the duration of this function call. We don't keep the + // resulting value beyond the function. + let mut parcel = match unsafe { BorrowedParcel::from_raw(parcel) } { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -121,10 +148,10 @@ unsafe extern "C" fn serialize_element( pub trait DeserializeArray: Deserialize { /// Deserialize an array of type from the given parcel. fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result>> { - let mut vec: Option>> = None; + let mut vec: Option> = None; + // Safety: Safe FFI, vec is the correct opaque type expected by + // allocate_vec and deserialize_element. let res = unsafe { - // Safety: Safe FFI, vec is the correct opaque type expected by - // allocate_vec and deserialize_element. sys::AParcel_readParcelableArray( parcel.as_native(), &mut vec as *mut _ as *mut c_void, @@ -133,36 +160,41 @@ pub trait DeserializeArray: Deserialize { ) }; status_result(res)?; - let vec: Option> = unsafe { - // Safety: We are assuming that the NDK correctly initialized every - // element of the vector by now, so we know that all the - // MaybeUninits are now properly initialized. We can transmute from - // Vec> to Vec because MaybeUninit has the same - // alignment and size as T, so the pointer to the vector allocation - // will be compatible. - mem::transmute(vec) - }; + // Safety: We are assuming that the NDK correctly initialized every + // element of the vector by now, so we know that all the + // UninitTypes are now properly initialized. We can transmute from + // Vec to Vec because T::UninitType has the same + // alignment and size as T, so the pointer to the vector allocation + // will be compatible. + let vec: Option> = unsafe { mem::transmute(vec) }; Ok(vec) } } /// Callback to deserialize a parcelable element. /// +/// # Safety +/// /// The opaque array data pointer must be a mutable pointer to an -/// `Option>>` with at least enough elements for `index` to be valid +/// `Option>` with at least enough elements for `index` to be valid /// (zero-based). unsafe extern "C" fn deserialize_element( parcel: *const sys::AParcel, array: *mut c_void, index: usize, ) -> status_t { - let vec = &mut *(array as *mut Option>>); + // Safety: The caller guarantees that `array` is a valid pointer of the + // appropriate type. + let vec = unsafe { &mut *(array as *mut Option>) }; let vec = match vec { Some(v) => v, None => return StatusCode::BAD_INDEX as status_t, }; - let parcel = match BorrowedParcel::from_raw(parcel as *mut _) { + // Safety: The caller must give us a parcel pointer which is either null or + // valid at least for the duration of this function call. We don't keep the + // resulting value beyond the function. + let parcel = match unsafe { BorrowedParcel::from_raw(parcel as *mut _) } { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -170,7 +202,7 @@ unsafe extern "C" fn deserialize_element( Ok(e) => e, Err(code) => return code as status_t, }; - ptr::write(vec[index].as_mut_ptr(), element); + vec[index] = T::from_init(element); StatusCode::OK as status_t } @@ -233,17 +265,22 @@ pub trait DeserializeOption: Deserialize { /// # Safety /// /// The opaque data pointer passed to the array read function must be a mutable -/// pointer to an `Option>>`. `buffer` will be assigned a mutable pointer -/// to the allocated vector data if this function returns true. -unsafe extern "C" fn allocate_vec_with_buffer( +/// pointer to an `Option>`. `buffer` will be assigned a mutable pointer +/// to the allocated vector data if this function returns true. `buffer` must be a valid pointer. +unsafe extern "C" fn allocate_vec_with_buffer( data: *mut c_void, len: i32, buffer: *mut *mut T, ) -> bool { - let res = allocate_vec::(data, len); - let vec = &mut *(data as *mut Option>>); + // Safety: We have the same safety requirements as `allocate_vec` for `data`. + let res = unsafe { allocate_vec::(data, len) }; + // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type. + let vec = unsafe { &mut *(data as *mut Option>) }; if let Some(new_vec) = vec { - *buffer = new_vec.as_mut_ptr() as *mut T; + // Safety: The caller guarantees that `buffer` is a valid pointer. + unsafe { + *buffer = new_vec.as_mut_ptr() as *mut T; + } } res } @@ -253,22 +290,24 @@ unsafe extern "C" fn allocate_vec_with_buffer( /// # Safety /// /// The opaque data pointer passed to the array read function must be a mutable -/// pointer to an `Option>>`. -unsafe extern "C" fn allocate_vec(data: *mut c_void, len: i32) -> bool { - let vec = &mut *(data as *mut Option>>); +/// pointer to an `Option>`. +unsafe extern "C" fn allocate_vec(data: *mut c_void, len: i32) -> bool { + // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type. + let vec = unsafe { &mut *(data as *mut Option>) }; if len < 0 { *vec = None; return true; } - let mut new_vec: Vec> = Vec::with_capacity(len as usize); - // Safety: We are filling the vector with uninitialized data here, but this - // is safe because the vector contains MaybeUninit elements which can be - // uninitialized. We're putting off the actual unsafe bit, transmuting the - // vector to a Vec until the contents are initialized. - new_vec.set_len(len as usize); + // Assert at compile time that `T` and `T::UninitType` have the same size and alignment. + let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT; + let mut new_vec: Vec = Vec::with_capacity(len as usize); + new_vec.resize_with(len as usize, T::uninit); - ptr::write(vec, Some(new_vec)); + // Safety: The caller guarantees that vec is a valid mutable pointer to the appropriate type. + unsafe { + ptr::write(vec, Some(new_vec)); + } true } @@ -283,22 +322,25 @@ macro_rules! parcelable_primitives { } /// Safety: All elements in the vector must be properly initialized. -unsafe fn vec_assume_init(vec: Vec>) -> Vec { - // We can convert from Vec> to Vec because MaybeUninit - // has the same alignment and size as T, so the pointer to the vector - // allocation will be compatible. +unsafe fn vec_assume_init(vec: Vec) -> Vec { + // Assert at compile time that `T` and `T::UninitType` have the same size and alignment. + let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT; + let mut vec = ManuallyDrop::new(vec); - Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) + // Safety: We can convert from Vec to Vec because + // T::UninitType has the same alignment and size as T, so the pointer to the + // vector allocation will be compatible. + unsafe { Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) } } macro_rules! impl_parcelable { {Serialize, $ty:ty, $write_fn:path} => { impl Serialize for $ty { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`, and any `$ty` literal value is safe to pass to + // `$write_fn`. unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`, and any `$ty` literal value is safe to pass to - // `$write_fn`. status_result($write_fn(parcel.as_native_mut(), *self)) } } @@ -307,13 +349,16 @@ macro_rules! impl_parcelable { {Deserialize, $ty:ty, $read_fn:path} => { impl Deserialize for $ty { + type UninitType = Self; + fn uninit() -> Self::UninitType { Self::UninitType::default() } + fn from_init(value: Self) -> Self::UninitType { value } fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { let mut val = Self::default(); + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid, mutable pointer to `val`, a + // literal of type `$ty`, and `$read_fn` will write the + // value read into `val` if successful unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. We pass a valid, mutable pointer to `val`, a - // literal of type `$ty`, and `$read_fn` will write the - // value read into `val` if successful status_result($read_fn(parcel.as_native(), &mut val))? }; Ok(val) @@ -324,13 +369,13 @@ macro_rules! impl_parcelable { {SerializeArray, $ty:ty, $write_array_fn:path} => { impl SerializeArray for $ty { fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` + // will be a valid pointer to an array of elements of type + // `$ty`. If the slice length is 0, `slice.as_ptr()` may be + // dangling, but this is safe since the pointer is not + // dereferenced if the length parameter is 0. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` - // will be a valid pointer to an array of elements of type - // `$ty`. If the slice length is 0, `slice.as_ptr()` may be - // dangling, but this is safe since the pointer is not - // dereferenced if the length parameter is 0. $write_array_fn( parcel.as_native_mut(), slice.as_ptr(), @@ -348,12 +393,12 @@ macro_rules! impl_parcelable { {DeserializeArray, $ty:ty, $read_array_fn:path} => { impl DeserializeArray for $ty { fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result>> { - let mut vec: Option>> = None; + let mut vec: Option> = None; + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `allocate_vec` expects the opaque pointer to + // be of type `*mut Option>`, so `&mut vec` is + // correct for it. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. $read_array_fn( parcel.as_native(), &mut vec as *mut _ as *mut c_void, @@ -361,11 +406,11 @@ macro_rules! impl_parcelable { ) }; status_result(status)?; + // Safety: We are assuming that the NDK correctly + // initialized every element of the vector by now, so we + // know that all the UninitTypes are now properly + // initialized. let vec: Option> = unsafe { - // Safety: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. vec.map(|vec| vec_assume_init(vec)) }; Ok(vec) @@ -440,6 +485,14 @@ impl Serialize for u8 { } impl Deserialize for u8 { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { i8::deserialize(parcel).map(|v| v as u8) } @@ -447,13 +500,13 @@ impl Deserialize for u8 { impl SerializeArray for u8 { fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is + // 0. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. sys::AParcel_writeByteArray( parcel.as_native_mut(), slice.as_ptr() as *const i8, @@ -471,6 +524,14 @@ impl Serialize for i16 { } impl Deserialize for i16 { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { u16::deserialize(parcel).map(|v| v as i16) } @@ -478,13 +539,13 @@ impl Deserialize for i16 { impl SerializeArray for i16 { fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is + // 0. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. sys::AParcel_writeCharArray( parcel.as_native_mut(), slice.as_ptr() as *const u16, @@ -498,22 +559,22 @@ impl SerializeArray for i16 { impl SerializeOption for str { fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { match this { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the string pointer is null, + // `AParcel_writeString` requires that the length is -1 to + // indicate that we want to serialize a null string. None => unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the string pointer is null, - // `AParcel_writeString` requires that the length is -1 to - // indicate that we want to serialize a null string. status_result(sys::AParcel_writeString(parcel.as_native_mut(), ptr::null(), -1)) }, + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8 + // string pointer of `length` bytes, which is what str in Rust + // is. The docstring for `AParcel_writeString` says that the + // string input should be null-terminated, but it doesn't + // actually rely on that fact in the code. If this ever becomes + // necessary, we will need to null-terminate the str buffer + // before sending it. Some(s) => unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8 - // string pointer of `length` bytes, which is what str in Rust - // is. The docstring for `AParcel_writeString` says that the - // string input should be null-terminated, but it doesn't - // actually rely on that fact in the code. If this ever becomes - // necessary, we will need to null-terminate the str buffer - // before sending it. status_result(sys::AParcel_writeString( parcel.as_native_mut(), s.as_ptr() as *const c_char, @@ -547,13 +608,21 @@ impl SerializeOption for String { } impl Deserialize for Option { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { let mut vec: Option> = None; + // Safety: `Parcel` always contains a valid pointer to an `AParcel`. + // `Option>` is equivalent to the expected `Option>` + // for `allocate_vec`, so `vec` is safe to pass as the opaque data + // pointer on platforms where char is signed. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel`. - // `Option>` is equivalent to the expected `Option>` - // for `allocate_vec`, so `vec` is safe to pass as the opaque data - // pointer on platforms where char is signed. sys::AParcel_readString( parcel.as_native(), &mut vec as *mut _ as *mut c_void, @@ -575,6 +644,14 @@ impl Deserialize for Option { impl DeserializeArray for Option {} impl Deserialize for String { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } @@ -611,6 +688,14 @@ impl SerializeOption for Vec { } impl Deserialize for Vec { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { DeserializeArray::deserialize_array(parcel) .transpose() @@ -640,6 +725,14 @@ impl SerializeOption for [T; N] { impl SerializeArray for [T; N] {} impl Deserialize for [T; N] { + type UninitType = [T::UninitType; N]; + fn uninit() -> Self::UninitType { + [(); N].map(|_| T::uninit()) + } + fn from_init(value: Self) -> Self::UninitType { + value.map(T::from_init) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { let vec = DeserializeArray::deserialize_array(parcel) .transpose() @@ -664,6 +757,14 @@ impl Serialize for Stability { } impl Deserialize for Stability { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { i32::deserialize(parcel).and_then(Stability::try_from) } @@ -671,34 +772,39 @@ impl Deserialize for Stability { impl Serialize for Status { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an `AParcel` + // and `Status` always contains a valid pointer to an `AStatus`, so + // both parameters are valid and safe. This call does not take + // ownership of either of its parameters. unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel` - // and `Status` always contains a valid pointer to an `AStatus`, so - // both parameters are valid and safe. This call does not take - // ownership of either of its parameters. status_result(sys::AParcel_writeStatusHeader(parcel.as_native_mut(), self.as_native())) } } } impl Deserialize for Status { + type UninitType = Option; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { let mut status_ptr = ptr::null_mut(); - let ret_status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. We pass a mutable out pointer which will be - // assigned a valid `AStatus` pointer if the function returns - // status OK. This function passes ownership of the status - // pointer to the caller, if it was assigned. - sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) - }; + let ret_status = + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a mutable out pointer which will be + // assigned a valid `AStatus` pointer if the function returns + // status OK. This function passes ownership of the status + // pointer to the caller, if it was assigned. + unsafe { sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) }; status_result(ret_status)?; - Ok(unsafe { - // Safety: At this point, the return status of the read call was ok, - // so we know that `status_ptr` is a valid, owned pointer to an - // `AStatus`, from which we can safely construct a `Status` object. - Status::from_ptr(status_ptr) - }) + // Safety: At this point, the return status of the read call was ok, + // so we know that `status_ptr` is a valid, owned pointer to an + // `AStatus`, from which we can safely construct a `Status` object. + Ok(unsafe { Status::from_ptr(status_ptr) }) } } @@ -717,12 +823,29 @@ impl SerializeOption for Strong { impl SerializeArray for Strong {} impl Deserialize for Strong { + type UninitType = Option>; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { let ibinder: SpIBinder = parcel.read()?; FromIBinder::try_from(ibinder) } } +struct AssertIBinder; +impl Interface for AssertIBinder {} +impl FromIBinder for AssertIBinder { + // This is only needed so we can assert on the size of Strong + fn try_from(_: SpIBinder) -> Result> { + unimplemented!() + } +} + impl DeserializeOption for Strong { fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result> { let ibinder: Option = parcel.read()?; @@ -752,6 +875,14 @@ impl Serialize for Option { } impl Deserialize for Option { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { DeserializeOption::deserialize_option(parcel) } @@ -767,7 +898,6 @@ impl Deserialize for Option { /// `Serialize`, `SerializeArray` and `SerializeOption` for /// structured parcelables. The target type must implement the /// `Parcelable` trait. -/// ``` #[macro_export] macro_rules! impl_serialize_for_parcelable { ($parcelable:ident) => { @@ -821,6 +951,9 @@ macro_rules! impl_deserialize_for_parcelable { }; ($parcelable:ident < $( $param:ident ),* > ) => { impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > { + type UninitType = Self; + fn uninit() -> Self::UninitType { Self::UninitType::default() } + fn from_init(value: Self) -> Self::UninitType { value } fn deserialize( parcel: &$crate::binder_impl::BorrowedParcel<'_>, ) -> std::result::Result { @@ -876,6 +1009,14 @@ impl Serialize for Box { } impl Deserialize for Box { + type UninitType = Option; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { Deserialize::deserialize(parcel).map(Box::new) } @@ -900,6 +1041,7 @@ mod tests { #[test] fn test_custom_parcelable() { + #[derive(Default)] struct Custom(u32, bool, String, Vec); impl Serialize for Custom { @@ -912,6 +1054,14 @@ mod tests { } impl Deserialize for Custom { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { Ok(Custom( parcel.read()?, @@ -937,6 +1087,8 @@ mod tests { assert!(custom.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -959,6 +1111,8 @@ mod tests { assert!(bools.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -968,6 +1122,8 @@ mod tests { assert_eq!(parcel.read::().unwrap(), 0); assert_eq!(parcel.read::().unwrap(), 0); assert_eq!(parcel.read::().unwrap(), 1); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -983,12 +1139,17 @@ mod tests { assert!(parcel.write(&u8s[..]).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 4); // 4 items assert_eq!(parcel.read::().unwrap(), 0x752aff65); // bytes + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -998,18 +1159,25 @@ mod tests { let i8s = [-128i8, 127, 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(parcel.write(&i8s[..]).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::().unwrap(), 4); // 4 items assert_eq!(parcel.read::().unwrap(), 0x8b2a7f80); // bytes + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1019,10 +1187,14 @@ mod tests { let u16s = [u16::max_value(), 12_345, 42, 117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(u16s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1032,6 +1204,9 @@ mod tests { assert_eq!(parcel.read::().unwrap(), 12345); // 12,345 assert_eq!(parcel.read::().unwrap(), 42); // 42 assert_eq!(parcel.read::().unwrap(), 117); // 117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1042,10 +1217,14 @@ mod tests { let i16s = [i16::max_value(), i16::min_value(), 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(i16s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1055,6 +1234,9 @@ mod tests { assert_eq!(parcel.read::().unwrap(), 0x8000); // i16::min_value() assert_eq!(parcel.read::().unwrap(), 42); // 42 assert_eq!(parcel.read::().unwrap(), 0xff8b); // -117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1065,10 +1247,14 @@ mod tests { let u32s = [u32::max_value(), 12_345, 42, 117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(u32s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1078,6 +1264,9 @@ mod tests { assert_eq!(parcel.read::().unwrap(), 12345); // 12,345 assert_eq!(parcel.read::().unwrap(), 42); // 42 assert_eq!(parcel.read::().unwrap(), 117); // 117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1088,10 +1277,14 @@ mod tests { let i32s = [i32::max_value(), i32::min_value(), 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(i32s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1101,6 +1294,9 @@ mod tests { assert_eq!(parcel.read::().unwrap(), 0x80000000); // i32::min_value() assert_eq!(parcel.read::().unwrap(), 42); // 42 assert_eq!(parcel.read::().unwrap(), 0xffffff8b); // -117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1111,10 +1307,14 @@ mod tests { let u64s = [u64::max_value(), 12_345, 42, 117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(u64s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1125,10 +1325,14 @@ mod tests { let i64s = [i64::max_value(), i64::min_value(), 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(i64s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1139,10 +1343,14 @@ mod tests { let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(f32s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1155,10 +1363,14 @@ mod tests { let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(f64s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1176,10 +1388,14 @@ mod tests { let strs = [s1, s2, s3, s4]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(strs.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index c829d37dca7b8f46bcfa1da592868dc0481bf7c6..f90611361fdf2356c2b74be20547c90c7fbf45d7 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -133,8 +133,8 @@ impl ParcelableHolder { } } ParcelableHolderData::Parcel(ref mut parcel) => { + // Safety: 0 should always be a valid position. unsafe { - // Safety: 0 should always be a valid position. parcel.set_data_position(0)?; } @@ -161,6 +161,15 @@ impl ParcelableHolder { } } +impl Clone for ParcelableHolder { + fn clone(&self) -> ParcelableHolder { + ParcelableHolder { + data: Mutex::new(self.data.lock().unwrap().clone()), + stability: self.stability, + } + } +} + impl Serialize for ParcelableHolder { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> { parcel.write(&NON_NULL_PARCELABLE_FLAG)?; @@ -169,6 +178,14 @@ impl Serialize for ParcelableHolder { } impl Deserialize for ParcelableHolder { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::new(Default::default()) + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { let status: i32 = parcel.read()?; if status == NULL_PARCELABLE_FLAG { @@ -197,15 +214,15 @@ impl Parcelable for ParcelableHolder { parcelable.write_to_parcel(parcel)?; let end = parcel.get_data_position(); + // Safety: we got the position from `get_data_position`. unsafe { - // Safety: we got the position from `get_data_position`. parcel.set_data_position(length_start)?; } assert!(end >= data_start); parcel.write(&(end - data_start))?; + // Safety: we got the position from `get_data_position`. unsafe { - // Safety: we got the position from `get_data_position`. parcel.set_data_position(end)?; } @@ -243,11 +260,11 @@ impl Parcelable for ParcelableHolder { new_parcel.append_from(parcel, data_start, data_size)?; *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel); + // Safety: `append_from` checks if `data_size` overflows + // `parcel` and returns `BAD_VALUE` if that happens. We also + // explicitly check for negative and zero `data_size` above, + // so `data_end` is guaranteed to be greater than `data_start`. unsafe { - // Safety: `append_from` checks if `data_size` overflows - // `parcel` and returns `BAD_VALUE` if that happens. We also - // explicitly check for negative and zero `data_size` above, - // so `data_end` is guaranteed to be greater than `data_start`. parcel.set_data_position(data_end)?; } diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 254efaed9a65354a14f76865d5916b4553aeb8ec..7434e9ddbde01ff2679e7f24f3f09ead9455be24 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -32,8 +32,8 @@ use std::convert::TryInto; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::mem; +use std::os::fd::AsRawFd; use std::os::raw::c_char; -use std::os::unix::io::AsRawFd; use std::ptr; use std::sync::Arc; @@ -49,14 +49,12 @@ impl fmt::Debug for SpIBinder { } } -/// # Safety -/// -/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe +/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Send for SpIBinder {} -/// # Safety -/// -/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe +/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Sync for SpIBinder {} impl SpIBinder { @@ -97,11 +95,9 @@ impl SpIBinder { /// Return true if this binder object is hosted in a different process than /// the current one. pub fn is_remote(&self) -> bool { - unsafe { - // Safety: `SpIBinder` guarantees that it always contains a valid - // `AIBinder` pointer. - sys::AIBinder_isRemote(self.as_native()) - } + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. + unsafe { sys::AIBinder_isRemote(self.as_native()) } } /// Try to convert this Binder object into a trait object for the given @@ -116,12 +112,12 @@ impl SpIBinder { /// Return the interface class of this binder object, if associated with /// one. pub fn get_class(&mut self) -> Option { + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. `AIBinder_getClass` returns either a null + // pointer or a valid pointer to an `AIBinder_Class`. After mapping + // null to None, we can safely construct an `InterfaceClass` if the + // pointer was non-null. unsafe { - // Safety: `SpIBinder` guarantees that it always contains a valid - // `AIBinder` pointer. `AIBinder_getClass` returns either a null - // pointer or a valid pointer to an `AIBinder_Class`. After mapping - // null to None, we can safely construct an `InterfaceClass` if the - // pointer was non-null. let class = sys::AIBinder_getClass(self.as_native_mut()); class.as_ref().map(|p| InterfaceClass::from_ptr(p)) } @@ -152,7 +148,8 @@ pub mod unstable_api { /// /// See `SpIBinder::from_raw`. pub unsafe fn new_spibinder(ptr: *mut sys::AIBinder) -> Option { - SpIBinder::from_raw(ptr) + // Safety: The caller makes the same guarantees as this requires. + unsafe { SpIBinder::from_raw(ptr) } } } @@ -171,30 +168,24 @@ pub trait AssociateClass { impl AssociateClass for SpIBinder { fn associate_class(&mut self, class: InterfaceClass) -> bool { - unsafe { - // Safety: `SpIBinder` guarantees that it always contains a valid - // `AIBinder` pointer. An `InterfaceClass` can always be converted - // into a valid `AIBinder_Class` pointer, so these parameters are - // always safe. - sys::AIBinder_associateClass(self.as_native_mut(), class.into()) - } + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. An `InterfaceClass` can always be converted + // into a valid `AIBinder_Class` pointer, so these parameters are + // always safe. + unsafe { sys::AIBinder_associateClass(self.as_native_mut(), class.into()) } } } impl Ord for SpIBinder { fn cmp(&self, other: &Self) -> Ordering { - let less_than = unsafe { - // Safety: SpIBinder always holds a valid `AIBinder` pointer, so - // this pointer is always safe to pass to `AIBinder_lt` (null is - // also safe to pass to this function, but we should never do that). - sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr()) - }; - let greater_than = unsafe { - // Safety: SpIBinder always holds a valid `AIBinder` pointer, so - // this pointer is always safe to pass to `AIBinder_lt` (null is - // also safe to pass to this function, but we should never do that). - sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr()) - }; + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this + // pointer is always safe to pass to `AIBinder_lt` (null is also safe to + // pass to this function, but we should never do that). + let less_than = unsafe { sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr()) }; + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this + // pointer is always safe to pass to `AIBinder_lt` (null is also safe to + // pass to this function, but we should never do that). + let greater_than = unsafe { sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr()) }; if !less_than && !greater_than { Ordering::Equal } else if less_than { @@ -221,10 +212,10 @@ impl Eq for SpIBinder {} impl Clone for SpIBinder { fn clone(&self) -> Self { + // Safety: Cloning a strong reference must increment the reference + // count. We are guaranteed by the `SpIBinder` constructor + // invariants that `self.0` is always a valid `AIBinder` pointer. unsafe { - // Safety: Cloning a strong reference must increment the reference - // count. We are guaranteed by the `SpIBinder` constructor - // invariants that `self.0` is always a valid `AIBinder` pointer. sys::AIBinder_incStrong(self.0.as_ptr()); } Self(self.0) @@ -235,9 +226,9 @@ impl Drop for SpIBinder { // We hold a strong reference to the IBinder in SpIBinder and need to give up // this reference on drop. fn drop(&mut self) { + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we + // know this pointer is safe to pass to `AIBinder_decStrong` here. unsafe { - // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we - // know this pointer is safe to pass to `AIBinder_decStrong` here. sys::AIBinder_decStrong(self.as_native_mut()); } } @@ -246,26 +237,24 @@ impl Drop for SpIBinder { impl> IBinderInternal for T { fn prepare_transact(&self) -> Result { let mut input = ptr::null_mut(); + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. It is safe to cast from an + // immutable pointer to a mutable pointer here, because + // `AIBinder_prepareTransaction` only calls immutable `AIBinder` + // methods but the parameter is unfortunately not marked as const. + // + // After the call, input will be either a valid, owned `AParcel` + // pointer, or null. let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. It is safe to cast from an - // immutable pointer to a mutable pointer here, because - // `AIBinder_prepareTransaction` only calls immutable `AIBinder` - // methods but the parameter is unfortunately not marked as const. - // - // After the call, input will be either a valid, owned `AParcel` - // pointer, or null. sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input) }; status_result(status)?; - unsafe { - // Safety: At this point, `input` is either a valid, owned `AParcel` - // pointer, or null. `OwnedParcel::from_raw` safely handles both cases, - // taking ownership of the parcel. - Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) - } + // Safety: At this point, `input` is either a valid, owned `AParcel` + // pointer, or null. `OwnedParcel::from_raw` safely handles both cases, + // taking ownership of the parcel. + unsafe { Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) } } fn submit_transact( @@ -275,23 +264,23 @@ impl> IBinderInternal for T { flags: TransactionFlags, ) -> Result { let mut reply = ptr::null_mut(); + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. Although `IBinder::transact` is + // not a const method, it is still safe to cast our immutable + // pointer to mutable for the call. First, `IBinder::transact` is + // thread-safe, so concurrency is not an issue. The only way that + // `transact` can affect any visible, mutable state in the current + // process is by calling `onTransact` for a local service. However, + // in order for transactions to be thread-safe, this method must + // dynamically lock its data before modifying it. We enforce this + // property in Rust by requiring `Sync` for remotable objects and + // only providing `on_transact` with an immutable reference to + // `self`. + // + // This call takes ownership of the `data` parcel pointer, and + // passes ownership of the `reply` out parameter to its caller. It + // does not affect ownership of the `binder` parameter. let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. Although `IBinder::transact` is - // not a const method, it is still safe to cast our immutable - // pointer to mutable for the call. First, `IBinder::transact` is - // thread-safe, so concurrency is not an issue. The only way that - // `transact` can affect any visible, mutable state in the current - // process is by calling `onTransact` for a local service. However, - // in order for transactions to be thread-safe, this method must - // dynamically lock its data before modifying it. We enforce this - // property in Rust by requiring `Sync` for remotable objects and - // only providing `on_transact` with an immutable reference to - // `self`. - // - // This call takes ownership of the `data` parcel pointer, and - // passes ownership of the `reply` out parameter to its caller. It - // does not affect ownership of the `binder` parameter. sys::AIBinder_transact( self.as_native() as *mut sys::AIBinder, code, @@ -302,45 +291,45 @@ impl> IBinderInternal for T { }; status_result(status)?; - unsafe { - // Safety: `reply` is either a valid `AParcel` pointer or null - // after the call to `AIBinder_transact` above, so we can - // construct a `Parcel` out of it. `AIBinder_transact` passes - // ownership of the `reply` parcel to Rust, so we need to - // construct an owned variant. - Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) - } + // Safety: `reply` is either a valid `AParcel` pointer or null + // after the call to `AIBinder_transact` above, so we can + // construct a `Parcel` out of it. `AIBinder_transact` passes + // ownership of the `reply` parcel to Rust, so we need to + // construct an owned variant. + unsafe { Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) } } fn is_binder_alive(&self) -> bool { - unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. - // - // This call does not affect ownership of its pointer parameter. - sys::AIBinder_isAlive(self.as_native()) - } + // Safety: `SpIBinder` guarantees that `self` always contains a valid + // pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + unsafe { sys::AIBinder_isAlive(self.as_native()) } } #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool) { + // Safety: `SpIBinder` guarantees that `self` always contains a valid + // pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) }; } fn dump(&mut self, fp: &F, args: &[&str]) -> Result<()> { let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect(); let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect(); + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the + // file descriptor parameter is always be a valid open file. The + // `args` pointer parameter is a valid pointer to an array of C + // strings that will outlive the call since `args` lives for the + // whole function scope. + // + // This call does not affect ownership of its binder pointer + // parameter and does not take ownership of the file or args array + // parameters. let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the - // file descriptor parameter is always be a valid open file. The - // `args` pointer parameter is a valid pointer to an array of C - // strings that will outlive the call since `args` lives for the - // whole function scope. - // - // This call does not affect ownership of its binder pointer - // parameter and does not take ownership of the file or args array - // parameters. sys::AIBinder_dump( self.as_native_mut(), fp.as_raw_fd(), @@ -353,22 +342,18 @@ impl> IBinderInternal for T { fn get_extension(&mut self) -> Result> { let mut out = ptr::null_mut(); - let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. After this call, the `out` - // parameter will be either null, or a valid pointer to an - // `AIBinder`. - // - // This call passes ownership of the out pointer to its caller - // (assuming it is set to a non-null value). - sys::AIBinder_getExtension(self.as_native_mut(), &mut out) - }; - let ibinder = unsafe { - // Safety: The call above guarantees that `out` is either null or a - // valid, owned pointer to an `AIBinder`, both of which are safe to - // pass to `SpIBinder::from_raw`. - SpIBinder::from_raw(out) - }; + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. After this call, the `out` + // parameter will be either null, or a valid pointer to an + // `AIBinder`. + // + // This call passes ownership of the out pointer to its caller + // (assuming it is set to a non-null value). + let status = unsafe { sys::AIBinder_getExtension(self.as_native_mut(), &mut out) }; + // Safety: The call above guarantees that `out` is either null or a + // valid, owned pointer to an `AIBinder`, both of which are safe to + // pass to `SpIBinder::from_raw`. + let ibinder = unsafe { SpIBinder::from_raw(out) }; status_result(status)?; Ok(ibinder) @@ -377,17 +362,17 @@ impl> IBinderInternal for T { impl> IBinder for T { fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `recipient` can always be + // converted into a valid pointer to an + // `AIBinder_DeathRecipient`. + // + // The cookie is also the correct pointer, and by calling new_cookie, + // we have created a new ref-count to the cookie, which linkToDeath + // takes ownership of. Once the DeathRecipient is unlinked for any + // reason (including if this call fails), the onUnlinked callback + // will consume that ref-count. status_result(unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. `recipient` can always be - // converted into a valid pointer to an - // `AIBinder_DeathRecipient`. - // - // The cookie is also the correct pointer, and by calling new_cookie, - // we have created a new ref-count to the cookie, which linkToDeath - // takes ownership of. Once the DeathRecipient is unlinked for any - // reason (including if this call fails), the onUnlinked callback - // will consume that ref-count. sys::AIBinder_linkToDeath( self.as_native_mut(), recipient.as_native_mut(), @@ -397,13 +382,13 @@ impl> IBinder for T { } fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `recipient` can always be + // converted into a valid pointer to an + // `AIBinder_DeathRecipient`. Any value is safe to pass as the + // cookie, although we depend on this value being set by + // `get_cookie` when the death recipient callback is called. status_result(unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. `recipient` can always be - // converted into a valid pointer to an - // `AIBinder_DeathRecipient`. Any value is safe to pass as the - // cookie, although we depend on this value being set by - // `get_cookie` when the death recipient callback is called. sys::AIBinder_unlinkToDeath( self.as_native_mut(), recipient.as_native_mut(), @@ -413,13 +398,11 @@ impl> IBinder for T { } fn ping_binder(&mut self) -> Result<()> { - let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. - // - // This call does not affect ownership of its pointer parameter. - sys::AIBinder_ping(self.as_native_mut()) - }; + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + let status = unsafe { sys::AIBinder_ping(self.as_native_mut()) }; status_result(status) } } @@ -439,6 +422,14 @@ impl SerializeOption for SpIBinder { impl SerializeArray for SpIBinder {} impl Deserialize for SpIBinder { + type UninitType = Option; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result { parcel.read_binder().transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } @@ -464,35 +455,31 @@ impl fmt::Debug for WpIBinder { } } -/// # Safety -/// -/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe. +/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Send for WpIBinder {} -/// # Safety -/// -/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe. +/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Sync for WpIBinder {} impl WpIBinder { /// Create a new weak reference from an object that can be converted into a /// raw `AIBinder` pointer. fn new>(binder: &mut B) -> WpIBinder { - let ptr = unsafe { - // Safety: `SpIBinder` guarantees that `binder` always contains a - // valid pointer to an `AIBinder`. - sys::AIBinder_Weak_new(binder.as_native_mut()) - }; + // Safety: `SpIBinder` guarantees that `binder` always contains a valid + // pointer to an `AIBinder`. + let ptr = unsafe { sys::AIBinder_Weak_new(binder.as_native_mut()) }; Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_new")) } /// Promote this weak reference to a strong reference to the binder object. pub fn promote(&self) -> Option { + // Safety: `WpIBinder` always contains a valid weak reference, so we can + // pass this pointer to `AIBinder_Weak_promote`. Returns either null or + // an AIBinder owned by the caller, both of which are valid to pass to + // `SpIBinder::from_raw`. unsafe { - // Safety: `WpIBinder` always contains a valid weak reference, so we - // can pass this pointer to `AIBinder_Weak_promote`. Returns either - // null or an AIBinder owned by the caller, both of which are valid - // to pass to `SpIBinder::from_raw`. let ptr = sys::AIBinder_Weak_promote(self.0.as_ptr()); SpIBinder::from_raw(ptr) } @@ -501,35 +488,27 @@ impl WpIBinder { impl Clone for WpIBinder { fn clone(&self) -> Self { - let ptr = unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, - // so this pointer is always safe to pass to `AIBinder_Weak_clone` - // (although null is also a safe value to pass to this API). - // - // We get ownership of the returned pointer, so can construct a new - // WpIBinder object from it. - sys::AIBinder_Weak_clone(self.0.as_ptr()) - }; + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so + // this pointer is always safe to pass to `AIBinder_Weak_clone` + // (although null is also a safe value to pass to this API). + // + // We get ownership of the returned pointer, so can construct a new + // WpIBinder object from it. + let ptr = unsafe { sys::AIBinder_Weak_clone(self.0.as_ptr()) }; Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_clone")) } } impl Ord for WpIBinder { fn cmp(&self, other: &Self) -> Ordering { - let less_than = unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, - // so this pointer is always safe to pass to `AIBinder_Weak_lt` - // (null is also safe to pass to this function, but we should never - // do that). - sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr()) - }; - let greater_than = unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, - // so this pointer is always safe to pass to `AIBinder_Weak_lt` - // (null is also safe to pass to this function, but we should never - // do that). - sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr()) - }; + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so + // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is + // also safe to pass to this function, but we should never do that). + let less_than = unsafe { sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr()) }; + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so + // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is + // also safe to pass to this function, but we should never do that). + let greater_than = unsafe { sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr()) }; if !less_than && !greater_than { Ordering::Equal } else if less_than { @@ -556,9 +535,9 @@ impl Eq for WpIBinder {} impl Drop for WpIBinder { fn drop(&mut self) { + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we + // know this pointer is safe to pass to `AIBinder_Weak_delete` here. unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we - // know this pointer is safe to pass to `AIBinder_Weak_delete` here. sys::AIBinder_Weak_delete(self.0.as_ptr()); } } @@ -566,7 +545,7 @@ impl Drop for WpIBinder { /// Rust wrapper around DeathRecipient objects. /// -/// The cookie in this struct represents an Arc for the owned callback. +/// The cookie in this struct represents an `Arc` for the owned callback. /// This struct owns a ref-count of it, and so does every binder that we /// have been linked with. /// @@ -584,17 +563,13 @@ struct DeathRecipientVtable { cookie_decr_refcount: unsafe extern "C" fn(*mut c_void), } -/// # Safety -/// -/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer -/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and +/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As /// `AIBinder_DeathRecipient` is threadsafe, this structure is too. unsafe impl Send for DeathRecipient {} -/// # Safety -/// -/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer -/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and +/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As /// `AIBinder_DeathRecipient` is threadsafe, this structure is too. unsafe impl Sync for DeathRecipient {} @@ -606,19 +581,17 @@ impl DeathRecipient { F: Fn() + Send + Sync + 'static, { let callback: *const F = Arc::into_raw(Arc::new(callback)); - let recipient = unsafe { - // Safety: The function pointer is a valid death recipient callback. - // - // This call returns an owned `AIBinder_DeathRecipient` pointer - // which must be destroyed via `AIBinder_DeathRecipient_delete` when - // no longer needed. - sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::)) - }; + // Safety: The function pointer is a valid death recipient callback. + // + // This call returns an owned `AIBinder_DeathRecipient` pointer which + // must be destroyed via `AIBinder_DeathRecipient_delete` when no longer + // needed. + let recipient = unsafe { sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::)) }; + // Safety: The function pointer is a valid onUnlinked callback. + // + // All uses of linkToDeath in this file correctly increment the + // ref-count that this onUnlinked callback will decrement. unsafe { - // Safety: The function pointer is a valid onUnlinked callback. - // - // All uses of linkToDeath in this file correctly increment the - // ref-count that this onUnlinked callback will decrement. sys::AIBinder_DeathRecipient_setOnUnlinked( recipient, Some(Self::cookie_decr_refcount::), @@ -640,7 +613,12 @@ impl DeathRecipient { /// /// The caller must handle the returned ref-count correctly. unsafe fn new_cookie(&self) -> *mut c_void { - (self.vtable.cookie_incr_refcount)(self.cookie); + // Safety: `cookie_incr_refcount` points to + // `Self::cookie_incr_refcount`, and `self.cookie` is the cookie for an + // Arc. + unsafe { + (self.vtable.cookie_incr_refcount)(self.cookie); + } // Return a raw pointer with ownership of a ref-count self.cookie @@ -659,13 +637,14 @@ impl DeathRecipient { /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an Arc and + /// The `cookie` parameter must be the cookie for an `Arc` and /// the caller must hold a ref-count to it. unsafe extern "C" fn binder_died(cookie: *mut c_void) where F: Fn() + Send + Sync + 'static, { - let callback = (cookie as *const F).as_ref().unwrap(); + // Safety: The caller promises that `cookie` is for an Arc. + let callback = unsafe { (cookie as *const F).as_ref().unwrap() }; callback(); } @@ -674,34 +653,34 @@ impl DeathRecipient { /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an Arc and + /// The `cookie` parameter must be the cookie for an `Arc` and /// the owner must give up a ref-count to it. unsafe extern "C" fn cookie_decr_refcount(cookie: *mut c_void) where F: Fn() + Send + Sync + 'static, { - drop(Arc::from_raw(cookie as *const F)); + // Safety: The caller promises that `cookie` is for an Arc. + drop(unsafe { Arc::from_raw(cookie as *const F) }); } /// Callback that increments the ref-count. /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an Arc and + /// The `cookie` parameter must be the cookie for an `Arc` and /// the owner must handle the created ref-count properly. unsafe extern "C" fn cookie_incr_refcount(cookie: *mut c_void) where F: Fn() + Send + Sync + 'static, { - let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F)); + // Safety: The caller promises that `cookie` is for an Arc. + let arc = mem::ManuallyDrop::new(unsafe { Arc::from_raw(cookie as *const F) }); mem::forget(Arc::clone(&arc)); } } -/// # Safety -/// -/// A `DeathRecipient` is always constructed with a valid raw pointer to an -/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this +/// Safety: A `DeathRecipient` is always constructed with a valid raw pointer to +/// an `AIBinder_DeathRecipient`, so it is always type-safe to extract this /// pointer. unsafe impl AsNative for DeathRecipient { fn as_native(&self) -> *const sys::AIBinder_DeathRecipient { @@ -715,18 +694,19 @@ unsafe impl AsNative for DeathRecipient { impl Drop for DeathRecipient { fn drop(&mut self) { + // Safety: `self.recipient` is always a valid, owned + // `AIBinder_DeathRecipient` pointer returned by + // `AIBinder_DeathRecipient_new` when `self` was created. This delete + // method can only be called once when `self` is dropped. unsafe { - // Safety: `self.recipient` is always a valid, owned - // `AIBinder_DeathRecipient` pointer returned by - // `AIBinder_DeathRecipient_new` when `self` was created. This - // delete method can only be called once when `self` is dropped. sys::AIBinder_DeathRecipient_delete(self.recipient); + } - // Safety: We own a ref-count to the cookie, and so does every - // linked binder. This call gives up our ref-count. The linked - // binders should already have given up their ref-count, or should - // do so shortly. - (self.vtable.cookie_decr_refcount)(self.cookie) + // Safety: We own a ref-count to the cookie, and so does every linked + // binder. This call gives up our ref-count. The linked binders should + // already have given up their ref-count, or should do so shortly. + unsafe { + (self.vtable.cookie_decr_refcount)(self.cookie); } } } @@ -746,11 +726,9 @@ pub trait Proxy: Sized + Interface { fn from_binder(binder: SpIBinder) -> Result; } -/// # Safety -/// -/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow -/// invocation of `IBinder` methods directly from `Interface` objects. It shares -/// the same safety as the implementation for `SpIBinder`. +/// Safety: This is a convenience method that wraps `AsNative` for `SpIBinder` +/// to allow invocation of `IBinder` methods directly from `Interface` objects. +/// It shares the same safety as the implementation for `SpIBinder`. unsafe impl AsNative for T { fn as_native(&self) -> *const sys::AIBinder { self.as_binder().as_native() @@ -765,24 +743,20 @@ unsafe impl AsNative for T { /// exist. pub fn get_service(name: &str) -> Option { let name = CString::new(name).ok()?; - unsafe { - // Safety: `AServiceManager_getService` returns either a null pointer or - // a valid pointer to an owned `AIBinder`. Either of these values is - // safe to pass to `SpIBinder::from_raw`. - SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) - } + // Safety: `AServiceManager_getService` returns either a null pointer or a + // valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) } } /// Retrieve an existing service, or start it if it is configured as a dynamic /// service and isn't yet started. pub fn wait_for_service(name: &str) -> Option { let name = CString::new(name).ok()?; - unsafe { - // Safety: `AServiceManager_waitforService` returns either a null - // pointer or a valid pointer to an owned `AIBinder`. Either of these - // values is safe to pass to `SpIBinder::from_raw`. - SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) - } + // Safety: `AServiceManager_waitforService` returns either a null pointer or + // a valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) } } /// Retrieve an existing service for a particular interface, blocking for a few @@ -801,12 +775,10 @@ pub fn wait_for_interface(name: &str) -> Result Result { let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; - unsafe { - // Safety: `interface` is a valid null-terminated C-style string and is - // only borrowed for the lifetime of the call. The `interface` local - // outlives this call as it lives for the function scope. - Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) - } + // Safety: `interface` is a valid null-terminated C-style string and is only + // borrowed for the lifetime of the call. The `interface` local outlives + // this call as it lives for the function scope. + unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) } } /// Retrieve all declared instances for a particular interface @@ -819,11 +791,13 @@ pub fn get_declared_instances(interface: &str) -> Result> { // CString, and outlives this callback. The null handling here is just // to avoid the possibility of unwinding across C code if this crate is // ever compiled with panic=unwind. - if let Some(instances) = opaque.cast::>().as_mut() { + if let Some(instances) = unsafe { opaque.cast::>().as_mut() } { // Safety: instance is a valid null-terminated C string with a // lifetime at least as long as this function, and we immediately // copy it into an owned CString. - instances.push(CStr::from_ptr(instance).to_owned()); + unsafe { + instances.push(CStr::from_ptr(instance).to_owned()); + } } else { eprintln!("Opaque pointer was null in get_declared_instances callback!"); } @@ -831,10 +805,10 @@ pub fn get_declared_instances(interface: &str) -> Result> { let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; let mut instances: Vec = vec![]; + // Safety: `interface` and `instances` are borrowed for the length of this + // call and both outlive the call. `interface` is guaranteed to be a valid + // null-terminated C-style string. unsafe { - // Safety: `interface` and `instances` are borrowed for the length of - // this call and both outlive the call. `interface` is guaranteed to be - // a valid null-terminated C-style string. sys::AServiceManager_forEachDeclaredInstance( interface.as_ptr(), &mut instances as *mut _ as *mut c_void, @@ -852,10 +826,8 @@ pub fn get_declared_instances(interface: &str) -> Result> { }) } -/// # Safety -/// -/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an -/// `AIBinder`, so we can trivially extract this pointer here. +/// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer +/// to an `AIBinder`, so we can trivially extract this pointer here. unsafe impl AsNative for SpIBinder { fn as_native(&self) -> *const sys::AIBinder { self.0.as_ptr() diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index cc18741a4ec75728c1e474209190e19c92db9b41..a3a2562eb1605dcf3a45c6088765b0eb7b9edcd3 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -22,30 +22,48 @@ use libc::{pid_t, uid_t}; pub struct ProcessState; impl ProcessState { - /// Start the Binder IPC thread pool + /// Starts the Binder IPC thread pool. + /// + /// Starts 1 thread, plus allows the kernel to lazily start up to + /// `num_threads` additional threads as specified by + /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count). + /// + /// This should be done before creating any Binder client or server. If + /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are + /// called, then some things (such as callbacks and + /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently + /// not work: the callbacks will be queued but never called as there is no + /// thread to call them on. pub fn start_thread_pool() { + // Safety: Safe FFI unsafe { - // Safety: Safe FFI sys::ABinderProcess_startThreadPool(); } } - /// Set the maximum number of threads that can be started in the threadpool. + /// Sets the maximum number of threads that can be started in the + /// threadpool. /// - /// By default, after startThreadPool is called, this is 15. If it is called - /// additional times, it will only prevent the kernel from starting new - /// threads and will not delete already existing threads. + /// By default, after [`start_thread_pool`](Self::start_thread_pool) is + /// called, this is 15. If it is called additional times, the thread pool + /// size can only be increased. pub fn set_thread_pool_max_thread_count(num_threads: u32) { + // Safety: Safe FFI unsafe { - // Safety: Safe FFI sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads); } } - /// Block on the Binder IPC thread pool + /// Blocks on the Binder IPC thread pool by adding the current thread to the + /// pool. + /// + /// Note that this adds the current thread in addition to those that are + /// created by + /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count) + /// and [`start_thread_pool`](Self::start_thread_pool). pub fn join_thread_pool() { + // Safety: Safe FFI unsafe { - // Safety: Safe FFI sys::ABinderProcess_joinThreadPool(); } } @@ -68,10 +86,8 @@ impl ThreadState { /// \return calling uid or the current process's UID if this thread isn't /// processing a transaction. pub fn get_calling_uid() -> uid_t { - unsafe { - // Safety: Safe FFI - sys::AIBinder_getCallingUid() - } + // Safety: Safe FFI + unsafe { sys::AIBinder_getCallingUid() } } /// This returns the calling PID assuming that this thread is called from a @@ -93,10 +109,8 @@ impl ThreadState { /// If the transaction being processed is a oneway transaction, then this /// method will return 0. pub fn get_calling_pid() -> pid_t { - unsafe { - // Safety: Safe FFI - sys::AIBinder_getCallingPid() - } + // Safety: Safe FFI + unsafe { sys::AIBinder_getCallingPid() } } /// Determine whether the current thread is currently executing an incoming transaction. @@ -104,10 +118,8 @@ impl ThreadState { /// \return true if the current thread is currently executing an incoming transaction, and false /// otherwise. pub fn is_handling_transaction() -> bool { - unsafe { - // Safety: Safe FFI - sys::AIBinder_isHandlingTransaction() - } + // Safety: Safe FFI + unsafe { sys::AIBinder_isHandlingTransaction() } } /// This function makes the client's security context available to the diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs index 1d1a295221276010c70e66081c7714ad0c2eb5f7..c5c847b87495dc4e6bf63a4c1365398d84907d87 100644 --- a/libs/binder/rust/sys/lib.rs +++ b/libs/binder/rust/sys/lib.rs @@ -19,7 +19,20 @@ use std::error::Error; use std::fmt; -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +#[cfg(not(target_os = "trusty"))] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +// Trusty puts the full path to the auto-generated file in BINDGEN_INC_FILE +// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)] +#[cfg(target_os = "trusty")] +#[allow(bad_style)] +mod bindings { + include!(env!("BINDGEN_INC_FILE")); +} + +pub use bindings::*; impl Error for android_c_interface_StatusCode {} diff --git a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp index 59ca6edf12a44743ea8640f31d6f1262f9549980..663b9bbe37b26477fb64c7af9e4f71a300bd21cb 100644 --- a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp +++ b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp @@ -54,14 +54,12 @@ TEST(RustNdkInterop, NdkCanCallRust) { EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); auto interface = aidl::IBinderRustNdkInteropTest::fromBinder(binder); - // TODO(b/167723746): this test requires that fromBinder allow association - // with an already associated local binder by treating it as remote. - EXPECT_EQ(interface, nullptr); + EXPECT_NE(interface, nullptr); - // std::string in("testing"); - // std::string out; - // EXPECT_TRUE(interface->echo(in, &out).isOk()); - // EXPECT_EQ(in, out); + std::string in("testing"); + std::string out; + EXPECT_TRUE(interface->echo(in, &out).isOk()); + EXPECT_EQ(in, out); } int main(int argc, char** argv) { diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index ca2cedc19d162e1698174f6331a9c80430003163..c87fa89756f7d0d54c0f7e6cec0a89f16184489f 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -26,7 +26,7 @@ use binder::binder_impl::{ use std::convert::{TryFrom, TryInto}; use std::ffi::CStr; -use std::fs::File; +use std::io::Write; use std::sync::Mutex; /// Name of service runner. @@ -118,7 +118,7 @@ impl TryFrom for TestTransactionCode { } impl Interface for TestService { - fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> { + fn dump(&self, _writer: &mut dyn Write, args: &[&CStr]) -> Result<(), StatusCode> { let mut dump_args = self.dump_args.lock().unwrap(); dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned())); Ok(()) @@ -545,6 +545,11 @@ mod tests { } fn get_expected_selinux_context() -> &'static str { + // SAFETY: The pointer we pass to `getcon` is valid because it comes from a reference, and + // `getcon` doesn't retain it after it returns. If `getcon` succeeds then `out_ptr` will + // point to a valid C string, otherwise it will remain null. We check for null, so the + // pointer we pass to `CStr::from_ptr` must be a valid pointer to a C string. There is a + // memory leak as we don't call `freecon`, but that's fine because this is just a test. unsafe { let mut out_ptr = ptr::null_mut(); assert_eq!(selinux_sys::getcon(&mut out_ptr), 0); diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs index 415ede1a7b975f7466e443022a95e98e5a0fa819..fbedfee956782958210950ddf1284b4ebcf952b0 100644 --- a/libs/binder/rust/tests/ndk_rust_interop.rs +++ b/libs/binder/rust/tests/ndk_rust_interop.rs @@ -28,10 +28,11 @@ use std::os::raw::{c_char, c_int}; /// /// # Safety /// -/// service_name must be a valid, non-null C-style string (null-terminated). +/// service_name must be a valid, non-null C-style string (nul-terminated). #[no_mangle] pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int { - let service_name = CStr::from_ptr(service_name).to_str().unwrap(); + // SAFETY: Our caller promises that service_name is a valid C string. + let service_name = unsafe { CStr::from_ptr(service_name) }.to_str().unwrap(); // The Rust class descriptor pointer will not match the NDK one, but the // descriptor strings match so this needs to still associate. @@ -57,7 +58,7 @@ pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int { let wrong_service: Result, StatusCode> = binder::get_interface(service_name); match wrong_service { - Err(e) if e == StatusCode::BAD_TYPE => {} + Err(StatusCode::BAD_TYPE) => {} Err(e) => { eprintln!("Trying to use a service via the wrong interface errored with unexpected error {:?}", e); return e as c_int; @@ -85,10 +86,11 @@ impl IBinderRustNdkInteropTest for Service { /// /// # Safety /// -/// service_name must be a valid, non-null C-style string (null-terminated). +/// service_name must be a valid, non-null C-style string (nul-terminated). #[no_mangle] pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int { - let service_name = CStr::from_ptr(service_name).to_str().unwrap(); + // SAFETY: Our caller promises that service_name is a valid C string. + let service_name = unsafe { CStr::from_ptr(service_name) }.to_str().unwrap(); let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default()); match binder::add_service(service_name, service.as_binder()) { Ok(_) => StatusCode::OK as c_int, diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp index df8a2afb0322006f619f45bfdd5594b3cad44041..6eb707bcf7901a439d2cefa55184e447414daedf 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp @@ -3,25 +3,34 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -rust_fuzz { - name: "parcel_fuzzer_rs", - srcs: [ - "parcel_fuzzer.rs", - ], +rust_defaults { + name: "service_fuzzer_defaults_rs", rustlibs: [ - "libarbitrary", - "libnum_traits", "libbinder_rs", "libbinder_random_parcel_rs", - "binderReadParcelIface-rust", ], - fuzz_config: { cc: [ "waghpawan@google.com", "smoreland@google.com", ], + triage_assignee: "waghpawan@google.com", // hotlist "AIDL fuzzers bugs" on buganizer hotlists: ["4637097"], }, } + +rust_fuzz { + name: "parcel_fuzzer_rs", + srcs: [ + "parcel_fuzzer.rs", + ], + defaults: [ + "service_fuzzer_defaults_rs", + ], + rustlibs: [ + "libarbitrary", + "libnum_traits", + "binderReadParcelIface-rust", + ], +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs index 29bf92cb979fa0252ee8c14680f6101fae3a89a7..ce0f742934e6038d0f99f4fdaf56ce92f973443e 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs +++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs @@ -105,9 +105,9 @@ fn do_read_fuzz(read_operations: Vec, data: &[u8]) { for operation in read_operations { match operation { ReadOperation::SetDataPosition { pos } => { + // Safety: Safe if pos is less than current size of the parcel. + // It relies on C++ code for bound checks unsafe { - // Safety: Safe if pos is less than current size of the parcel. - // It relies on C++ code for bound checks match parcel.set_data_position(pos) { Ok(result) => result, Err(e) => println!("error occurred while setting data position: {:?}", e), diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp index 43a309409d4ce0e753dba2f75abed8562a2b4199..5cac6475cff3a8e6f1c2b04e15be5b1daa617cde 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp @@ -11,7 +11,6 @@ rust_bindgen { source_stem: "bindings", visibility: [":__subpackages__"], bindgen_flags: [ - "--size_t-is-usize", "--allowlist-function", "createRandomParcel", "--allowlist-function", diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp index 5cb406afc24f9f6b7b9834d6be730a4cc6f3721a..84130c17e19131c45b60240c358f787fa7ed432e 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp @@ -19,17 +19,10 @@ rust_fuzz { srcs: [ "service_fuzzer.rs", ], + defaults: [ + "service_fuzzer_defaults_rs", + ], rustlibs: [ - "libbinder_rs", - "libbinder_random_parcel_rs", "testServiceInterface-rust", ], - fuzz_config: { - cc: [ - "waghpawan@google.com", - "smoreland@google.com", - ], - // hotlist "AIDL fuzzers bugs" on buganizer - hotlists: ["4637097"], - }, } diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs index 1bbd6742f239a8c888bf18358da3d5b4ffe259c4..896b78f488058500f1ccbdb625f4b9c10d016641 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs @@ -35,10 +35,26 @@ pub fn create_random_parcel(fuzzer_data: &[u8]) -> Parcel { /// This API automatically fuzzes provided service pub fn fuzz_service(binder: &mut SpIBinder, fuzzer_data: &[u8]) { - let ptr = binder.as_native_mut() as *mut c_void; + let mut binders = [binder]; + fuzz_multiple_services(&mut binders, fuzzer_data); +} + +/// This API automatically fuzzes provided services +pub fn fuzz_multiple_services(binders: &mut [&mut SpIBinder], fuzzer_data: &[u8]) { + let mut cppBinders = vec![]; + for binder in binders.iter_mut() { + let ptr = binder.as_native_mut() as *mut c_void; + cppBinders.push(ptr); + } + unsafe { - // Safety: `SpIBinder::as_native_mut` and `slice::as_ptr` always + // Safety: `Vec::as_mut_ptr` and `slice::as_ptr` always // return valid pointers. - fuzzRustService(ptr, fuzzer_data.as_ptr(), fuzzer_data.len()); + fuzzRustService( + cppBinders.as_mut_ptr(), + cppBinders.len(), + fuzzer_data.as_ptr(), + fuzzer_data.len(), + ); } } diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp index 831bd5660c29fe8dfbaad548200496b85a8dbe82..cfdd2abd05febd6a13b27e992f90f1f3841478aa 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp @@ -21,5 +21,5 @@ extern "C" { void createRandomParcel(void* aParcel, const uint8_t* data, size_t len); // This API is used by fuzzers to automatically fuzz aidl services - void fuzzRustService(void* binder, const uint8_t* data, size_t len); -} \ No newline at end of file + void fuzzRustService(void** binders, size_t numBinders, const uint8_t* data, size_t len); +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs index a2d48b61152e0d40dc1421caa55cac55866f9ab5..2c8d05facdc5a852f9d09ecd0811a0449437a30e 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs +++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs @@ -89,14 +89,17 @@ pub const READ_FUNCS: &[fn(&BorrowedParcel<'_>)] = &[ read_parcel_interface!(Option>), read_parcel_interface!(Option>), read_parcel_interface!(ParcelFileDescriptor), + read_parcel_interface!(Vec), read_parcel_interface!(Vec>), read_parcel_interface!(Option>), read_parcel_interface!(Option>>), read_parcel_interface!(SpIBinder), + read_parcel_interface!(Vec), read_parcel_interface!(Vec>), read_parcel_interface!(Option>), read_parcel_interface!(Option>>), read_parcel_interface!(SomeParcelable), + read_parcel_interface!(Vec), read_parcel_interface!(Vec>), read_parcel_interface!(Option>), read_parcel_interface!(Option>>), diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp index 3f59dab3a97a6e83230f5b39220b7e772962b899..0cdf8c56abd7851049cae49e3f554cbd3eaa0ef6 100644 --- a/libs/binder/rust/tests/serialization.cpp +++ b/libs/binder/rust/tests/serialization.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include "serialization.hpp" +#include "../../FdUtils.h" +#include "../../tests/FileUtils.h" + #include #include #include @@ -24,8 +28,6 @@ #include #include #include -#include "android-base/file.h" -#include "serialization.hpp" #include #include @@ -34,7 +36,7 @@ using namespace std; using namespace android; -using android::base::unique_fd; +using android::binder::unique_fd; using android::os::ParcelFileDescriptor; // defined in Rust @@ -349,7 +351,7 @@ TEST_F(SerializationTest, SerializeString) { TEST_F(SerializationTest, SerializeFileDescriptor) { unique_fd out_file, in_file; - ASSERT_TRUE(base::Pipe(&out_file, &in_file)); + ASSERT_TRUE(binder::Pipe(&out_file, &in_file)); vector file_descriptors; file_descriptors.push_back(ParcelFileDescriptor(std::move(out_file))); diff --git a/libs/binder/rust/tests/serialization.hpp b/libs/binder/rust/tests/serialization.hpp index 0041608ae0133655831a482d04579d28ce160259..9edcd6d9b6979adc2ef68ebe47747c82ac3fc2bf 100644 --- a/libs/binder/rust/tests/serialization.hpp +++ b/libs/binder/rust/tests/serialization.hpp @@ -14,7 +14,10 @@ * limitations under the License. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpragma-once-outside-header" #pragma once +#pragma clang diagnostic pop #include diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index 6220db4b2807541d02d49cb4e72596c74785d844..2b6c282530df14f0d1763865c633d93c04d60025 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -26,7 +26,7 @@ use binder::{ use binder::binder_impl::{Binder, BorrowedParcel, TransactionCode}; use std::ffi::{c_void, CStr, CString}; -use std::sync::Once; +use std::sync::OnceLock; #[allow( non_camel_case_types, @@ -70,20 +70,18 @@ macro_rules! assert { }; } -static SERVICE_ONCE: Once = Once::new(); -static mut SERVICE: Option = None; +static SERVICE: OnceLock = OnceLock::new(); /// Start binder service and return a raw AIBinder pointer to it. /// /// Safe to call multiple times, only creates the service once. #[no_mangle] pub extern "C" fn rust_service() -> *mut c_void { - unsafe { - SERVICE_ONCE.call_once(|| { - SERVICE = Some(BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder()); - }); - SERVICE.as_ref().unwrap().as_raw().cast() - } + let service = SERVICE + .get_or_init(|| BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder()); + // SAFETY: The SpIBinder will remain alive as long as the program is running because it is in + // the static SERVICE, so the pointer is valid forever. + unsafe { service.as_raw().cast() } } /// Empty interface just to use the declare_binder_interface macro @@ -113,11 +111,13 @@ fn on_transact( bindings::Transaction_TEST_BOOL => { assert!(parcel.read::()?); assert!(!parcel.read::()?); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_BOOL }); assert_eq!(parcel.read::>>()?, None); reply.write(&true)?; reply.write(&false)?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?; reply.write(&(None as Option>))?; } @@ -125,14 +125,18 @@ fn on_transact( assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, i8::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I8 }); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_U8 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i8)?; reply.write(&1i8)?; reply.write(&i8::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?; reply.write(&(None as Option>))?; } @@ -140,12 +144,14 @@ fn on_transact( assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, u16::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::>>()?, None); reply.write(&0u16)?; reply.write(&1u16)?; reply.write(&u16::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?; reply.write(&(None as Option>))?; } @@ -153,12 +159,14 @@ fn on_transact( assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, i32::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i32)?; reply.write(&1i32)?; reply.write(&i32::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?; reply.write(&(None as Option>))?; } @@ -166,12 +174,14 @@ fn on_transact( assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, i64::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i64)?; reply.write(&1i64)?; reply.write(&i64::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?; reply.write(&(None as Option>))?; } @@ -179,12 +189,14 @@ fn on_transact( assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, u64::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0u64)?; reply.write(&1u64)?; reply.write(&u64::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?; reply.write(&(None as Option>))?; } @@ -192,10 +204,12 @@ fn on_transact( assert_eq!(parcel.read::()?, 0f32); let floats = parcel.read::>()?; assert!(floats[0].is_nan()); + // SAFETY: Just reading an extern constant. assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]); assert_eq!(parcel.read::>>()?, None); reply.write(&0f32)?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?; reply.write(&(None as Option>))?; } @@ -203,10 +217,12 @@ fn on_transact( assert_eq!(parcel.read::()?, 0f64); let doubles = parcel.read::>()?; assert!(doubles[0].is_nan()); + // SAFETY: Just reading an extern constant. assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]); assert_eq!(parcel.read::>>()?, None); reply.write(&0f64)?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?; reply.write(&(None as Option>))?; } @@ -216,14 +232,17 @@ fn on_transact( let s: Option = parcel.read()?; assert_eq!(s, None); let s: Option>> = parcel.read()?; + // SAFETY: Just reading an extern constant. for (s, expected) in s.unwrap().iter().zip(unsafe { bindings::TESTDATA_STRS }.iter()) { let expected = + // SAFETY: Just reading an extern constant. unsafe { expected.as_ref().and_then(|e| CStr::from_ptr(e).to_str().ok()) }; assert_eq!(s.as_deref(), expected); } let s: Option>> = parcel.read()?; assert_eq!(s, None); + // SAFETY: Just reading an extern constant. let strings: Vec> = unsafe { bindings::TESTDATA_STRS .iter() @@ -258,8 +277,7 @@ fn on_transact( assert!(ibinders[1].is_none()); assert!(parcel.read::>>>()?.is_none()); - let service = - unsafe { SERVICE.as_ref().expect("Global binder service not initialized").clone() }; + let service = SERVICE.get().expect("Global binder service not initialized").clone(); reply.write(&service)?; reply.write(&(None as Option<&SpIBinder>))?; reply.write(&[Some(&service), None][..])?; diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp index 692cc95e3b16019b540fe873f8677de58c1b379e..18b178b9b1b089d03d5101cccce72af617d712bb 100644 --- a/libs/binder/servicedispatcher.cpp +++ b/libs/binder/servicedispatcher.cpp @@ -17,12 +17,11 @@ #include #include +#include #include -#include #include #include -#include #include #include #include @@ -31,6 +30,8 @@ #include #include +#include "file.h" + using android::BBinder; using android::defaultServiceManager; using android::OK; @@ -39,14 +40,12 @@ using android::sp; using android::status_t; using android::statusToString; using android::String16; -using android::base::Basename; using android::base::GetBoolProperty; using android::base::InitLogging; using android::base::LogdLogger; using android::base::LogId; using android::base::LogSeverity; using android::base::StdioLogger; -using android::base::StringPrintf; using std::string_view_literals::operator""sv; namespace { @@ -55,26 +54,29 @@ const char* kLocalInetAddress = "127.0.0.1"; using ServiceRetriever = decltype(&android::IServiceManager::checkService); using android::debug::IAdbManager; -int Usage(const char* program) { - auto basename = Basename(program); - auto format = R"(dispatch calls to RPC service. +int Usage(std::filesystem::path program) { + auto basename = program.filename(); + // clang-format off + LOG(ERROR) << R"(dispatch calls to RPC service. Usage: - %s [-g] + )" << basename << R"( [-g] [-i ] : the service to connect to. - %s [-g] manager + )" << basename << R"( [-g] manager Runs an RPC-friendly service that redirects calls to servicemanager. -g: use getService() instead of checkService(). + -i: use ip_address when setting up the server instead of '127.0.0.1' If successful, writes port number and a new line character to stdout, and blocks until killed. Otherwise, writes error message to stderr and exits with non-zero code. )"; - LOG(ERROR) << StringPrintf(format, basename.c_str(), basename.c_str()); + // clang-format on return EX_USAGE; } -int Dispatch(const char* name, const ServiceRetriever& serviceRetriever) { +int Dispatch(const char* name, const ServiceRetriever& serviceRetriever, + const char* ip_address = kLocalInetAddress) { auto sm = defaultServiceManager(); if (nullptr == sm) { LOG(ERROR) << "No servicemanager"; @@ -91,7 +93,7 @@ int Dispatch(const char* name, const ServiceRetriever& serviceRetriever) { return EX_SOFTWARE; } unsigned int port; - if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) { + if (status_t status = rpcServer->setupInetServer(ip_address, 0, &port); status != OK) { LOG(ERROR) << "setupInetServer failed: " << statusToString(status); return EX_SOFTWARE; } @@ -188,7 +190,8 @@ private: // Workaround for b/191059588. // TODO(b/191059588): Once we can run RpcServer on single-threaded services, // `servicedispatcher manager` should call Dispatch("manager") directly. -int wrapServiceManager(const ServiceRetriever& serviceRetriever) { +int wrapServiceManager(const ServiceRetriever& serviceRetriever, + const char* ip_address = kLocalInetAddress) { auto sm = defaultServiceManager(); if (nullptr == sm) { LOG(ERROR) << "No servicemanager"; @@ -212,7 +215,7 @@ int wrapServiceManager(const ServiceRetriever& serviceRetriever) { auto rpcServer = RpcServer::make(); rpcServer->setRootObject(service); unsigned int port; - if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) { + if (status_t status = rpcServer->setupInetServer(ip_address, 0, &port); status != OK) { LOG(ERROR) << "Unable to set up inet server: " << statusToString(status); return EX_SOFTWARE; } @@ -251,7 +254,8 @@ public: mLogdLogger(id, severity, tag, file, line, message); if (severity >= LogSeverity::WARNING) { std::cout << std::flush; - std::cerr << Basename(getprogname()) << ": " << message << std::endl; + auto progname = std::filesystem::path(getprogname()).filename(); + std::cerr << progname << ": " << message << std::endl; } } @@ -272,10 +276,17 @@ int main(int argc, char* argv[]) { int opt; ServiceRetriever serviceRetriever = &android::IServiceManager::checkService; - while (-1 != (opt = getopt(argc, argv, "g"))) { + char* ip_address = nullptr; + while (-1 != (opt = getopt(argc, argv, "gi:"))) { switch (opt) { case 'g': { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" serviceRetriever = &android::IServiceManager::getService; +#pragma clang diagnostic pop + } break; + case 'i': { + ip_address = optarg; } break; default: { return Usage(argv[0]); @@ -291,7 +302,15 @@ int main(int argc, char* argv[]) { auto name = argv[optind]; if (name == "manager"sv) { - return wrapServiceManager(serviceRetriever); + if (ip_address) { + return wrapServiceManager(serviceRetriever, ip_address); + } else { + return wrapServiceManager(serviceRetriever); + } + } + if (ip_address) { + return Dispatch(name, serviceRetriever, ip_address); + } else { + return Dispatch(name, serviceRetriever); } - return Dispatch(name, serviceRetriever); } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 873e9550f95e2b78cfcbc92c7c112c6548999f4a..e4d4de86bcb39cc24f6a3fc11e94879ede54ffc3 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -31,29 +31,9 @@ cc_defaults { ], } -cc_test { - name: "binderDriverInterfaceTest_IPC_32", - defaults: ["binder_test_defaults"], - srcs: ["binderDriverInterfaceTest.cpp"], - header_libs: ["libbinder_headers"], - compile_multilib: "32", - multilib: { - lib32: { - suffix: "", - }, - }, - cflags: ["-DBINDER_IPC_32BIT=1"], - test_suites: ["vts"], -} - cc_test { name: "binderDriverInterfaceTest", defaults: ["binder_test_defaults"], - product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], - }, - }, header_libs: ["libbinder_headers"], srcs: ["binderDriverInterfaceTest.cpp"], test_suites: [ @@ -62,30 +42,6 @@ cc_test { ], } -cc_test { - name: "binderLibTest_IPC_32", - defaults: ["binder_test_defaults"], - srcs: ["binderLibTest.cpp"], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libutils", - ], - static_libs: [ - "libgmock", - ], - compile_multilib: "32", - multilib: { - lib32: { - suffix: "", - }, - }, - cflags: ["-DBINDER_IPC_32BIT=1"], - test_suites: ["vts"], - require_root: true, -} - // unit test only, which can run on host and doesn't use /dev/binder cc_test { name: "binderUnitTest", @@ -111,13 +67,41 @@ cc_test { } cc_test { - name: "binderLibTest", - defaults: ["binder_test_defaults"], - product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], + name: "binderRecordReplayTest", + srcs: ["binderRecordReplayTest.cpp"], + shared_libs: [ + "libbinder", + "libcutils", + "libutils", + ], + static_libs: [ + "binderRecordReplayTestIface-cpp", + "binderReadParcelIface-cpp", + "libbinder_random_parcel_seeds", + "libbinder_random_parcel", + ], + test_suites: ["general-tests"], + require_root: true, +} + +aidl_interface { + name: "binderRecordReplayTestIface", + unstable: true, + srcs: [ + "IBinderRecordReplayTest.aidl", + ], + imports: ["binderReadParcelIface"], + backend: { + java: { + enabled: true, + platform_apis: true, }, }, +} + +cc_test { + name: "binderLibTest", + defaults: ["binder_test_defaults"], srcs: ["binderLibTest.cpp"], shared_libs: [ @@ -150,6 +134,10 @@ aidl_interface { "IBinderRpcTest.aidl", "ParcelableCertificateData.aidl", ], + flags: [ + "-Werror", + "-Wno-mixed-oneway", + ], backend: { java: { enabled: false, @@ -188,6 +176,30 @@ cc_library_static { ], } +cc_library_static { + name: "libbinder_test_utils", + host_supported: true, + vendor_available: true, + target: { + darwin: { + enabled: false, + }, + }, + defaults: [ + "binder_test_defaults", + ], + shared_libs: [ + "libbase", + "liblog", + ], + srcs: [ + "FileUtils.cpp", + ], + visibility: [ + ":__subpackages__", + ], +} + cc_defaults { name: "binderRpcTest_common_defaults", host_supported: true, @@ -201,6 +213,7 @@ cc_defaults { ], static_libs: [ + "libbinder_test_utils", "libbinder_tls_static", "libbinder_tls_test_utils", "binderRpcTestIface-cpp", @@ -716,6 +729,7 @@ cc_benchmark { "liblog", "libutils", ], + test_suites: ["general-tests"], } cc_test_host { @@ -818,3 +832,15 @@ cc_defaults { hotlists: ["4637097"], }, } + +cc_defaults { + name: "fuzzer_disable_leaks", + fuzz_config: { + asan_options: [ + "detect_leaks=0", + ], + hwasan_options: [ + "detect_leaks=0", + ], + }, +} diff --git a/libs/binder/tests/FileUtils.cpp b/libs/binder/tests/FileUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..61509fe18eaec2ffcbfb2b75118993fdf8c9c002 --- /dev/null +++ b/libs/binder/tests/FileUtils.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 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 "FileUtils.h" + +#ifdef BINDER_NO_LIBBASE + +#include +#include + +#if defined(__APPLE__) +#include +#endif +#if defined(_WIN32) +#include +#include +#endif + +namespace android::binder { + +bool ReadFdToString(borrowed_fd fd, std::string* content) { + content->clear(); + + // Although original we had small files in mind, this code gets used for + // very large files too, where the std::string growth heuristics might not + // be suitable. https://code.google.com/p/android/issues/detail?id=258500. + struct stat sb; + if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) { + content->reserve(sb.st_size); + } + + char buf[4096] __attribute__((__uninitialized__)); + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) { + content->append(buf, n); + } + return (n == 0) ? true : false; +} + +bool WriteStringToFd(std::string_view content, borrowed_fd fd) { + const char* p = content.data(); + size_t left = content.size(); + while (left > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left)); + if (n == -1) { + return false; + } + p += n; + left -= n; + } + return true; +} + +static std::filesystem::path GetExecutablePath2() { +#if defined(__linux__) + return std::filesystem::read_symlink("/proc/self/exe"); +#elif defined(__APPLE__) + char path[PATH_MAX + 1]; + uint32_t path_len = sizeof(path); + int rc = _NSGetExecutablePath(path, &path_len); + if (rc < 0) { + std::unique_ptr path_buf(new char[path_len]); + _NSGetExecutablePath(path_buf.get(), &path_len); + return path_buf.get(); + } + return path; +#elif defined(_WIN32) + char path[PATH_MAX + 1]; + DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1); + if (result == 0 || result == sizeof(path) - 1) return ""; + path[PATH_MAX - 1] = 0; + return path; +#elif defined(__EMSCRIPTEN__) + abort(); +#else +#error unknown OS +#endif +} + +std::string GetExecutableDirectory() { + return GetExecutablePath2().parent_path(); +} + +} // namespace android::binder + +#endif // BINDER_NO_LIBBASE diff --git a/libs/binder/tests/FileUtils.h b/libs/binder/tests/FileUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..2cbe5e7f25c988d95ec19118d3b74cd54cd50f55 --- /dev/null +++ b/libs/binder/tests/FileUtils.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 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 "../file.h" + +#ifndef BINDER_NO_LIBBASE + +namespace android::binder { +using android::base::GetExecutableDirectory; +using android::base::ReadFdToString; +using android::base::WriteStringToFd; +} // namespace android::binder + +#else // BINDER_NO_LIBBASE + +#include + +#include + +#if !defined(_WIN32) && !defined(O_BINARY) +/** Windows needs O_BINARY, but Unix never mangles line endings. */ +#define O_BINARY 0 +#endif + +namespace android::binder { + +bool ReadFdToString(borrowed_fd fd, std::string* content); +bool WriteStringToFd(std::string_view content, borrowed_fd fd); + +std::string GetExecutableDirectory(); + +} // namespace android::binder + +#endif // BINDER_NO_LIBBASE diff --git a/libs/binder/tests/IBinderRecordReplayTest.aidl b/libs/binder/tests/IBinderRecordReplayTest.aidl new file mode 100644 index 0000000000000000000000000000000000000000..bd6b03c6e0637b78f425447580207f43a3d91796 --- /dev/null +++ b/libs/binder/tests/IBinderRecordReplayTest.aidl @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 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. + */ +import parcelables.SingleDataParcelable; + +interface IBinderRecordReplayTest { + void setByte(byte input); + byte getByte(); + + void setChar(char input); + char getChar(); + + void setBoolean(boolean input); + boolean getBoolean(); + + void setInt(int input); + int getInt(); + + void setFloat(float input); + float getFloat(); + + void setLong(long input); + long getLong(); + + void setDouble(double input); + double getDouble(); + + void setString(String input); + String getString(); + + void setSingleDataParcelable(in SingleDataParcelable p); + SingleDataParcelable getSingleDataParcelable(); + + void setByteArray(in byte[] input); + byte[] getByteArray(); + + void setCharArray(in char[] input); + char[] getCharArray(); + + void setBooleanArray(in boolean[] input); + boolean[] getBooleanArray(); + + void setIntArray(in int[] input); + int[] getIntArray(); + + void setFloatArray(in float[] input); + float[] getFloatArray(); + + void setLongArray(in long[] input); + long[] getLongArray(); + + void setDoubleArray(in double[] input); + double[] getDoubleArray(); + + void setStringArray(in String[] input); + String[] getStringArray(); + + void setSingleDataParcelableArray(in SingleDataParcelable[] input); + SingleDataParcelable[] getSingleDataParcelableArray(); +} diff --git a/libs/binder/tests/IBinderRpcBenchmark.aidl b/libs/binder/tests/IBinderRpcBenchmark.aidl index 2baf68097a448e22db93ecf86d5f327bb149519c..100877847df7e83ef006f068dc4b77b320c9eaad 100644 --- a/libs/binder/tests/IBinderRpcBenchmark.aidl +++ b/libs/binder/tests/IBinderRpcBenchmark.aidl @@ -18,4 +18,7 @@ interface IBinderRpcBenchmark { @utf8InCpp String repeatString(@utf8InCpp String str); IBinder repeatBinder(IBinder binder); byte[] repeatBytes(in byte[] bytes); + + IBinder gimmeBinder(); + void waitGimmesDestroyed(); } diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h deleted file mode 100644 index 369b55dc222ff86694a478b54d0cf89c6d5bdd71..0000000000000000000000000000000000000000 --- a/libs/binder/tests/binderAbiHelper.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2020 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 -#include - -#ifdef BINDER_IPC_32BIT -static constexpr bool kBuild32Abi = true; -#else -static constexpr bool kBuild32Abi = false; -#endif - -// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported -static inline bool ReadKernelConfigIs32BitAbi() { - // failure case implies we run with standard ABI - return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\""); -} - -static inline void ExitIfWrongAbi() { - bool runtime32Abi = ReadKernelConfigIs32BitAbi(); - - if (kBuild32Abi != runtime32Abi) { - std::cout << "[==========] Running 1 test from 1 test suite." << std::endl; - std::cout << "[----------] Global test environment set-up." << std::endl; - std::cout << "[----------] 1 tests from BinderLibTest" << std::endl; - std::cout << "[ RUN ] BinderTest.AbortForWrongAbi" << std::endl; - std::cout << "[ INFO ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl; - std::cout << "[ OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl; - std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl; - std::cout << "" << std::endl; - std::cout << "[----------] Global test environment tear-down" << std::endl; - std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl; - std::cout << "[ PASSED ] 1 tests." << std::endl; - exit(0); - } -} - diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index bc4086402039024743d5f0fd0d46b0de86dc3815..7e0b59463aeebacfaf74613f4923ba679776565d 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,8 @@ #include #include +using namespace android::binder::impl; + static android::String8 gEmpty(""); // make sure first allocation from optimization runs struct DestructionAction { @@ -172,6 +175,18 @@ TEST(BinderAllocation, PingTransaction) { a_binder->pingBinder(); } +TEST(BinderAllocation, MakeScopeGuard) { + const auto m = ScopeDisallowMalloc(); + { + auto guard1 = make_scope_guard([] {}); + guard1.release(); + + auto guard2 = make_scope_guard([&guard1, ptr = imaginary_use] { + if (ptr == nullptr) guard1.release(); + }); + } +} + TEST(BinderAllocation, InterfaceDescriptorTransaction) { sp a_binder = GetRemoteBinder(); @@ -216,16 +231,16 @@ TEST(RpcBinderAllocation, SetupRpcServer) { auto server = RpcServer::make(); server->setRootObject(sp::make()); - CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())); + ASSERT_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(); + status_t status = session->setupUnixDomainClient(addr.c_str()); + ASSERT_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str(); auto remoteBinder = session->getRootObject(); + ASSERT_NE(remoteBinder, nullptr); size_t mallocs = 0, totalBytes = 0; { @@ -233,7 +248,7 @@ TEST(RpcBinderAllocation, SetupRpcServer) { mallocs++; totalBytes += bytes; }); - CHECK_EQ(OK, remoteBinder->pingBinder()); + ASSERT_EQ(OK, remoteBinder->pingBinder()); } EXPECT_EQ(mallocs, 1); EXPECT_EQ(totalBytes, 40); diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp index 307151c7de294d8ce3d2effcbcb789a0b2960e14..e43ee5fcf5f3ad8f2d81428613a22ae85dcf0075 100644 --- a/libs/binder/tests/binderClearBufTest.cpp +++ b/libs/binder/tests/binderClearBufTest.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include #include #include #include @@ -24,6 +23,8 @@ #include #include +#include "../Utils.h" + #include #include @@ -68,13 +69,16 @@ class FooBar : public BBinder { lastReply = reply.data(); lastReplySize = reply.dataSize(); } - *outBuffer = android::base::HexString(lastReply, lastReplySize); + *outBuffer = android::HexString(lastReply, lastReplySize); return result; } }; TEST(BinderClearBuf, ClearKernelBuffer) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp binder = defaultServiceManager()->getService(kServerName); +#pragma clang diagnostic pop ASSERT_NE(nullptr, binder); std::string replyBuffer; diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index 8cc3054f80dda180a55c3609dfb13a8fabb2bd08..cf23a4658c5dd86ee86f697c915d1c9aac1688a5 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp @@ -25,8 +25,6 @@ #include #include -#include "binderAbiHelper.h" - #define BINDER_DEV_NAME "/dev/binder" testing::Environment* binder_env; @@ -362,8 +360,7 @@ TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) { binderTestReadEmpty(); } -int main(int argc, char **argv) { - ExitIfWrongAbi(); +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv()); diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp index 77a5fa8d652fcfd070dc454109a98caf37c7efdb..0ae536c6bc79f1214078154ea9b23d96c34ec66d 100644 --- a/libs/binder/tests/binderHostDeviceTest.cpp +++ b/libs/binder/tests/binderHostDeviceTest.cpp @@ -75,7 +75,7 @@ class HostDeviceTest : public ::testing::Test { public: void SetUp() override { auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr); - ASSERT_THAT(debuggableResult, Ok()); + ASSERT_TRUE(debuggableResult.has_value()); ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult; auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr)); ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr); @@ -84,7 +84,7 @@ public: } auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr); - ASSERT_THAT(lsResult, Ok()); + ASSERT_TRUE(lsResult.has_value()); if (lsResult->exitCode != 0) { GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices " "without servicedispatcher"; @@ -95,7 +95,7 @@ public: auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor}, &CommandResult::stdoutEndsWithNewLine); - ASSERT_THAT(service, Ok()); + ASSERT_TRUE(service.has_value()); ASSERT_EQ(std::nullopt, service->exitCode) << *service; mService = std::move(*service); } @@ -135,7 +135,10 @@ TEST_F(HostDeviceTest, CheckService) { TEST_F(HostDeviceTest, GetService) { auto sm = defaultServiceManager(); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" auto rpcBinder = sm->getService(String16(kServiceName)); +#pragma clang diagnostic pop ASSERT_NE(nullptr, rpcBinder); EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK)); diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 8974ad745d704e0f9e322782aa06ce160694cc2a..cb1a1ee443cd8e554e92c72249ddda2910dde279 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -29,17 +29,17 @@ #include #include -#include -#include #include -#include #include #include +#include #include #include #include #include #include +#include +#include #include #include @@ -48,15 +48,16 @@ #include #include "../binder_module.h" -#include "binderAbiHelper.h" #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; +using namespace android::binder::impl; using namespace std::string_literals; using namespace std::chrono_literals; using android::base::testing::HasValue; using android::base::testing::Ok; +using android::binder::unique_fd; using testing::ExplainMatchResult; using testing::Matcher; using testing::Not; @@ -83,7 +84,7 @@ static char binderserverarg[] = "--binderserver"; static constexpr int kSchedPolicy = SCHED_RR; static constexpr int kSchedPriority = 7; static constexpr int kSchedPriorityMore = 8; -static constexpr int kKernelThreads = 15; +static constexpr int kKernelThreads = 17; // anything different than the default static String16 binderLibTestServiceName = String16("test.binderLib"); @@ -214,7 +215,10 @@ class BinderLibTestEnv : public ::testing::Environment { sp sm = defaultServiceManager(); //printf("%s: pid %d, get service\n", __func__, m_pid); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" m_server = sm->getService(binderLibTestServiceName); +#pragma clang diagnostic pop ASSERT_TRUE(m_server != nullptr); //printf("%s: pid %d, get service done\n", __func__, m_pid); } @@ -844,7 +848,7 @@ TEST_F(BinderLibTest, PassParcelFileDescriptor) { writebuf[i] = i; } - android::base::unique_fd read_end, write_end; + unique_fd read_end, write_end; { int pipefd[2]; ASSERT_EQ(0, pipe2(pipefd, O_NONBLOCK)); @@ -1108,6 +1112,7 @@ TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) status_t ret; data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(NO_ERROR, ret); Parcel data2, reply2; status_t ret2; @@ -1173,7 +1178,7 @@ TEST_F(BinderLibTest, FileDescriptorRemainsNonBlocking) { Parcel reply; EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_NON_BLOCKING_FD, {} /*data*/, &reply), StatusEq(NO_ERROR)); - base::unique_fd fd; + unique_fd fd; EXPECT_THAT(reply.readUniqueFileDescriptor(&fd), StatusEq(OK)); const int result = fcntl(fd.get(), F_GETFL); @@ -1322,7 +1327,7 @@ TEST_F(BinderLibTest, TooManyFdsFlattenable) { ASSERT_EQ(0, ret); // Restore the original file limits when the test finishes - base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + auto guardUnguard = make_scope_guard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); rlimit testNofile = {1024, 1024}; ret = setrlimit(RLIMIT_NOFILE, &testNofile); @@ -1358,17 +1363,20 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { 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); + // see getThreadPoolMaxTotalThreadCount for why there is a race + EXPECT_TRUE(replyi == kKernelThreads + 1 || replyi == kKernelThreads + 2) << replyi; + 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). + * This will use all threads in the pool but one. There are actually kKernelThreads+2 + * available in the other process (startThreadPool, joinThreadPool, + the kernel- + * started threads from setThreadPoolMaxThreadCount + * + * Adding one more will cause it to deadlock. */ std::vector ts; - for (size_t i = 0; i < kKernelThreads; i++) { + 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), @@ -1376,8 +1384,13 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { })); } - data.writeInt32(500); - // Give a chance for all threads to be used + // make sure all of the above calls will be queued in parallel. Otherwise, most of + // the time, the below call will pre-empt them (presumably because we have the + // scheduler timeslice already + scheduler hint). + sleep(1); + + data.writeInt32(1000); + // Give a chance for all threads to be used (kKernelThreads + 1 thread in use) EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR); for (auto &t : ts) { @@ -1387,7 +1400,7 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), StatusEq(NO_ERROR)); replyi = reply.readInt32(); - EXPECT_EQ(replyi, kKernelThreads + 1); + EXPECT_EQ(replyi, kKernelThreads + 2); } TEST_F(BinderLibTest, ThreadPoolStarted) { @@ -1434,6 +1447,36 @@ TEST_F(BinderLibTest, HangingServices) { EXPECT_GE(epochMsAfter, epochMsBefore + delay); } +TEST_F(BinderLibTest, BinderProxyCount) { + Parcel data, reply; + sp server = addServer(); + ASSERT_NE(server, nullptr); + + uint32_t initialCount = BpBinder::getBinderProxyCount(); + size_t iterations = 100; + { + uint32_t count = initialCount; + std::vector > proxies; + sp proxy; + // Create binder proxies and verify the count. + for (size_t i = 0; i < iterations; i++) { + ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); + proxies.push_back(reply.readStrongBinder()); + EXPECT_EQ(BpBinder::getBinderProxyCount(), ++count); + } + // Remove every other one and verify the count. + auto it = proxies.begin(); + for (size_t i = 0; it != proxies.end(); i++) { + if (i % 2 == 0) { + it = proxies.erase(it); + EXPECT_EQ(BpBinder::getBinderProxyCount(), --count); + } + } + } + EXPECT_EQ(BpBinder::getBinderProxyCount(), initialCount); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { @@ -1444,7 +1487,7 @@ public: BinderLibTest::SetUp(); } - std::tuple CreateSocket() { + std::tuple CreateSocket() { auto rpcServer = RpcServer::make(); EXPECT_NE(nullptr, rpcServer); if (rpcServer == nullptr) return {}; @@ -1511,7 +1554,7 @@ public: TEST_P(BinderLibRpcTestP, SetRpcClientDebugNoFd) { auto binder = GetService(); ASSERT_TRUE(binder != nullptr); - EXPECT_THAT(binder->setRpcClientDebug(android::base::unique_fd(), sp::make()), + EXPECT_THAT(binder->setRpcClientDebug(unique_fd(), sp::make()), Debuggable(StatusEq(BAD_VALUE))); } @@ -1558,9 +1601,8 @@ public: } switch (code) { case BINDER_LIB_TEST_REGISTER_SERVER: { - int32_t id; sp binder; - id = data.readInt32(); + /*id =*/data.readInt32(); binder = data.readStrongBinder(); if (binder == nullptr) { return BAD_VALUE; @@ -1782,7 +1824,7 @@ public: int ret; int32_t size; const void *buf; - android::base::unique_fd fd; + unique_fd fd; ret = data.readUniqueParcelFileDescriptor(&fd); if (ret != NO_ERROR) { @@ -1847,7 +1889,7 @@ public: ALOGE("Could not make socket non-blocking: %s", strerror(errno)); return UNKNOWN_ERROR; } - base::unique_fd out(sockets[0]); + unique_fd out(sockets[0]); status_t writeResult = reply->writeUniqueFileDescriptor(out); if (writeResult != NO_ERROR) { ALOGE("Could not write unique_fd"); @@ -1956,7 +1998,10 @@ int run_server(int index, int readypipefd, bool usePoll) if (index == 0) { ret = sm->addService(binderLibTestServiceName, testService); } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp server = sm->getService(binderLibTestServiceName); +#pragma clang diagnostic pop Parcel data, reply; data.writeInt32(index); data.writeStrongBinder(testService); @@ -2022,9 +2067,7 @@ int run_server(int index, int readypipefd, bool usePoll) return 1; /* joinThreadPool should not return */ } -int main(int argc, char **argv) { - ExitIfWrongAbi(); - +int main(int argc, char** argv) { if (argc == 4 && !strcmp(argv[1], "--servername")) { binderservername = argv[2]; } else { diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index 359c783de510503e4d2d85dd10225232712a4e10..34fc43f9261ea731dd15df52225e0d0af479261c 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -30,6 +30,7 @@ using android::status_t; using android::String16; using android::String8; using android::binder::Status; +using android::binder::unique_fd; TEST(Parcel, NonNullTerminatedString8) { String8 kTestString = String8("test-is-good"); @@ -112,6 +113,166 @@ TEST(Parcel, DebugReadAllFds) { EXPECT_EQ(ret[1], STDIN_FILENO); } +TEST(Parcel, AppendFromEmpty) { + Parcel p1; + Parcel p2; + p2.writeInt32(2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(2, p1.readInt32()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); +} + +TEST(Parcel, AppendPlainData) { + Parcel p1; + p1.writeInt32(1); + Parcel p2; + p2.writeInt32(2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(2, p1.readInt32()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); +} + +TEST(Parcel, AppendPlainDataPartial) { + Parcel p1; + p1.writeInt32(1); + Parcel p2; + p2.writeInt32(2); + p2.writeInt32(3); + p2.writeInt32(4); + + // only copy 8 bytes (two int32's worth) + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(3, p1.readInt32()); + ASSERT_EQ(0, p1.readInt32()); // not 4, end of Parcel + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); +} + +TEST(Parcel, AppendWithBinder) { + sp b1 = sp::make(); + sp b2 = sp::make(); + + Parcel p1; + p1.writeInt32(1); + p1.writeStrongBinder(b1); + Parcel p2; + p2.writeInt32(2); + p2.writeStrongBinder(b2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(b1, p1.readStrongBinder()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(b2, p1.readStrongBinder()); + ASSERT_EQ(2, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_EQ(b2, p2.readStrongBinder()); +} + +TEST(Parcel, AppendWithBinderPartial) { + sp b1 = sp::make(); + sp b2 = sp::make(); + + Parcel p1; + p1.writeInt32(1); + p1.writeStrongBinder(b1); + Parcel p2; + p2.writeInt32(2); + p2.writeStrongBinder(b2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into strong binder + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(b1, p1.readStrongBinder()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(1935813253, p1.readInt32()); // whatever garbage that is there (ABI) + ASSERT_EQ(1, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_EQ(b2, p2.readStrongBinder()); +} + +TEST(Parcel, AppendWithFd) { + unique_fd fd1 = unique_fd(dup(0)); + unique_fd fd2 = unique_fd(dup(0)); + + Parcel p1; + p1.writeInt32(1); + p1.writeDupFileDescriptor(0); // with ownership + p1.writeFileDescriptor(fd1.get()); // without ownership + Parcel p2; + p2.writeInt32(2); + p2.writeDupFileDescriptor(0); // with ownership + p2.writeFileDescriptor(fd2.get()); // without ownership + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_EQ(4, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); +} + +TEST(Parcel, AppendWithFdPartial) { + unique_fd fd1 = unique_fd(dup(0)); + unique_fd fd2 = unique_fd(dup(0)); + + Parcel p1; + p1.writeInt32(1); + p1.writeDupFileDescriptor(0); // with ownership + p1.writeFileDescriptor(fd1.get()); // without ownership + Parcel p2; + p2.writeInt32(2); + p2.writeDupFileDescriptor(0); // with ownership + p2.writeFileDescriptor(fd2.get()); // without ownership + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into binder + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(1717840517, p1.readInt32()); // whatever garbage that is there (ABI) + ASSERT_EQ(2, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); +} + // Tests a second operation results in a parcel at the same location as it // started. void parcelOpSameLength(const std::function& a, const std::function& b) { diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73c0a94ce2339ca27b82d51b18dc85128fad25c6 --- /dev/null +++ b/libs/binder/tests/binderRecordReplayTest.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2023 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "../file.h" +#include "parcelables/SingleDataParcelable.h" + +using namespace android; +using android::generateSeedsFromRecording; +using android::binder::borrowed_fd; +using android::binder::Status; +using android::binder::unique_fd; +using android::binder::debug::RecordedTransaction; +using parcelables::SingleDataParcelable; + +const String16 kServerName = String16("binderRecordReplay"); + +#define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \ + Status set##name(T input) { \ + m##name = input; \ + return Status::ok(); \ + } \ + \ + Status get##name(T* output) { \ + *output = m##name; \ + return Status::ok(); \ + } \ + T m##name + +#define GENERATE_GETTER_SETTER(name, T) \ + Status set##name(const T& input) { \ + m##name = input; \ + return Status::ok(); \ + } \ + \ + Status get##name(T* output) { \ + *output = m##name; \ + return Status::ok(); \ + } \ + T m##name + +class MyRecordReplay : public BnBinderRecordReplayTest { +public: + GENERATE_GETTER_SETTER_PRIMITIVE(Boolean, bool); + GENERATE_GETTER_SETTER_PRIMITIVE(Byte, int8_t); + GENERATE_GETTER_SETTER_PRIMITIVE(Int, int); + GENERATE_GETTER_SETTER_PRIMITIVE(Char, char16_t); + GENERATE_GETTER_SETTER_PRIMITIVE(Long, int64_t); + GENERATE_GETTER_SETTER_PRIMITIVE(Float, float); + GENERATE_GETTER_SETTER_PRIMITIVE(Double, double); + + GENERATE_GETTER_SETTER(String, String16); + GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable); + + GENERATE_GETTER_SETTER(BooleanArray, std::vector); + GENERATE_GETTER_SETTER(ByteArray, std::vector); + GENERATE_GETTER_SETTER(IntArray, std::vector); + GENERATE_GETTER_SETTER(CharArray, std::vector); + GENERATE_GETTER_SETTER(LongArray, std::vector); + GENERATE_GETTER_SETTER(FloatArray, std::vector); + GENERATE_GETTER_SETTER(DoubleArray, std::vector); + GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>); + GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector); +}; + +std::vector retrieveData(borrowed_fd fd) { + struct stat fdStat; + EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1); + EXPECT_TRUE(fdStat.st_size != 0); + + std::vector buffer(fdStat.st_size); + auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size); + EXPECT_TRUE(readResult != 0); + return std::move(buffer); +} + +void replayFuzzService(const sp& binder, const RecordedTransaction& transaction) { + unique_fd seedFd(open("/data/local/tmp/replayFuzzService", + O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, 0666)); + ASSERT_TRUE(seedFd.ok()); + + // generate corpus from this transaction. + generateSeedsFromRecording(seedFd, transaction); + + // Read the data which has been written to seed corpus + ASSERT_EQ(0, lseek(seedFd.get(), 0, SEEK_SET)); + std::vector seedData = retrieveData(seedFd); + + // use fuzzService to replay the corpus + FuzzedDataProvider provider(seedData.data(), seedData.size()); + fuzzService(binder, std::move(provider)); +} + +void replayBinder(const sp& binder, const RecordedTransaction& transaction) { + // TODO: move logic to replay RecordedTransaction into RecordedTransaction + Parcel data; + data.setData(transaction.getDataParcel().data(), transaction.getDataParcel().dataSize()); + auto result = binder->transact(transaction.getCode(), data, nullptr, transaction.getFlags()); + + // make sure recording does the thing we expect it to do + EXPECT_EQ(OK, result); +} + +class BinderRecordReplayTest : public ::testing::Test { +public: + void SetUp() override { + // get the remote service +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + auto binder = defaultServiceManager()->getService(kServerName); +#pragma clang diagnostic pop + ASSERT_NE(nullptr, binder); + mInterface = interface_cast(binder); + mBpBinder = binder->remoteBinder(); + ASSERT_NE(nullptr, mBpBinder); + } + + template + void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue, + Status (IBinderRecordReplayTest::*get)(U*), U changedValue) { + auto replayFunctions = {&replayBinder, &replayFuzzService}; + for (auto replayFunc : replayFunctions) { + unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec", + O_RDWR | O_CREAT | O_CLOEXEC, 0666)); + ASSERT_TRUE(fd.ok()); + + // record a transaction + mBpBinder->startRecordingBinder(fd); + auto status = (*mInterface.*set)(recordedValue); + EXPECT_TRUE(status.isOk()); + mBpBinder->stopRecordingBinder(); + + // test transaction does the thing we expect it to do + U output; + status = (*mInterface.*get)(&output); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(output, recordedValue); + + // write over the existing state + status = (*mInterface.*set)(changedValue); + EXPECT_TRUE(status.isOk()); + + status = (*mInterface.*get)(&output); + EXPECT_TRUE(status.isOk()); + + EXPECT_EQ(output, changedValue); + + // replay transaction + ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET)); + std::optional transaction = RecordedTransaction::fromFile(fd); + ASSERT_NE(transaction, std::nullopt); + + const RecordedTransaction& recordedTransaction = *transaction; + // call replay function with recorded transaction + (*replayFunc)(mBpBinder, recordedTransaction); + + status = (*mInterface.*get)(&output); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(output, recordedValue); + } + } + +private: + sp mBpBinder; + sp mInterface; +}; + +TEST_F(BinderRecordReplayTest, ReplayByte) { + recordReplay(&IBinderRecordReplayTest::setByte, int8_t{122}, &IBinderRecordReplayTest::getByte, + int8_t{90}); +} + +TEST_F(BinderRecordReplayTest, ReplayBoolean) { + recordReplay(&IBinderRecordReplayTest::setBoolean, true, &IBinderRecordReplayTest::getBoolean, + false); +} + +TEST_F(BinderRecordReplayTest, ReplayChar) { + recordReplay(&IBinderRecordReplayTest::setChar, char16_t{'G'}, + &IBinderRecordReplayTest::getChar, char16_t{'K'}); +} + +TEST_F(BinderRecordReplayTest, ReplayInt) { + recordReplay(&IBinderRecordReplayTest::setInt, 3, &IBinderRecordReplayTest::getInt, 5); +} + +TEST_F(BinderRecordReplayTest, ReplayFloat) { + recordReplay(&IBinderRecordReplayTest::setFloat, 1.1f, &IBinderRecordReplayTest::getFloat, + 22.0f); +} + +TEST_F(BinderRecordReplayTest, ReplayLong) { + recordReplay(&IBinderRecordReplayTest::setLong, int64_t{1LL << 55}, + &IBinderRecordReplayTest::getLong, int64_t{1LL << 12}); +} + +TEST_F(BinderRecordReplayTest, ReplayDouble) { + recordReplay(&IBinderRecordReplayTest::setDouble, 0.00, &IBinderRecordReplayTest::getDouble, + 1.11); +} + +TEST_F(BinderRecordReplayTest, ReplayString) { + const ::android::String16& input1 = String16("This is saved string"); + const ::android::String16& input2 = String16("This is changed string"); + recordReplay(&IBinderRecordReplayTest::setString, input1, &IBinderRecordReplayTest::getString, + input2); +} + +TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelable) { + SingleDataParcelable saved, changed; + saved.data = 3; + changed.data = 5; + recordReplay(&IBinderRecordReplayTest::setSingleDataParcelable, saved, + &IBinderRecordReplayTest::getSingleDataParcelable, changed); +} + +TEST_F(BinderRecordReplayTest, ReplayByteArray) { + std::vector savedArray = {uint8_t{255}, uint8_t{0}, uint8_t{127}}; + std::vector changedArray = {uint8_t{2}, uint8_t{7}, uint8_t{117}}; + recordReplay(&IBinderRecordReplayTest::setByteArray, savedArray, + &IBinderRecordReplayTest::getByteArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayBooleanArray) { + std::vector savedArray = {true, false, true}; + std::vector changedArray = {false, true, false}; + recordReplay(&IBinderRecordReplayTest::setBooleanArray, savedArray, + &IBinderRecordReplayTest::getBooleanArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayCharArray) { + std::vector savedArray = {char16_t{'G'}, char16_t{'L'}, char16_t{'K'}, char16_t{'T'}}; + std::vector changedArray = {char16_t{'X'}, char16_t{'Y'}, char16_t{'Z'}}; + recordReplay(&IBinderRecordReplayTest::setCharArray, savedArray, + &IBinderRecordReplayTest::getCharArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayIntArray) { + std::vector savedArray = {12, 45, 178}; + std::vector changedArray = {32, 14, 78, 1899}; + recordReplay(&IBinderRecordReplayTest::setIntArray, savedArray, + &IBinderRecordReplayTest::getIntArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayFloatArray) { + std::vector savedArray = {12.14f, 45.56f, 123.178f}; + std::vector changedArray = {0.00f, 14.0f, 718.1f, 1899.122f, 3268.123f}; + recordReplay(&IBinderRecordReplayTest::setFloatArray, savedArray, + &IBinderRecordReplayTest::getFloatArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayLongArray) { + std::vector savedArray = {int64_t{1LL << 11}, int64_t{1LL << 55}, int64_t{1LL << 45}}; + std::vector changedArray = {int64_t{1LL << 1}, int64_t{1LL << 21}, int64_t{1LL << 33}, + int64_t{1LL << 62}}; + recordReplay(&IBinderRecordReplayTest::setLongArray, savedArray, + &IBinderRecordReplayTest::getLongArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayDoubleArray) { + std::vector savedArray = {12.1412313, 45.561232, 123.1781111}; + std::vector changedArray = {0.00111, 14.32130, 712312318.19, 1899212.122, + 322168.122123}; + recordReplay(&IBinderRecordReplayTest::setDoubleArray, savedArray, + &IBinderRecordReplayTest::getDoubleArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayStringArray) { + std::vector savedArray = {String16("This is saved value"), String16(), + String16("\0\0", 2), String16("\xF3\x01\xAC\xAD\x21\xAF")}; + + std::vector changedArray = {String16("This is changed value"), + String16("\xF0\x90\x90\xB7\xE2\x82\xAC")}; + recordReplay(&IBinderRecordReplayTest::setStringArray, savedArray, + &IBinderRecordReplayTest::getStringArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) { + SingleDataParcelable s1, s2, s3, s4, s5; + s1.data = 5213; + s2.data = 1512; + s3.data = 4233; + s4.data = 123124; + s5.data = 0; + std::vector saved = {s1, s2, s3}; + std::vector changed = {s4, s5}; + + recordReplay(&IBinderRecordReplayTest::setSingleDataParcelableArray, saved, + &IBinderRecordReplayTest::getSingleDataParcelableArray, changed); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + + auto server = sp::make(); + android::defaultServiceManager()->addService(kServerName, server.get()); + + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + // not racey, but getService sleeps for 1s + usleep(100000); + + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp index 30172cce87d8a48787199ffaed46f6ce4cd349cc..6eb78d037d53f88a17724fd990abaf82d43f8ea5 100644 --- a/libs/binder/tests/binderRecordedTransactionTest.cpp +++ b/libs/binder/tests/binderRecordedTransactionTest.cpp @@ -20,7 +20,7 @@ using android::Parcel; using android::status_t; -using android::base::unique_fd; +using android::binder::unique_fd; using android::binder::debug::RecordedTransaction; TEST(BinderRecordedTransaction, RoundTripEncoding) { diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp index 593927306ec6f56a7f0cd9bf6fceec0f360c8dd8..4f10d7417fdbcbd41cace62ff3aa2cb2493f5e70 100644 --- a/libs/binder/tests/binderRpcBenchmark.cpp +++ b/libs/binder/tests/binderRpcBenchmark.cpp @@ -74,6 +74,44 @@ class MyBinderRpcBenchmark : public BnBinderRpcBenchmark { *out = bytes; return Status::ok(); } + + class CountedBinder : public BBinder { + public: + CountedBinder(const sp& parent) : mParent(parent) { + std::lock_guard l(mParent->mCountMutex); + mParent->mBinderCount++; + // std::cout << "Count + is now " << mParent->mBinderCount << std::endl; + } + ~CountedBinder() { + { + std::lock_guard l(mParent->mCountMutex); + mParent->mBinderCount--; + // std::cout << "Count - is now " << mParent->mBinderCount << std::endl; + + // skip notify + if (mParent->mBinderCount != 0) return; + } + mParent->mCountCv.notify_one(); + } + + private: + sp mParent; + }; + + Status gimmeBinder(sp* out) override { + *out = sp::make(sp::fromExisting(this)); + return Status::ok(); + } + Status waitGimmesDestroyed() override { + std::unique_lock l(mCountMutex); + mCountCv.wait(l, [&] { return mBinderCount == 0; }); + return Status::ok(); + } + + friend class CountedBinder; + std::mutex mCountMutex; + std::condition_variable mCountCv; + size_t mBinderCount; }; enum Transport { @@ -129,12 +167,33 @@ static sp getBinderForOptions(benchmark::State& state) { } } +static void SetLabel(benchmark::State& state) { + Transport transport = static_cast(state.range(0)); + switch (transport) { +#ifdef __BIONIC__ + case KERNEL: + state.SetLabel("kernel"); + break; +#endif + case RPC: + state.SetLabel("rpc"); + break; + case RPC_TLS: + state.SetLabel("rpc_tls"); + break; + default: + LOG(FATAL) << "Unknown transport value: " << transport; + } +} + void BM_pingTransaction(benchmark::State& state) { sp binder = getBinderForOptions(state); while (state.KeepRunning()) { CHECK_EQ(OK, binder->pingBinder()); } + + SetLabel(state); } BENCHMARK(BM_pingTransaction)->ArgsProduct({kTransportList}); @@ -164,6 +223,8 @@ void BM_repeatTwoPageString(benchmark::State& state) { Status ret = iface->repeatString(str, &out); CHECK(ret.isOk()) << ret; } + + SetLabel(state); } BENCHMARK(BM_repeatTwoPageString)->ArgsProduct({kTransportList}); @@ -182,11 +243,45 @@ void BM_throughputForTransportAndBytes(benchmark::State& state) { Status ret = iface->repeatBytes(bytes, &out); CHECK(ret.isOk()) << ret; } + + SetLabel(state); } BENCHMARK(BM_throughputForTransportAndBytes) ->ArgsProduct({kTransportList, {64, 1024, 2048, 4096, 8182, 16364, 32728, 65535, 65536, 65537}}); +void BM_collectProxies(benchmark::State& state) { + sp binder = getBinderForOptions(state); + sp iface = interface_cast(binder); + CHECK(iface != nullptr); + + const size_t kNumIters = state.range(1); + + while (state.KeepRunning()) { + std::vector> out; + out.resize(kNumIters); + + for (size_t i = 0; i < kNumIters; i++) { + Status ret = iface->gimmeBinder(&out[i]); + CHECK(ret.isOk()) << ret; + } + + out.clear(); + + // we are using a thread up to wait, so make a call to + // force all refcounts to be updated first - current + // binder behavior means we really don't need to wait, + // so code which is waiting is really there to protect + // against any future changes that could delay destruction + android::IInterface::asBinder(iface)->pingBinder(); + + iface->waitGimmesDestroyed(); + } + + SetLabel(state); +} +BENCHMARK(BM_collectProxies)->ArgsProduct({kTransportList, {10, 100, 1000, 5000, 10000, 20000}}); + void BM_repeatBinder(benchmark::State& state) { sp binder = getBinderForOptions(state); CHECK(binder != nullptr); @@ -201,6 +296,8 @@ void BM_repeatBinder(benchmark::State& state) { Status ret = iface->repeatBinder(binder, &out); CHECK(ret.isOk()) << ret; } + + SetLabel(state); } BENCHMARK(BM_repeatBinder)->ArgsProduct({kTransportList}); @@ -228,11 +325,6 @@ int main(int argc, char** argv) { ::benchmark::Initialize(&argc, argv); if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; - std::cerr << "Tests suffixes:" << std::endl; - std::cerr << "\t.../" << Transport::KERNEL << " is KERNEL" << std::endl; - std::cerr << "\t.../" << Transport::RPC << " is RPC" << std::endl; - std::cerr << "\t.../" << Transport::RPC_TLS << " is RPC with TLS" << std::endl; - #ifdef __BIONIC__ if (0 == fork()) { prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 8d1300779af4be05b9668cf68ce87b3adc0625b0..2769a88342981646f395c5c80e9a927f058ba7d7 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#ifndef __ANDROID_VENDOR__ +// only used on NDK tests outside of vendor #include -#include +#endif #include #include @@ -23,6 +25,7 @@ #include #include +#include #include #include #include @@ -33,11 +36,16 @@ #include #endif // BINDER_RPC_TO_TRUSTY_TEST +#include "../Utils.h" #include "binderRpcTestCommon.h" #include "binderRpcTestFixture.h" using namespace std::chrono_literals; using namespace std::placeholders; +using android::binder::borrowed_fd; +using android::binder::GetExecutableDirectory; +using android::binder::ReadFdToString; +using android::binder::unique_fd; using testing::AssertionFailure; using testing::AssertionResult; using testing::AssertionSuccess; @@ -56,12 +64,12 @@ constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0"; static std::string WaitStatusToString(int wstatus) { if (WIFEXITED(wstatus)) { - return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus)); + return std::format("exit status {}", WEXITSTATUS(wstatus)); } if (WIFSIGNALED(wstatus)) { - return base::StringPrintf("term signal %d", WTERMSIG(wstatus)); + return std::format("term signal {}", WTERMSIG(wstatus)); } - return base::StringPrintf("unexpected state %d", wstatus); + return std::format("unexpected state {}", wstatus); } static void debugBacktrace(pid_t pid) { @@ -80,12 +88,11 @@ public: mPid = other.mPid; other.mPid = 0; } - Process(const std::function& f) { - android::base::unique_fd childWriteEnd; - android::base::unique_fd childReadEnd; - CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno); - CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno); + Process(const std::function& f) { + unique_fd childWriteEnd; + unique_fd childReadEnd; + if (!binder::Pipe(&mReadEnd, &childWriteEnd, 0)) PLOGF("child write pipe failed"); + if (!binder::Pipe(&childReadEnd, &mWriteEnd, 0)) PLOGF("child read pipe failed"); if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -107,8 +114,8 @@ public: } } } - android::base::borrowed_fd readEnd() { return mReadEnd; } - android::base::borrowed_fd writeEnd() { return mWriteEnd; } + borrowed_fd readEnd() { return mReadEnd; } + borrowed_fd writeEnd() { return mWriteEnd; } void setCustomExitStatusCheck(std::function f) { mCustomExitStatusCheck = std::move(f); @@ -122,8 +129,8 @@ public: private: std::function mCustomExitStatusCheck; pid_t mPid = 0; - android::base::unique_fd mReadEnd; - android::base::unique_fd mWriteEnd; + unique_fd mReadEnd; + unique_fd mWriteEnd; }; static std::string allocateSocketAddress() { @@ -139,12 +146,13 @@ static unsigned int allocateVsockPort() { return vsockPort++; } -static base::unique_fd initUnixSocket(std::string addr) { +static unique_fd initUnixSocket(std::string addr) { auto socket_addr = UnixSocketAddress(addr.c_str()); - base::unique_fd fd( - TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX))); - CHECK(fd.ok()); - CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))); + unique_fd fd(TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX))); + if (!fd.ok()) PLOGF("initUnixSocket failed to create socket"); + if (0 != TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))) { + PLOGF("initUnixSocket failed to bind"); + } return fd; } @@ -199,37 +207,33 @@ public: void terminate() override { host.terminate(); } }; -static base::unique_fd connectTo(const RpcSocketAddress& addr) { - base::unique_fd serverFd( +static unique_fd connectTo(const RpcSocketAddress& addr) { + unique_fd serverFd( TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); - int savedErrno = errno; - CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": " - << strerror(savedErrno); + if (!serverFd.ok()) { + PLOGF("Could not create socket %s", addr.toString().c_str()); + } if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { - int savedErrno = errno; - LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": " - << strerror(savedErrno); + PLOGF("Could not connect to socket %s", addr.toString().c_str()); } return serverFd; } #ifndef BINDER_RPC_TO_TRUSTY_TEST -static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) { - base::unique_fd sockClient, sockServer; - if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) { - int savedErrno = errno; - LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); +static unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) { + unique_fd sockClient, sockServer; + if (!binder::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) { + PLOGF("Failed socketpair()"); } int zero = 0; iovec iov{&zero, sizeof(zero)}; - std::vector> fds; + std::vector> fds; fds.emplace_back(std::move(sockServer)); - if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) { - int savedErrno = errno; - LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno); + if (binder::os::sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) { + PLOGF("Failed sendMessageOnSocket"); } return std::move(sockClient); } @@ -243,25 +247,27 @@ std::unique_ptr BinderRpc::newFactory(RpcSecurity rpcSec // threads. std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( const BinderRpcOptions& options) { - CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; + LOG_ALWAYS_FATAL_IF(options.numSessions < 1, "Must have at least one session to a server"); if (options.numIncomingConnectionsBySession.size() != 0) { - CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions); + LOG_ALWAYS_FATAL_IF(options.numIncomingConnectionsBySession.size() != options.numSessions, + "%s: %zu != %zu", __func__, + options.numIncomingConnectionsBySession.size(), options.numSessions); } - 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()); + SocketType socketType = GetParam().type; + RpcSecurity rpcSecurity = GetParam().security; + uint32_t clientVersion = GetParam().clientVersion; + uint32_t serverVersion = GetParam().serverVersion; + bool singleThreaded = GetParam().singleThreaded; + bool noKernel = GetParam().noKernel; - 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" : ""); + std::string path = GetExecutableDirectory(); + auto servicePath = + std::format("{}/binder_rpc_test_service{}{}", path, + singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : ""); - base::unique_fd bootstrapClientFd, socketFd; + unique_fd bootstrapClientFd, socketFd; auto addr = allocateSocketAddress(); // Initializes the socket before the fork/exec. @@ -270,14 +276,13 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( } else if (socketType == SocketType::UNIX_BOOTSTRAP) { // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec. // This is because we cannot pass ParcelFileDescriptor over a pipe. - if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) { - int savedErrno = errno; - LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); + if (!binder::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) { + PLOGF("Failed socketpair()"); } } auto ret = std::make_unique( - Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { + Process([=](borrowed_fd writeEnd, borrowed_fd readEnd) { if (socketType == SocketType::TIPC) { // Trusty has a single persistent service return; @@ -285,8 +290,10 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( 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); + auto status = execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), + readFd.c_str(), NULL); + PLOGF("execl('%s', _, %s, %s) should not return at all, but it returned %d", + servicePath.c_str(), writeFd.c_str(), readFd.c_str(), status); })); BinderRpcTestServerConfig serverConfig; @@ -331,16 +338,16 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( } writeToFd(ret->host.writeEnd(), clientInfo); - CHECK_LE(serverInfo.port, std::numeric_limits::max()); + LOG_ALWAYS_FATAL_IF(serverInfo.port > std::numeric_limits::max()); if (socketType == SocketType::INET) { - CHECK_NE(0, serverInfo.port); + LOG_ALWAYS_FATAL_IF(0 == serverInfo.port); } if (rpcSecurity == RpcSecurity::TLS) { const auto& serverCert = serverInfo.cert.data; - CHECK_EQ(OK, - certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - serverCert)); + LOG_ALWAYS_FATAL_IF( + OK != + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert)); } } @@ -353,7 +360,7 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( ? options.numIncomingConnectionsBySession.at(i) : 0; - CHECK(session->setProtocolVersion(clientVersion)); + LOG_ALWAYS_FATAL_IF(!session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(numIncoming); session->setMaxOutgoingConnections(options.numOutgoingConnections); session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); @@ -370,7 +377,7 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( break; case SocketType::UNIX_BOOTSTRAP: status = session->setupUnixDomainSocketBootstrapClient( - base::unique_fd(dup(bootstrapClientFd.get()))); + unique_fd(dup(bootstrapClientFd.get()))); break; case SocketType::VSOCK: status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort); @@ -387,14 +394,14 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( // in case the service is slow to start int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str()); if (tipcFd >= 0) { - return android::base::unique_fd(tipcFd); + return unique_fd(tipcFd); } usleep(50000); } - return android::base::unique_fd(); + return unique_fd(); #else LOG_ALWAYS_FATAL("Tried to connect to Trusty outside of vendor"); - return android::base::unique_fd(); + return unique_fd(); #endif }); break; @@ -405,7 +412,7 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( ret->sessions.clear(); break; } - CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); + LOG_ALWAYS_FATAL_IF(status != OK, "Could not connect: %s", statusToString(status).c_str()); ret->sessions.push_back({session, session->getRootObject()}); } return ret; @@ -461,8 +468,11 @@ static void testThreadPoolOverSaturated(sp iface, size_t numCall EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs); - // Potential flake, but make sure calls are handled in parallel. - EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs); + // Potential flake, but make sure calls are handled in parallel. Due + // to past flakes, this only checks that the amount of time taken has + // some parallelism. Other tests such as ThreadPoolGreaterThanEqualRequested + // check this more exactly. + EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs); } TEST_P(BinderRpc, ThreadPoolOverSaturated) { @@ -583,12 +593,12 @@ TEST_P(BinderRpc, OnewayCallQueueingWithFds) { android::os::ParcelFileDescriptor fdA; EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA)); std::string result; - CHECK(android::base::ReadFdToString(fdA.get(), &result)); + ASSERT_TRUE(ReadFdToString(fdA.get(), &result)); EXPECT_EQ(result, "a"); android::os::ParcelFileDescriptor fdB; EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB)); - CHECK(android::base::ReadFdToString(fdB.get(), &result)); + ASSERT_TRUE(ReadFdToString(fdB.get(), &result)); EXPECT_EQ(result, "b"); saturateThreadPool(kNumServerThreads, proc.rootIface); @@ -671,7 +681,7 @@ TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) { // session 0 - will check for leaks in destrutor of proc // session 1 - we want to make sure it gets deleted when we drop all references to it auto proc = createRpcTestSocketServerProcess( - {.numThreads = 1, .numIncomingConnectionsBySession = {0, 1}, .numSessions = 2}); + {.numThreads = 1, .numSessions = 2, .numIncomingConnectionsBySession = {0, 1}}); wp session = proc.proc->sessions.at(1).session; @@ -687,6 +697,12 @@ TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) { } EXPECT_EQ(nullptr, session.promote()); + + // now that it has died, wait for the remote session to shutdown + std::vector remoteCounts; + do { + EXPECT_OK(proc.rootIface->countBinders(&remoteCounts)); + } while (remoteCounts.size() > 1); } TEST_P(BinderRpc, SingleDeathRecipient) { @@ -939,8 +955,8 @@ TEST_P(BinderRpc, ReceiveFile) { ASSERT_TRUE(status.isOk()) << status; std::string result; - CHECK(android::base::ReadFdToString(out.get(), &result)); - EXPECT_EQ(result, "hello"); + ASSERT_TRUE(ReadFdToString(out.get(), &result)); + ASSERT_EQ(result, "hello"); } TEST_P(BinderRpc, SendFiles) { @@ -969,7 +985,7 @@ TEST_P(BinderRpc, SendFiles) { ASSERT_TRUE(status.isOk()) << status; std::string result; - CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_TRUE(ReadFdToString(out.get(), &result)); EXPECT_EQ(result, "123abcd"); } @@ -994,7 +1010,7 @@ TEST_P(BinderRpc, SendMaxFiles) { ASSERT_TRUE(status.isOk()) << status; std::string result; - CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_TRUE(ReadFdToString(out.get(), &result)); EXPECT_EQ(result, std::string(253, 'a')); } @@ -1112,26 +1128,41 @@ TEST_P(BinderRpc, Fds) { } #ifdef BINDER_RPC_TO_TRUSTY_TEST -INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, - ::testing::Combine(::testing::Values(SocketType::TIPC), - ::testing::Values(RpcSecurity::RAW), - ::testing::ValuesIn(testVersions()), - ::testing::ValuesIn(testVersions()), - ::testing::Values(true), ::testing::Values(true)), + +static std::vector getTrustyBinderRpcParams() { + std::vector ret; + + for (const auto& clientVersion : testVersions()) { + for (const auto& serverVersion : testVersions()) { + ret.push_back(BinderRpc::ParamType{ + .type = SocketType::TIPC, + .security = RpcSecurity::RAW, + .clientVersion = clientVersion, + .serverVersion = serverVersion, + .singleThreaded = true, + .noKernel = true, + }); + } + } + + return ret; +} + +INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()), BinderRpc::PrintParamInfo); #else // BINDER_RPC_TO_TRUSTY_TEST -static bool testSupportVsockLoopback() { +bool testSupportVsockLoopback() { // We don't need to enable TLS to know if vsock is supported. unsigned int vsockPort = allocateVsockPort(); - android::base::unique_fd serverFd( + unique_fd serverFd( TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); if (errno == EAFNOSUPPORT) { return false; } - LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno)); + LOG_ALWAYS_FATAL_IF(!serverFd.ok(), "Could not create socket: %s", strerror(errno)); sockaddr_vm serverAddr{ .svm_family = AF_VSOCK, @@ -1151,9 +1182,9 @@ static bool testSupportVsockLoopback() { // 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( + 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, + LOG_ALWAYS_FATAL_IF(!connectFd.ok(), "Could not create socket for port %u: %s", vsockPort, strerror(errno)); bool success = false; @@ -1165,13 +1196,13 @@ static bool testSupportVsockLoopback() { ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast(&connectAddr), sizeof(connectAddr))); if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) { - android::base::unique_fd acceptFd; + 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)); + ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1)); LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno)); if (pfd[0].revents & POLLIN) { @@ -1220,7 +1251,15 @@ static std::vector testSocketTypes(bool hasPreconnected = true) { if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED); +#ifdef __BIONIC__ + // Devices may not have vsock support. AVF tests will verify whether they do, but + // we can't require it due to old kernels for the time being. static bool hasVsockLoopback = testSupportVsockLoopback(); +#else + // On host machines, we always assume we have vsock loopback. If we don't, the + // subsequent failures will be more clear than showing one now. + static bool hasVsockLoopback = true; +#endif if (hasVsockLoopback) { ret.push_back(SocketType::VSOCK); @@ -1229,13 +1268,47 @@ static std::vector testSocketTypes(bool hasPreconnected = true) { return ret; } -INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, - ::testing::Combine(::testing::ValuesIn(testSocketTypes()), - ::testing::ValuesIn(RpcSecurityValues()), - ::testing::ValuesIn(testVersions()), - ::testing::ValuesIn(testVersions()), - ::testing::Values(false, true), - ::testing::Values(false, true)), +static std::vector getBinderRpcParams() { + std::vector ret; + + constexpr bool full = false; + + for (const auto& type : testSocketTypes()) { + if (full || type == SocketType::UNIX) { + for (const auto& security : RpcSecurityValues()) { + for (const auto& clientVersion : testVersions()) { + for (const auto& serverVersion : testVersions()) { + for (bool singleThreaded : {false, true}) { + for (bool noKernel : {false, true}) { + ret.push_back(BinderRpc::ParamType{ + .type = type, + .security = security, + .clientVersion = clientVersion, + .serverVersion = serverVersion, + .singleThreaded = singleThreaded, + .noKernel = noKernel, + }); + } + } + } + } + } + } else { + ret.push_back(BinderRpc::ParamType{ + .type = type, + .security = RpcSecurity::RAW, + .clientVersion = RPC_WIRE_PROTOCOL_VERSION, + .serverVersion = RPC_WIRE_PROTOCOL_VERSION, + .singleThreaded = false, + .noKernel = false, + }); + } + } + + return ret; +} + +INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn(getBinderRpcParams()), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject @@ -1290,7 +1363,11 @@ private: }; TEST(BinderRpc, Java) { -#if !defined(__ANDROID__) + bool expectDebuggable = false; +#if defined(__ANDROID__) + expectDebuggable = android::base::GetBoolProperty("ro.debuggable", false) && + android::base::GetProperty("ro.build.type", "") != "user"; +#else GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on" "createRpcDelegateServiceManager() with a device attached, such test belongs " "to binderHostDeviceTest. Hence, just disable this test on host."; @@ -1318,8 +1395,7 @@ TEST(BinderRpc, Java) { auto keepAlive = sp::make(); auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive); - if (!android::base::GetBoolProperty("ro.debuggable", false) || - android::base::GetProperty("ro.build.type", "") == "user") { + if (!expectDebuggable) { ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus) << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user " "builds, but get " @@ -1350,14 +1426,14 @@ public: }; TEST_P(BinderRpcServerOnly, SetExternalServerTest) { - base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); + unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); int sinkFd = sink.get(); auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam()))); - server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam()))); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); ASSERT_TRUE(server->hasServer()); - base::unique_fd retrieved = server->releaseServer(); + unique_fd retrieved = server->releaseServer(); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(sinkFd, retrieved.get()); } @@ -1369,7 +1445,7 @@ TEST_P(BinderRpcServerOnly, Shutdown) { auto addr = allocateSocketAddress(); auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam()))); - server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam()))); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); auto joinEnds = std::make_shared(); @@ -1403,12 +1479,12 @@ public: // in the client half of the tests. using Param = std::tuple, uint32_t>; - using ConnectToServer = std::function; + using ConnectToServer = std::function; // A server that handles client socket connections. class Server { public: - using AcceptConnection = std::function; + using AcceptConnection = std::function; explicit Server() {} Server(Server&&) = default; @@ -1418,7 +1494,9 @@ public: std::unique_ptr auth = std::make_unique()) { auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity)); - rpcServer->setProtocolVersion(serverVersion); + if (!rpcServer->setProtocolVersion(serverVersion)) { + return AssertionFailure() << "Invalid protocol version: " << serverVersion; + } switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; @@ -1435,8 +1513,8 @@ public: }; } break; case SocketType::UNIX_BOOTSTRAP: { - base::unique_fd bootstrapFdClient, bootstrapFdServer; - if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) { + unique_fd bootstrapFdClient, bootstrapFdServer; + if (!binder::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) { return AssertionFailure() << "Socketpair() failed"; } auto status = rpcServer->setupUnixDomainSocketBootstrapServer( @@ -1479,7 +1557,7 @@ public: mConnectToServer = [port] { const char* addr = kLocalInetAddress; auto aiStart = InetSocketAddress::getAddrInfo(addr, port); - if (aiStart == nullptr) return base::unique_fd{}; + if (aiStart == nullptr) return unique_fd{}; for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { auto fd = connectTo( InetSocketAddress(ai->ai_addr, ai->ai_addrlen, addr, port)); @@ -1487,7 +1565,7 @@ public: } ALOGE("None of the socket address resolved for %s:%u can be connected", addr, port); - return base::unique_fd{}; + return unique_fd{}; }; } break; case SocketType::TIPC: { @@ -1511,24 +1589,22 @@ public: mThread = std::make_unique(&Server::run, this); } - base::unique_fd acceptServerConnection() { - return base::unique_fd(TEMP_FAILURE_RETRY( + unique_fd acceptServerConnection() { + return unique_fd(TEMP_FAILURE_RETRY( accept4(mFd.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))); } - base::unique_fd recvmsgServerConnection() { - std::vector> fds; + unique_fd recvmsgServerConnection() { + std::vector> fds; int buf; iovec iov{&buf, sizeof(buf)}; - if (receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) { - int savedErrno = errno; - LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno); - } - if (fds.size() != 1) { - LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size(); + if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) { + PLOGF("Failed receiveMessage"); } - return std::move(std::get(fds[0])); + LOG_ALWAYS_FATAL_IF(fds.size() != 1, "Expected one FD from receiveMessage(), got %zu", + fds.size()); + return std::move(std::get(fds[0])); } void run() { @@ -1536,13 +1612,13 @@ public: std::vector threads; while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) { - base::unique_fd acceptedFd = mAcceptConnection(this); + unique_fd acceptedFd = mAcceptConnection(this); threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd)); } for (auto& thread : threads) thread.join(); } - void handleOne(android::base::unique_fd acceptedFd) { + void handleOne(unique_fd acceptedFd) { ASSERT_TRUE(acceptedFd.ok()); RpcTransportFd transportFd(std::move(acceptedFd)); auto serverTransport = mCtx->newTransport(std::move(transportFd), mFdTrigger.get()); @@ -2012,7 +2088,7 @@ INSTANTIATE_TEST_CASE_P( int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + __android_log_set_logger(__android_log_stderr_logger); return RUN_ALL_TESTS(); } diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index c1364dd9a052b04f9724080e146d187c9980e2fc..62fe9e56f63167ee66b4e81e54096b057e224d78 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -37,10 +36,11 @@ #include #include -#ifndef __TRUSTY__ -#include -#include +#ifdef __ANDROID__ #include +#endif + +#ifndef __TRUSTY__ #include #include #include @@ -57,7 +57,10 @@ #include "../BuildFlags.h" #include "../FdTrigger.h" +#include "../FdUtils.h" #include "../RpcState.h" // for debugging +#include "FileUtils.h" +#include "format.h" #include "utils/Errors.h" namespace android { @@ -70,17 +73,27 @@ static inline std::vector RpcSecurityValues() { return {RpcSecurity::RAW, RpcSecurity::TLS}; } +static inline bool hasExperimentalRpc() { +#ifdef __ANDROID__ + return base::GetProperty("ro.build.version.codename", "") != "REL"; +#else + return false; +#endif +} + static inline std::vector testVersions() { std::vector 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); + if (hasExperimentalRpc()) { + versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + } return versions; } static inline std::string trustyIpcPort(uint32_t serverVersion) { - return base::StringPrintf("com.android.trusty.binderRpcTestService.V%" PRIu32, serverVersion); + return std::format("com.android.trusty.binderRpcTestService.V{}", serverVersion); } enum class SocketType { @@ -144,33 +157,34 @@ struct BinderRpcOptions { }; #ifndef __TRUSTY__ -static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { +static inline void writeString(binder::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())); + LOG_ALWAYS_FATAL_IF(!android::binder::WriteFully(fd, &length, sizeof(length))); + LOG_ALWAYS_FATAL_IF(!android::binder::WriteFully(fd, str.data(), str.length())); } -static inline std::string readString(android::base::borrowed_fd fd) { +static inline std::string readString(binder::borrowed_fd fd) { uint64_t length; - CHECK(android::base::ReadFully(fd, &length, sizeof(length))); + LOG_ALWAYS_FATAL_IF(!android::binder::ReadFully(fd, &length, sizeof(length))); std::string ret(length, '\0'); - CHECK(android::base::ReadFully(fd, ret.data(), length)); + LOG_ALWAYS_FATAL_IF(!android::binder::ReadFully(fd, ret.data(), length)); return ret; } -static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { +static inline void writeToFd(binder::borrowed_fd fd, const Parcelable& parcelable) { Parcel parcel; - CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); + LOG_ALWAYS_FATAL_IF(OK != parcelable.writeToParcel(&parcel)); writeString(fd, std::string(reinterpret_cast(parcel.data()), parcel.dataSize())); } template -static inline T readFromFd(android::base::borrowed_fd fd) { +static inline T readFromFd(binder::borrowed_fd fd) { std::string data = readString(fd); Parcel parcel; - CHECK_EQ(OK, parcel.setData(reinterpret_cast(data.data()), data.size())); + LOG_ALWAYS_FATAL_IF(OK != + parcel.setData(reinterpret_cast(data.data()), data.size())); T object; - CHECK_EQ(OK, object.readFromParcel(&parcel)); + LOG_ALWAYS_FATAL_IF(OK != object.readFromParcel(&parcel)); return object; } @@ -195,12 +209,12 @@ static inline std::unique_ptr newTlsFactory( } // 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); +static inline binder::unique_fd mockFileDescriptor(std::string contents) { + binder::unique_fd readFd, writeFd; + LOG_ALWAYS_FATAL_IF(!binder::Pipe(&readFd, &writeFd), "%s", 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)) { + if (!android::binder::WriteStringToFd(contents, writeFd)) { int savedErrno = errno; LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s", strerror(savedErrno)); @@ -379,7 +393,7 @@ public: } if (delayed) { - RpcMaybeThread([=]() { + RpcMaybeThread([=, this]() { ALOGE("Executing delayed callback: '%s'", value.c_str()); Status status = doCallback(callback, oneway, false, value); ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h index 6cde9f7f8dc8b591a4979f14e0db42c8d648f801..2c9646b30e433ecf6e1e88781c137a029b07d6c5 100644 --- a/libs/binder/tests/binderRpcTestFixture.h +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -79,6 +79,7 @@ struct BinderRpcTestProcessSession { expectAlreadyShutdown = true; } + BinderRpcTestProcessSession(std::unique_ptr proc) : proc(std::move(proc)){}; BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; ~BinderRpcTestProcessSession() { if (!expectAlreadyShutdown) { @@ -105,15 +106,23 @@ struct BinderRpcTestProcessSession { } }; -class BinderRpc : public ::testing::TestWithParam< - std::tuple> { +struct BinderRpcParam { + SocketType type; + RpcSecurity security; + uint32_t clientVersion; + uint32_t serverVersion; + bool singleThreaded; + bool noKernel; +}; +class BinderRpc : public ::testing::TestWithParam { public: - 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 serverSingleThreaded() const { return std::get<4>(GetParam()); } - bool noKernel() const { return std::get<5>(GetParam()); } + // TODO: avoid unnecessary layer of indirection + SocketType socketType() const { return GetParam().type; } + RpcSecurity rpcSecurity() const { return GetParam().security; } + uint32_t clientVersion() const { return GetParam().clientVersion; } + uint32_t serverVersion() const { return GetParam().serverVersion; } + bool serverSingleThreaded() const { return GetParam().singleThreaded; } + bool noKernel() const { return GetParam().noKernel; } bool clientOrServerSingleThreaded() const { return !kEnableRpcThreads || serverSingleThreaded(); @@ -138,9 +147,7 @@ public: } BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { - BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcessEtc(options), - }; + BinderRpcTestProcessSession ret(createRpcTestSocketServerProcessEtc(options)); ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root; ret.rootIface = interface_cast(ret.rootBinder); @@ -149,15 +156,16 @@ public: } static std::string PrintParamInfo(const testing::TestParamInfo& info) { - 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) { + auto ret = PrintToString(info.param.type) + "_" + + newFactory(info.param.security)->toCString() + "_clientV" + + std::to_string(info.param.clientVersion) + "_serverV" + + std::to_string(info.param.serverVersion); + if (info.param.singleThreaded) { ret += "_single_threaded"; } else { ret += "_multi_threaded"; } - if (noKernel) { + if (info.param.noKernel) { ret += "_no_kernel"; } else { ret += "_with_kernel"; diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index a9736d5ecd129f33a5f46b11fb629d5c6c24c607..28125f169ca94ee277801f45b871e561a85e8a92 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -17,6 +17,8 @@ #include "binderRpcTestCommon.h" using namespace android; +using android::binder::ReadFdToString; +using android::binder::unique_fd; class MyBinderRpcTestAndroid : public MyBinderRpcTestBase { public: @@ -65,17 +67,17 @@ public: std::string acc; for (const auto& file : files) { std::string result; - CHECK(android::base::ReadFdToString(file.get(), &result)); + LOG_ALWAYS_FATAL_IF(!ReadFdToString(file.get(), &result)); acc.append(result); } out->reset(mockFileDescriptor(acc)); return Status::ok(); } - HandoffChannel mFdChannel; + HandoffChannel mFdChannel; Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override { - mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0))); + mFdChannel.write(unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0))); return Status::ok(); } @@ -98,11 +100,11 @@ public: }; int main(int argc, char* argv[]) { - android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + __android_log_set_logger(__android_log_stderr_logger); 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])); + unique_fd writeEnd(atoi(argv[1])); + unique_fd readEnd(atoi(argv[2])); auto serverConfig = readFromFd(readEnd); auto socketType = static_cast(serverConfig.socketType); @@ -118,32 +120,36 @@ int main(int argc, char* argv[]) { auto certVerifier = std::make_shared(); sp server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier)); - server->setProtocolVersion(serverConfig.serverVersion); + LOG_ALWAYS_FATAL_IF(!server->setProtocolVersion(serverConfig.serverVersion)); server->setMaxThreads(serverConfig.numThreads); server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); unsigned int outPort = 0; - base::unique_fd socketFd(serverConfig.socketFd); + unique_fd socketFd(serverConfig.socketFd); switch (socketType) { case SocketType::PRECONNECTED: [[fallthrough]]; case SocketType::UNIX: - CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str())) - << serverConfig.addr; + LOG_ALWAYS_FATAL_IF(OK != server->setupUnixDomainServer(serverConfig.addr.c_str()), + "%s", serverConfig.addr.c_str()); break; case SocketType::UNIX_BOOTSTRAP: - CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd))); + LOG_ALWAYS_FATAL_IF(OK != + server->setupUnixDomainSocketBootstrapServer(std::move(socketFd))); break; case SocketType::UNIX_RAW: - CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd))); + LOG_ALWAYS_FATAL_IF(OK != server->setupRawSocketServer(std::move(socketFd))); break; case SocketType::VSOCK: - CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort)); + LOG_ALWAYS_FATAL_IF(OK != + server->setupVsockServer(VMADDR_CID_LOCAL, + serverConfig.vsockPort), + "Need `sudo modprobe vsock_loopback`?"); break; case SocketType::INET: { - CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); - CHECK_NE(0, outPort); + LOG_ALWAYS_FATAL_IF(OK != server->setupInetServer(kLocalInetAddress, 0, &outPort)); + LOG_ALWAYS_FATAL_IF(0 == outPort); break; } default: @@ -158,16 +164,21 @@ int main(int argc, char* argv[]) { if (rpcSecurity == RpcSecurity::TLS) { for (const auto& clientCert : clientInfo.certs) { - CHECK_EQ(OK, - certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - clientCert.data)); + LOG_ALWAYS_FATAL_IF(OK != + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + clientCert.data)); } } - server->setPerSessionRootObject([&](const void* addrPtr, size_t len) { + server->setPerSessionRootObject([&](wp session, const void* addrPtr, size_t len) { + { + sp spSession = session.promote(); + LOG_ALWAYS_FATAL_IF(nullptr == spSession.get()); + } + // UNIX sockets with abstract addresses return // sizeof(sa_family_t)==2 in addrlen - CHECK_GE(len, sizeof(sa_family_t)); + LOG_ALWAYS_FATAL_IF(len < sizeof(sa_family_t)); const sockaddr* addr = reinterpret_cast(addrPtr); sp service = sp::make(); switch (addr->sa_family) { @@ -175,15 +186,15 @@ int main(int argc, char* argv[]) { // nothing to save break; case AF_VSOCK: - CHECK_EQ(len, sizeof(sockaddr_vm)); + LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_vm)); service->port = reinterpret_cast(addr)->svm_port; break; case AF_INET: - CHECK_EQ(len, sizeof(sockaddr_in)); + LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in)); service->port = ntohs(reinterpret_cast(addr)->sin_port); break; case AF_INET6: - CHECK_EQ(len, sizeof(sockaddr_in)); + LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in)); service->port = ntohs(reinterpret_cast(addr)->sin6_port); break; default: diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp index 85573895e9fcea7e07951a745da32eb173fe8f96..8346b3691ea7f7c75f6a561c570e6044aa7ef6a3 100644 --- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp +++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp @@ -16,7 +16,6 @@ #define TLOG_TAG "binderRpcTestService" -#include #include #include #include @@ -28,7 +27,6 @@ #include "binderRpcTestCommon.h" using namespace android; -using android::base::StringPrintf; using binder::Status; static int gConnectionCounter = 0; @@ -79,26 +77,27 @@ int main(void) { // Message size needs to be large enough to cover all messages sent by the // tests: SendAndGetResultBackBig sends two large strings. constexpr size_t max_msg_size = 4096; - auto serverOrErr = + auto server = RpcServerTrusty::make(hset, serverInfo.port->c_str(), std::shared_ptr(&port_acl), max_msg_size); - if (!serverOrErr.ok()) { - TLOGE("Failed to create RpcServer (%d)\n", serverOrErr.error()); + if (server == nullptr) { return EXIT_FAILURE; } - auto server = std::move(*serverOrErr); serverInfo.server = server; - serverInfo.server->setProtocolVersion(serverVersion); - serverInfo.server->setPerSessionRootObject([=](const void* /*addrPtr*/, size_t /*len*/) { - auto service = sp::make(); - // Assign a unique connection identifier to service->port so - // getClientPort returns a unique value per connection - service->port = ++gConnectionCounter; - service->server = server; - return service; - }); + if (!serverInfo.server->setProtocolVersion(serverVersion)) { + return EXIT_FAILURE; + } + serverInfo.server->setPerSessionRootObject( + [=](wp /*session*/, const void* /*addrPtr*/, size_t /*len*/) { + auto service = sp::make(); + // Assign a unique connection identifier to service->port so + // getClientPort returns a unique value per connection + service->port = ++gConnectionCounter; + service->server = server; + return service; + }); servers.push_back(std::move(serverInfo)); } diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp index 28be10db7661252a27f85ef86625fbf357ad7370..18751cc089094006609341e0b5908ee93d4d141a 100644 --- a/libs/binder/tests/binderRpcTestTrusty.cpp +++ b/libs/binder/tests/binderRpcTestTrusty.cpp @@ -16,13 +16,14 @@ #define LOG_TAG "binderRpcTest" -#include #include #include #include #include "binderRpcTestFixture.h" +using android::binder::unique_fd; + namespace android { // Destructors need to be defined, even if pure virtual @@ -57,9 +58,9 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( [](size_t n) { return n != 0; }), "Non-zero incoming connections on Trusty"); - RpcSecurity rpcSecurity = std::get<1>(GetParam()); - uint32_t clientVersion = std::get<2>(GetParam()); - uint32_t serverVersion = std::get<3>(GetParam()); + RpcSecurity rpcSecurity = GetParam().security; + uint32_t clientVersion = GetParam().clientVersion; + uint32_t serverVersion = GetParam().serverVersion; auto ret = std::make_unique(); @@ -75,7 +76,7 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( auto port = trustyIpcPort(serverVersion); int rc = connect(port.c_str(), IPC_CONNECT_WAIT_FOR_PORT); LOG_ALWAYS_FATAL_IF(rc < 0, "Failed to connect to service: %d", rc); - return base::unique_fd(rc); + return unique_fd(rc); }); if (options.allowConnectFailure && status != OK) { ret->sessions.clear(); @@ -89,12 +90,27 @@ std::unique_ptr BinderRpc::createRpcTestSocketServerProcessEtc( return ret; } -INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, - ::testing::Combine(::testing::Values(SocketType::TIPC), - ::testing::Values(RpcSecurity::RAW), - ::testing::ValuesIn(testVersions()), - ::testing::ValuesIn(testVersions()), - ::testing::Values(false), ::testing::Values(true)), +static std::vector getTrustyBinderRpcParams() { + std::vector ret; + + for (const auto& clientVersion : testVersions()) { + for (const auto& serverVersion : testVersions()) { + ret.push_back(BinderRpc::ParamType{ + .type = SocketType::TIPC, + .security = RpcSecurity::RAW, + .clientVersion = clientVersion, + .serverVersion = serverVersion, + // TODO: should we test both versions here? + .singleThreaded = false, + .noKernel = true, + }); + } + } + + return ret; +} + +INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()), BinderRpc::PrintParamInfo); } // namespace android diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index 1f4601010c5d029df6ce34e71c5060c7e6af8b66..885bb45d823c0a7a317dee084ca59e2ab96381a6 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -50,7 +50,8 @@ TEST(BinderRpc, CannotUseNextWireVersion) { TEST(BinderRpc, CanUseExperimentalWireVersion) { auto session = RpcSession::make(); - EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); + EXPECT_EQ(hasExperimentalRpc(), + session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); } TEST_P(BinderRpc, Ping) { @@ -84,7 +85,7 @@ TEST_P(BinderRpc, SeparateRootObject) { GTEST_SKIP() << "This test requires a multi-threaded service"; } - SocketType type = std::get<0>(GetParam()); + SocketType type = GetParam().type; if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) { // we can't get port numbers for unix sockets diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index 642cea440d545ced1223876d99a088893ccb69eb..e59dc82b2b7a17e9400a92fa207b5e9e21092567 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include #include -#include #include #include #include @@ -25,6 +23,7 @@ #include #include "../Debug.h" +#include "../Utils.h" namespace android { @@ -176,7 +175,7 @@ static std::string buildRepr(uint32_t version) { setParcelForRpc(&p, version); kFillFuns[i](&p); - result += base::HexString(p.data(), p.dataSize()); + result += HexString(p.data(), p.dataSize()); } return result; } @@ -263,16 +262,4 @@ TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) { } } -TEST(RpcWire, IfNotExperimentalCodeHasNoExperimentalFeatures) { - if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { - GTEST_SKIP() << "Version is experimental, so experimental features are okay."; - } - - // if we set the wire protocol version to experimental, none of the code - // should introduce a difference (if this fails, it means we have features - // which are enabled under experimental mode, but we aren't actually using - // or testing them!) - checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); -} - } // namespace android diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index c857d62cb2ef8aaadb8af23ddd99c95de43f0567..41cb552e4cddfcb0485154a5260eac6425cf47af 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -28,6 +28,7 @@ #include #pragma clang diagnostic pop +#include #include #include @@ -35,10 +36,12 @@ #include +#include #include #include using namespace std::chrono_literals; // NOLINT - google-build-using-namespace +using android::binder::unique_fd; namespace android { namespace tests { @@ -604,7 +607,10 @@ private: static constexpr const char* getLogTag() { return "SafeInterfaceTest"; } sp getRemoteService() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp binder = defaultServiceManager()->getService(kServiceName); +#pragma clang diagnostic pop sp iface = interface_cast(binder); EXPECT_TRUE(iface != nullptr); @@ -680,16 +686,18 @@ bool fdsAreEquivalent(int a, int b) { TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) { // Create an fd we can use to send and receive from the remote process - base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)}; + unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)}; ASSERT_NE(-1, eventFd); // Determine the maximum number of fds this process can have open struct rlimit limit {}; ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit)); - uint32_t maxFds = static_cast(limit.rlim_cur); + uint64_t maxFds = limit.rlim_cur; + + ALOG(LOG_INFO, "SafeInterfaceTest", "%s max FDs: %" PRIu64, __PRETTY_FUNCTION__, maxFds); // Perform this test enough times to rule out fd leaks - for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) { + for (uint32_t iter = 0; iter < (maxFds + 100); ++iter) { native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/); ASSERT_NE(nullptr, handle); handle->data[0] = dup(eventFd.get()); diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp index 2398e1e1ef2517764ce40d9c261cde6b5c14c7f5..3d993588a426ac0958edf864235dadbf79fae4ac 100644 --- a/libs/binder/tests/binderStabilityTest.cpp +++ b/libs/binder/tests/binderStabilityTest.cpp @@ -155,7 +155,10 @@ TEST(BinderStability, NdkForceDowngradeToLocalStability) { } TEST(BinderStability, ForceDowngradeToVendorStability) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer); +#pragma clang diagnostic pop auto server = interface_cast(serverBinder); ASSERT_NE(nullptr, server.get()); @@ -206,7 +209,10 @@ TEST(BinderStability, ConnectionInfoRequiresManifestEntries) { EXPECT_EQ(connectionInfo, std::nullopt); } TEST(BinderStability, CantCallVendorBinderInSystemContext) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sp serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer); +#pragma clang diagnostic pop auto server = interface_cast(serverBinder); ASSERT_NE(nullptr, server.get()); @@ -310,8 +316,11 @@ static AIBinder_Class* kNdkBadStableBinder = extern "C" void AIBinder_markVendorStability(AIBinder* binder); // <- BAD DO NOT COPY TEST(BinderStability, NdkCantCallVendorBinderInSystemContext) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" SpAIBinder binder = SpAIBinder(AServiceManager_getService( String8(kSystemStabilityServer).c_str())); +#pragma clang diagnostic pop std::shared_ptr remoteServer = aidl::IBinderStabilityTest::fromBinder(binder); diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp index b37030e7d769380ace767ed26fbea0ac4c609ea7..a648c0886df2cf8eeb36df0fb72536ddcc741ee4 100644 --- a/libs/binder/tests/binderTextOutputTest.cpp +++ b/libs/binder/tests/binderTextOutputTest.cpp @@ -20,13 +20,14 @@ #include #include -#include "android-base/file.h" #include "android-base/test_utils.h" #include #include #include +#include "../file.h" + static void CheckMessage(CapturedStderr& cap, const char* expected, bool singleline) { diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index cfaf2a987f41581e0aaf4c8be57714133d4a39f7..0ea4a3faaa6ccca1892daf9e592abfb5dae28681 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -204,7 +204,10 @@ void worker_fx(int num, for (int i = 0; i < server_count; i++) { if (num == i) continue; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" workers.push_back(serviceMgr->getService(generateServiceName(i))); +#pragma clang diagnostic pop } // Run the benchmark if client diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 25e286ce0c09353cdeede4cbb1b24969dd310f92..6301c7484270b776a6fcb2ee7b37901807b7c01f 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -32,7 +32,7 @@ namespace android { TEST(UtilsHost, ExecuteImmediately) { auto result = execute({"echo", "foo"}, nullptr); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_THAT(result->exitCode, Optional(EX_OK)); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -58,7 +58,7 @@ TEST(UtilsHost, ExecuteLongRunning) { EXPECT_GE(elapsedMs, 1000); EXPECT_LT(elapsedMs, 2000); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -83,7 +83,7 @@ TEST(UtilsHost, ExecuteLongRunning2) { EXPECT_GE(elapsedMs, 4000); EXPECT_LT(elapsedMs, 6000); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -104,7 +104,7 @@ TEST(UtilsHost, KillWithSigKill) { return false; }); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_THAT(result->signal, Optional(SIGKILL)); } diff --git a/libs/binder/tests/format.h b/libs/binder/tests/format.h new file mode 100644 index 0000000000000000000000000000000000000000..b5440a43e4c7e163290c8f9b3ba3d47897bd4b9e --- /dev/null +++ b/libs/binder/tests/format.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 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. + */ + +// TODO(b/302723053): remove this header and replace with once b/175635923 is done +// ETA for this blocker is 2023-10-27~2023-11-10. +// Also, remember to remove fmtlib's format.cc from trusty makefiles. + +#if __has_include() +#include +#else +#include + +namespace std { +using fmt::format; +} +#endif \ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 35866adf2027d2523f82595e9888978866446791..83db6c9b6db828a7c42e3635ebb3e46c77ad9c4f 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -16,6 +16,9 @@ aidl_interface { "parcelables/SingleDataParcelable.aidl", "parcelables/GenericDataParcelable.aidl", ], + flags: [ + "-Werror", + ], backend: { java: { enabled: true, @@ -32,7 +35,11 @@ cc_fuzz { host_supported: true, fuzz_config: { - cc: ["smoreland@google.com"], + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + use_for_presubmit: true, }, srcs: [ @@ -104,3 +111,43 @@ cc_library_static { local_include_dirs: ["include_random_parcel"], export_include_dirs: ["include_random_parcel"], } + +cc_library { + name: "libbinder_random_parcel_seeds", + host_supported: true, + vendor_available: true, + target: { + darwin: { + enabled: false, + }, + }, + srcs: [ + "random_parcel_seeds.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + "libcutils", + "libutils", + ], + local_include_dirs: [ + "include_random_parcel_seeds", + ], + export_include_dirs: ["include_random_parcel_seeds"], +} + +cc_binary_host { + name: "binder2corpus", + static_libs: [ + "libbinder_random_parcel_seeds", + ], + srcs: [ + "binder2corpus/binder2corpus.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libutils", + ], +} diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 46d387ce868eb290c110ee3d36db4c66488829b5..08fe071bfb0fe2190e4464e3df55d63dae4171d3 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -21,14 +21,17 @@ #include "parcelables/SingleDataParcelable.h" #include "util.h" -#include #include #include #include #include +#include +#include "../../Utils.h" + +using ::android::HexString; using ::android::status_t; -using ::android::base::HexString; +using ::android::binder::unique_fd; enum ByteEnum : int8_t {}; enum IntEnum : int32_t {}; @@ -305,11 +308,12 @@ std::vector> BINDER_PARCEL_READ_FUNCTIONS { }, PARCEL_READ_NO_STATUS(int, readFileDescriptor), PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor), - PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor), + PARCEL_READ_WITH_STATUS(unique_fd, readUniqueFileDescriptor), - PARCEL_READ_WITH_STATUS(std::unique_ptr>, readUniqueFileDescriptorVector), - PARCEL_READ_WITH_STATUS(std::optional>, readUniqueFileDescriptorVector), - PARCEL_READ_WITH_STATUS(std::vector, readUniqueFileDescriptorVector), + PARCEL_READ_WITH_STATUS(std::unique_ptr>, + readUniqueFileDescriptorVector), + PARCEL_READ_WITH_STATUS(std::optional>, readUniqueFileDescriptorVector), + PARCEL_READ_WITH_STATUS(std::vector, readUniqueFileDescriptorVector), [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { size_t len = provider.ConsumeIntegral(); diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md new file mode 100644 index 0000000000000000000000000000000000000000..59bf9f31c0b1928f627e82fee9c1fd6ad84bbc6d --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md @@ -0,0 +1,31 @@ +# binder2corpus + +This tool converts recordings generated by record_binder tool to fuzzer seeds for fuzzService. + +# Steps to add corpus: + +## Start recording the service binder +ex. record_binder start manager + +## Run test on device or keep device idle +ex. atest servicemanager_test + +## Stop the recording +record_binder stop manager + +## Pull the recording on host +Recordings are present on device at /data/local/recordings/. Use adb pull. +Use inspect command of record_binder to check if there are some transactions captured. +ex. record_binder inspect manager + +## run corpus generator tool +binder2corpus + +## Build fuzzer and sync data directory +ex. m servicemanager_fuzzer && adb sync data + +## Push corpus on device +ex. adb push servicemanager_fuzzer_corpus/ /data/fuzz/x86_64/servicemanager_fuzzer/ + +## Run fuzzer with corpus directory as argument +ex. adb shell /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer_corpus \ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp new file mode 100644 index 0000000000000000000000000000000000000000..57521f4a7375d9089c85ac30151e6d7f41fc2fb4 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 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 "../../FileUtils.h" + +#include +#include +#include + +#include + +#include +#include + +using android::generateSeedsFromRecording; +using android::status_t; +using android::binder::unique_fd; +using android::binder::debug::RecordedTransaction; + +status_t generateCorpus(const char* recordingPath, const char* corpusDir) { + unique_fd fd(open(recordingPath, O_RDONLY)); + if (!fd.ok()) { + std::cerr << "Failed to open recording file at path " << recordingPath + << " with error: " << strerror(errno) << '\n'; + return android::BAD_VALUE; + } + + if (auto res = mkdir(corpusDir, 0766); res != 0) { + std::cerr + << "Failed to create corpus directory at path. Delete directory if already exists: " + << corpusDir << std::endl; + return android::BAD_VALUE; + } + + int transactionNumber = 0; + while (auto transaction = RecordedTransaction::fromFile(fd)) { + ++transactionNumber; + std::string filePath = std::string(corpusDir) + std::string("transaction_") + + std::to_string(transactionNumber); + constexpr int openFlags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC; + unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666)); + if (!corpusFd.ok()) { + std::cerr << "Failed to open fd. Path " << filePath + << " with error: " << strerror(errno) << std::endl; + return android::UNKNOWN_ERROR; + } + generateSeedsFromRecording(corpusFd, transaction.value()); + } + + if (transactionNumber == 0) { + std::cerr << "No valid transaction has been found in recording file: " << recordingPath + << std::endl; + return android::BAD_VALUE; + } + + return android::NO_ERROR; +} + +void printHelp(const char* toolName) { + std::cout << "Usage: \n\n" + << toolName + << " \n\n*Use " + "record_binder tool for recording binder transactions." + << std::endl; +} + +int main(int argc, char** argv) { + if (argc != 3) { + printHelp(argv[0]); + return 1; + } + const char* sourcePath = argv[1]; + const char* corpusDir = argv[2]; + if (android::NO_ERROR != generateCorpus(sourcePath, corpusDir)) { + std::cerr << "Failed to generate fuzzer corpus." << std::endl; + return 1; + } + return 0; +} diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp index 438e8ae9b23ed41d0b6df4f5b8d6007191b04ec9..cdc8bccf0c8f67125147f80ce4df31750d93b2af 100644 --- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp +++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp @@ -18,12 +18,13 @@ #include "hwbinder.h" #include "util.h" -#include #include #include +#include "../../Utils.h" + using ::android::status_t; -using ::android::base::HexString; +using ::android::HexString; // TODO: support scatter-gather types diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h index a9a61974396dc55ec19fcba08e5ea99e0f7bc271..cb37cfaa2732d0c8303af10b40eb68e17871d150 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h @@ -19,7 +19,17 @@ #include #include +#include + namespace android { + +/** + * See fuzzService, but fuzzes multiple services at the same time. + * + * Consumes providers. + */ +void fuzzService(const std::vector>& binders, FuzzedDataProvider&& provider); + /** * Based on the random data in provider, construct an arbitrary number of * Parcel objects and send them to the service in serial. @@ -34,4 +44,5 @@ namespace android { * } */ void fuzzService(const sp& binder, FuzzedDataProvider&& provider); + } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h index f2b782337c0524527de1fc5b9b81d48b0ee26575..d8bf87a58c4c40098152129efea496215345c875 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h @@ -16,10 +16,21 @@ #pragma once +#include #include #include +#include + namespace android { + +/** + * See fuzzService, but fuzzes multiple services at the same time. + * + * Consumes providers. + */ +void fuzzService(const std::vector& binders, FuzzedDataProvider&& provider); + /** * Based on the random data in provider, construct an arbitrary number of * Parcel objects and send them to the service in serial. diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h index 6ea970825bb63f04188e5d73b0d69661fc10feeb..8d1299d92adc90f7b457bb41a8f6639de22047ec 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h @@ -16,7 +16,7 @@ #pragma once -#include +#include #include #include @@ -27,6 +27,6 @@ namespace android { // get a random FD for use in fuzzing, of a few different specific types // // may return multiple FDs (e.g. pipe), but will always return at least one -std::vector getRandomFds(FuzzedDataProvider* provider); +std::vector getRandomFds(FuzzedDataProvider* provider); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 27587a9162472d603ea19f9f8344e6244f06f77f..2812da79faaa0a679d86f51741834e0abd03e407 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -27,7 +27,7 @@ namespace android { struct RandomParcelOptions { std::function writeHeader; std::vector> extraBinders; - std::vector extraFds; + std::vector extraFds; }; /** diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h new file mode 100644 index 0000000000000000000000000000000000000000..694b68d25e7ebd19d25eaa8ec2d463b8a87460c2 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 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 "../../../../file.h" + +#include + +#include +#include +#include + +#include + +#include + +using android::Parcel; +using std::vector; + +namespace android { +namespace impl { +// computes the bytes so that if they are passed to FuzzedDataProvider and +// provider.ConsumeIntegralInRange(min, max) is called, it will return val +template +void writeReversedBuffer(std::vector& integralBuffer, T min, T max, T val); + +// Calls writeInBuffer method with min and max numeric limits of type T. This method +// is reversal of ConsumeIntegral() in FuzzedDataProvider +template +void writeReversedBuffer(std::vector& integralBuffer, T val); +} // namespace impl +void generateSeedsFromRecording(binder::borrowed_fd fd, + const binder::debug::RecordedTransaction& transaction); +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 8bef33f2ca0e0311803aa02dc8ff5ed35b47e544..02e69cc37189a50579d5d081b6cd724c671589a7 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -21,29 +21,61 @@ #include #include +#include + +using android::binder::unique_fd; + namespace android { void fuzzService(const sp& binder, FuzzedDataProvider&& provider) { - sp target; + fuzzService(std::vector>{binder}, std::move(provider)); +} +void fuzzService(const std::vector>& binders, FuzzedDataProvider&& provider) { RandomParcelOptions options{ - .extraBinders = {binder}, + .extraBinders = binders, .extraFds = {}, }; + // Reserved bytes so that we don't have to change fuzzers and seed corpus if + // we introduce anything new in fuzzService. + std::vector reservedBytes = provider.ConsumeBytes(8); + (void)reservedBytes; + + // always refresh the calling identity, because we sometimes set it below, but also, + // the code we're fuzzing might reset it + IPCThreadState::self()->clearCallingIdentity(); + + // Always take so that a perturbation of just the one ConsumeBool byte will always + // take the same path, but with a different UID. Without this, the fuzzer needs to + // guess both the change in value and the shift at the same time. + int64_t maybeSetUid = provider.PickValueInArray( + {static_cast(AID_ROOT) << 32, static_cast(AID_SYSTEM) << 32, + provider.ConsumeIntegralInRange(static_cast(AID_ROOT) << 32, + static_cast(AID_USER) << 32), + provider.ConsumeIntegral()}); + if (provider.ConsumeBool()) { // set calling uid - IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral()); + IPCThreadState::self()->restoreCallingIdentity(maybeSetUid); } while (provider.remaining_bytes() > 0) { // Most of the AIDL services will have small set of transaction codes. + // TODO(b/295942369) : Add remaining transact codes from IBinder.h uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral() - : provider.ConsumeIntegralInRange(0, 100); + : provider.ConsumeBool() + ? provider.ConsumeIntegralInRange(0, 100) + : provider.PickValueInArray( + {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, + IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, + IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, + IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); uint32_t flags = provider.ConsumeIntegral(); Parcel data; // for increased fuzz coverage - data.setEnforceNoDataAvail(provider.ConsumeBool()); + data.setEnforceNoDataAvail(false); + data.setServiceFuzzing(); sp target = options.extraBinders.at( provider.ConsumeIntegralInRange(0, options.extraBinders.size() - 1)); @@ -61,7 +93,8 @@ void fuzzService(const sp& binder, FuzzedDataProvider&& provider) { Parcel reply; // for increased fuzz coverage - reply.setEnforceNoDataAvail(provider.ConsumeBool()); + reply.setEnforceNoDataAvail(false); + reply.setServiceFuzzing(); (void)target->transact(code, data, &reply, flags); // feed back in binders and fds that are returned from the service, so that @@ -72,12 +105,11 @@ void fuzzService(const sp& binder, FuzzedDataProvider&& provider) { retBinders.end()); auto retFds = reply.debugReadAllFileDescriptors(); for (size_t i = 0; i < retFds.size(); i++) { - options.extraFds.push_back(base::unique_fd(dup(retFds[i]))); + options.extraFds.push_back(unique_fd(dup(retFds[i]))); } } // invariants - auto ps = ProcessState::selfOrNull(); if (ps) { CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount()) diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp index a1fb70131e87c429180cd26b715bfc796de99a33..84b9ff684f6a04c338aea4c45f2074237290dfdb 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp @@ -22,8 +22,20 @@ // and APEX users, but we need access to it to fuzz. #include "../../ndk/ibinder_internal.h" +using android::IBinder; +using android::sp; + namespace android { +void fuzzService(const std::vector& binders, FuzzedDataProvider&& provider) { + std::vector> cppBinders; + for (const auto& binder : binders) { + cppBinders.push_back(binder.get()->getBinder()); + } + + fuzzService(cppBinders, std::move(provider)); +} + void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { fuzzService(binder->getBinder(), std::move(provider)); } @@ -32,9 +44,14 @@ void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { extern "C" { // This API is used by fuzzers to automatically fuzz aidl services -void fuzzRustService(void* binder, const uint8_t* data, size_t len) { - AIBinder* aiBinder = static_cast(binder); +void fuzzRustService(void** binders, size_t numBinders, const uint8_t* data, size_t len) { + std::vector> cppBinders; + for (size_t binderIndex = 0; binderIndex < numBinders; ++binderIndex) { + AIBinder* aiBinder = static_cast(binders[binderIndex]); + cppBinders.push_back(aiBinder->getBinder()); + } + FuzzedDataProvider provider(data, len); - android::fuzzService(aiBinder, std::move(provider)); + android::fuzzService(cppBinders, std::move(provider)); } } // extern "C" diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index bef4ab6e9940172f88b1ca80d55d948d918a721a..5b1e9eac23a4a4c80764a7ce520e84803edcf476 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -22,7 +22,6 @@ #include -#include #include #include #include @@ -34,10 +33,12 @@ #include #include +#include "../../Utils.h" + using android::fillRandomParcel; using android::RandomParcelOptions; using android::sp; -using android::base::HexString; +using android::HexString; void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) { diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl index dd08f72470a9cab292d36aae2a84cd6902ac9b1f..9884dbb1afdbbe9b2636920be597e9b462c6937c 100644 --- a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl +++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl @@ -17,7 +17,7 @@ package parcelables; parcelable GenericDataParcelable { enum JustSomeEnum { - SOME_ENUMERATOR, + ONE_ENUMERATOR, ANOTHER_ENUMERATOR, MAYBE_ONE_MORE_ENUMERATOR, } diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index e4dbb2d955b589ad5a747159b30ff4cf4e040709..c7d15337b517314600a544203d6e0d7c8b2899f0 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,46 +23,73 @@ namespace android { -using base::unique_fd; +using binder::unique_fd; std::vector getRandomFds(FuzzedDataProvider* provider) { const char* fdType; std::vector fds = provider->PickValueInArray< - std::function()>>({ - [&]() { - fdType = "ashmem"; - std::vector ret; - ret.push_back(unique_fd( - ashmem_create_region("binder test region", - provider->ConsumeIntegralInRange(0, 4096)))); - return ret; - }, - [&]() { - fdType = "/dev/null"; - std::vector ret; - ret.push_back(unique_fd(open("/dev/null", O_RDWR))); - return ret; - }, - [&]() { - fdType = "pipefd"; - - int pipefds[2]; - - int flags = O_CLOEXEC; - if (provider->ConsumeBool()) flags |= O_DIRECT; - if (provider->ConsumeBool()) flags |= O_NONBLOCK; - - CHECK_EQ(0, pipe2(pipefds, flags)) << flags; - - if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]); - - std::vector ret; - ret.push_back(unique_fd(pipefds[0])); - ret.push_back(unique_fd(pipefds[1])); - return ret; - }, - })(); + std::function()>>( + {[&]() { + fdType = "ashmem"; + std::vector ret; + ret.push_back(unique_fd( + ashmem_create_region("binder test region", + provider->ConsumeIntegralInRange(0, 4096)))); + return ret; + }, + [&]() { + fdType = "/dev/null"; + std::vector ret; + ret.push_back(unique_fd(open("/dev/null", O_RDWR))); + return ret; + }, + [&]() { + fdType = "pipefd"; + + int pipefds[2]; + + int flags = O_CLOEXEC; + if (provider->ConsumeBool()) flags |= O_DIRECT; + + // TODO(b/236812909): also test blocking + if (true) flags |= O_NONBLOCK; + + CHECK_EQ(0, pipe2(pipefds, flags)) << flags; + + if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]); + + std::vector ret; + ret.push_back(unique_fd(pipefds[0])); + ret.push_back(unique_fd(pipefds[1])); + return ret; + }, + [&]() { + fdType = "tempfd"; + char name[PATH_MAX]; +#if defined(__ANDROID__) + snprintf(name, sizeof(name), "/data/local/tmp/android-tempfd-test-%d-XXXXXX", + getpid()); +#else + snprintf(name, sizeof(name), "/tmp/android-tempfd-test-%d-XXXXXX", getpid()); +#endif + int fd = mkstemp(name); + CHECK_NE(fd, -1) << "Failed to create file " << name << ", errno: " << errno; + unlink(name); + if (provider->ConsumeBool()) { + CHECK_NE(TEMP_FAILURE_RETRY( + ftruncate(fd, + provider->ConsumeIntegralInRange(0, 4096))), + -1) + << "Failed to truncate file, errno: " << errno; + } + + std::vector ret; + ret.push_back(unique_fd(fd)); + return ret; + } + + })(); for (const auto& fd : fds) CHECK(fd.ok()) << fd.get() << " " << fdType; diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index f0beed234bc34e47c863528ac2bddb4f63086746..4e58dc48993f07fdf01b46bb593725e50aa52962 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -23,6 +23,8 @@ #include #include +using android::binder::unique_fd; + namespace android { static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { @@ -66,8 +68,13 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti }, // write FD [&]() { + // b/296516864 - Limit number of objects written to a parcel. + if (p->objectsCount() > 100) { + return; + } + if (options->extraFds.size() > 0 && provider.ConsumeBool()) { - const base::unique_fd& fd = options->extraFds.at( + const unique_fd& fd = options->extraFds.at( provider.ConsumeIntegralInRange(0, options->extraFds.size() - 1)); @@ -78,11 +85,10 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti return; } - std::vector fds = getRandomFds(&provider); + std::vector fds = getRandomFds(&provider); CHECK(OK == p->writeFileDescriptor(fds.begin()->release(), true /*takeOwnership*/)); - options->extraFds.insert(options->extraFds.end(), std::make_move_iterator(fds.begin() + 1), std::make_move_iterator(fds.end())); @@ -90,6 +96,11 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti }, // write binder [&]() { + // b/296516864 - Limit number of objects written to a parcel. + if (p->objectsCount() > 100) { + return; + } + sp binder; if (options->extraBinders.size() > 0 && provider.ConsumeBool()) { binder = options->extraBinders.at( diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b3c80642ba10ade2dbb6333cdac232f1aa8524b --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2023 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 + +#include + +#include + +#include "../../file.h" + +using android::binder::borrowed_fd; +using android::binder::WriteFully; + +namespace android { +namespace impl { +template +std::vector reverseBytes(T min, T max, T val) { + uint64_t range = static_cast(max) - min; + uint64_t result = val - min; + size_t offset = 0; + + std::vector reverseData; + uint8_t reversed = 0; + reversed |= result; + + while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0) { + reverseData.push_back(reversed); + reversed = 0; + reversed |= (result >> CHAR_BIT); + result = result >> CHAR_BIT; + offset += CHAR_BIT; + } + + return std::move(reverseData); +} + +template +void writeReversedBuffer(std::vector& integralBuffer, T min, T max, T val) { + std::vector reversedData = reverseBytes(min, max, val); + // ConsumeIntegral Calls read buffer from the end. Keep inserting at the front of the buffer + // so that we can align fuzzService operations with seed generation for readability. + integralBuffer.insert(integralBuffer.begin(), reversedData.begin(), reversedData.end()); +} + +template +void writeReversedBuffer(std::vector& integralBuffer, T val) { + // For ConsumeIntegral() calls, FuzzedDataProvider uses numeric limits min and max + // as range + writeReversedBuffer(integralBuffer, std::numeric_limits::min(), + std::numeric_limits::max(), val); +} + +} // namespace impl + +void generateSeedsFromRecording(borrowed_fd fd, + const binder::debug::RecordedTransaction& transaction) { + // Write Reserved bytes for future use + std::vector reservedBytes(8); + CHECK(WriteFully(fd, reservedBytes.data(), reservedBytes.size())) << fd.get(); + + std::vector integralBuffer; + + // Write UID array : Array elements are initialized in the order that they are declared + // UID array index 2 element + // int64_t aidRoot = 0; + impl::writeReversedBuffer(integralBuffer, static_cast(AID_ROOT) << 32, + static_cast(AID_USER) << 32, + static_cast(AID_ROOT) << 32); + + // UID array index 3 element + impl::writeReversedBuffer(integralBuffer, static_cast(AID_ROOT) << 32); + + // always pick AID_ROOT -> index 0 + size_t uidIndex = 0; + impl::writeReversedBuffer(integralBuffer, static_cast(0), static_cast(3), + uidIndex); + + // Never set uid in seed corpus + uint8_t writeUid = 0; + impl::writeReversedBuffer(integralBuffer, writeUid); + + // Read random code. this will be from recorded transaction + uint8_t selectCode = 1; + impl::writeReversedBuffer(integralBuffer, selectCode); + + // Get from recorded transaction + uint32_t code = transaction.getCode(); + impl::writeReversedBuffer(integralBuffer, code); + + // Get from recorded transaction + uint32_t flags = transaction.getFlags(); + impl::writeReversedBuffer(integralBuffer, flags); + + // always fuzz primary binder + size_t extraBindersIndex = 0; + impl::writeReversedBuffer(integralBuffer, static_cast(0), static_cast(0), + extraBindersIndex); + + const Parcel& dataParcel = transaction.getDataParcel(); + + // This buffer holds the bytes which will be used for fillRandomParcel API + std::vector fillParcelBuffer; + + // Don't take rpc path + uint8_t rpcBranch = 0; + impl::writeReversedBuffer(fillParcelBuffer, rpcBranch); + + // Implicit branch on this path -> options->writeHeader(p, provider) + uint8_t writeHeaderInternal = 0; + impl::writeReversedBuffer(fillParcelBuffer, writeHeaderInternal); + + // Choose to write data in parcel + size_t fillFuncIndex = 0; + impl::writeReversedBuffer(fillParcelBuffer, static_cast(0), static_cast(2), + fillFuncIndex); + + // Write parcel data size from recorded transaction + size_t toWrite = transaction.getDataParcel().dataBufferSize(); + impl::writeReversedBuffer(fillParcelBuffer, static_cast(0), toWrite, toWrite); + + // Write parcel data with size towrite from recorded transaction + CHECK(WriteFully(fd, dataParcel.data(), toWrite)) << fd.get(); + + // Write Fill Parcel buffer size in integralBuffer so that fuzzService knows size of data + size_t subDataSize = toWrite + fillParcelBuffer.size(); + impl::writeReversedBuffer(integralBuffer, static_cast(0), subDataSize, subDataSize); + + // Write fill parcel buffer + CHECK(WriteFully(fd, fillParcelBuffer.data(), fillParcelBuffer.size())) << fd.get(); + + // Write the integralBuffer to data + CHECK(WriteFully(fd, integralBuffer.data(), integralBuffer.size())) << fd.get(); +} +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..690c39afc908f12d660d5d7ed8dbe29e539ba6a6 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp @@ -0,0 +1,64 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +aidl_interface { + name: "testServiceIface", + host_supported: true, + unstable: true, + srcs: [ + "ITestService.aidl", + ], + backend: { + java: { + enabled: true, + platform_apis: true, + }, + rust: { + enabled: true, + }, + }, +} + +// Adding this fuzzer to test the fuzzService functionality +cc_fuzz { + name: "test_service_fuzzer_should_crash", + defaults: [ + "service_fuzzer_defaults", + ], + static_libs: [ + "liblog", + "testServiceIface-cpp", + ], + host_supported: true, + srcs: ["TestServiceFuzzer.cpp"], + fuzz_config: { + triage_assignee: "waghpawan@google.com", + + // This fuzzer should be used only test fuzzService locally + fuzz_on_haiku_host: false, + fuzz_on_haiku_device: false, + }, +} + +sh_test_host { + name: "fuzz_service_test", + src: "run_fuzz_service_test.sh", + filename: "run_fuzz_service_test.sh", + test_config: "fuzz_service_test_config.xml", + data_bins: [ + "test_service_fuzzer_should_crash", + ], + required: [ + "test_service_fuzzer_should_crash", + ], + target: { + linux_bionic: { + enabled: false, + }, + darwin: { + enabled: false, + }, + }, + test_suites: ["general-tests"], +} diff --git a/services/inputflinger/host/main.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl similarity index 65% rename from services/inputflinger/host/main.cpp rename to libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl index 0a517cc5d92bbf755169b9a0667bc2e069cd8e03..5089ae50044f69bf1eacb4bfbcaf9eb2c5e6915e 100644 --- a/services/inputflinger/host/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2023 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. @@ -14,13 +14,13 @@ * limitations under the License. */ -#include -#include "InputFlinger.h" +interface ITestService { -using namespace android; + void setIntData(int input); -int main(int, char**) { - ProcessState::self()->setThreadPoolMaxThreadCount(4); - BinderService::publishAndJoinThreadPool(true); - return 0; + void setCharData(char input); + + void setBooleanData(boolean input); + + void setService(ITestService service); } diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2fa581822fec65aff158162c5ee1cb8d0215dc9 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2023 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 +#include + +#include +#include + +#include + +using android::binder::Status; + +namespace android { + +enum class CrashType { + NONE, + ON_PLAIN, + ON_BINDER, + ON_KNOWN_UID, + ON_SYSTEM_AID, + ON_ROOT_AID, + ON_DUMP_TRANSACT, + ON_SHELL_CMD_TRANSACT, + CRASH_ALWAYS, +}; + +// This service is to verify that fuzzService is functioning properly +class TestService : public BnTestService { +public: + TestService(CrashType crash) : mCrash(crash) {} + + void onData() { + switch (mCrash) { + case CrashType::ON_PLAIN: { + LOG_ALWAYS_FATAL("Expected crash, PLAIN."); + break; + } + case CrashType::ON_KNOWN_UID: { + if (IPCThreadState::self()->getCallingUid() == getuid()) { + LOG_ALWAYS_FATAL("Expected crash, KNOWN_UID."); + } + break; + } + case CrashType::ON_SYSTEM_AID: { + if (IPCThreadState::self()->getCallingUid() == AID_SYSTEM) { + LOG_ALWAYS_FATAL("Expected crash, AID_SYSTEM."); + } + break; + } + case CrashType::ON_ROOT_AID: { + if (IPCThreadState::self()->getCallingUid() == AID_ROOT) { + LOG_ALWAYS_FATAL("Expected crash, AID_ROOT."); + } + break; + } + default: + break; + } + } + + Status setIntData(int /*input*/) override { + onData(); + return Status::ok(); + } + + Status setCharData(char16_t /*input*/) override { + onData(); + return Status::ok(); + } + + Status setBooleanData(bool /*input*/) override { + onData(); + return Status::ok(); + } + + Status setService(const sp& service) override { + onData(); + if (mCrash == CrashType::ON_BINDER && service != nullptr) { + LOG_ALWAYS_FATAL("Expected crash, BINDER."); + } + return Status::ok(); + } + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override { + if (mCrash == CrashType::ON_DUMP_TRANSACT && code == DUMP_TRANSACTION) { + LOG_ALWAYS_FATAL("Expected crash, DUMP."); + } else if (mCrash == CrashType::ON_SHELL_CMD_TRANSACT && + code == SHELL_COMMAND_TRANSACTION) { + LOG_ALWAYS_FATAL("Expected crash, SHELL_CMD."); + } + return BnTestService::onTransact(code, data, reply, flags); + } + +private: + CrashType mCrash; +}; + +CrashType gCrashType = CrashType::NONE; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + if (*argc < 2) { + // This fuzzer is also used as test fuzzer to check infra pipeline. + // It should always run and find a crash in TestService. + gCrashType = CrashType::CRASH_ALWAYS; + return 0; + } + + std::string arg = std::string((*argv)[1]); + + // ignore first argument, because we consume it + (*argv)[1] = (*argv[0]); + (*argc)--; + (*argv)++; + + if (arg == "PLAIN") { + gCrashType = CrashType::ON_PLAIN; + } else if (arg == "KNOWN_UID") { + gCrashType = CrashType::ON_KNOWN_UID; + } else if (arg == "AID_SYSTEM") { + gCrashType = CrashType::ON_SYSTEM_AID; + } else if (arg == "AID_ROOT") { + gCrashType = CrashType::ON_ROOT_AID; + } else if (arg == "BINDER") { + gCrashType = CrashType::ON_BINDER; + } else if (arg == "DUMP") { + gCrashType = CrashType::ON_DUMP_TRANSACT; + } else if (arg == "SHELL_CMD") { + gCrashType = CrashType::ON_SHELL_CMD_TRANSACT; + } else { + printf("INVALID ARG\n"); + exit(0); // success because this is a crash test + } + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (gCrashType == CrashType::CRASH_ALWAYS) { + LOG_ALWAYS_FATAL("Expected crash, This fuzzer will always crash."); + } + auto service = sp::make(gCrashType); + fuzzService(service, FuzzedDataProvider(data, size)); + return 0; +} + +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml new file mode 100644 index 0000000000000000000000000000000000000000..19eb33a6353cf3182f78116d58ff26c09e4b8ff6 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml @@ -0,0 +1,22 @@ + + + + diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..5d68fe172e1e1ac80cfd1b81b1274e718977343a --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +color_success=$'\E'"[0;32m" +color_failed=$'\E'"[0;31m" +color_reset=$'\E'"[00m" + +FUZZER_NAME=test_service_fuzzer_should_crash +FUZZER_OUT=fuzzer-output + +if [ ! -f "$FUZZER_NAME" ] +then + echo -e "${color_failed}Binary $FUZZER_NAME does not exist" + echo "${color_reset}" + exit 1 +fi + +for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER DUMP SHELL_CMD; do + echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE" + + ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=60 &>"$FUZZER_OUT" + + echo "INFO: Searching fuzzer output for expected crashes" + if grep -q "Expected crash, $CRASH_TYPE." "$FUZZER_OUT" + then + echo -e "${color_success}Success: Found expected crash. fuzzService test successful!" + else + echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash" + echo "${color_reset}" + exit 1 + fi +done diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp index 71e847fd1ed6d5a06b9e190b97b8ef9fb4bc8c41..ab72bfd2b5af116d5de7d9eb3bd708688cfdfd30 100644 --- a/libs/binder/tests/rpc_fuzzer/Android.bp +++ b/libs/binder/tests/rpc_fuzzer/Android.bp @@ -25,13 +25,14 @@ cc_fuzz { "libbase", "libcutils", "liblog", + "libbinder_test_utils", "libbinder_tls_static", "libbinder_tls_test_utils", "libssl_fuzz_unsafe", "libcrypto_fuzz_unsafe", ], cflags: [ - "-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE" // for RAND_reset_for_fuzzing + "-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE", // for RAND_reset_for_fuzzing ], target: { android: { diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp index b8ae84d5c0da272c5769bcbe3e494204ad702eab..50fc2f21f17de981796da1e5b07306fdfad5057c 100644 --- a/libs/binder/tests/rpc_fuzzer/main.cpp +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include +#include "../FileUtils.h" + #include -#include #include #include #include @@ -24,13 +24,18 @@ #include #include #include +#include #include #include #include #include +#include #include +using android::binder::GetExecutableDirectory; +using android::binder::unique_fd; + namespace android { static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") + @@ -76,12 +81,12 @@ struct ServerAuth { ServerAuth readServerKeyAndCert() { ServerAuth ret; - auto keyPath = android::base::GetExecutableDirectory() + "/data/server.key"; + auto keyPath = GetExecutableDirectory() + "/data/server.key"; bssl::UniquePtr keyBio(BIO_new_file(keyPath.c_str(), "r")); ret.pkey.reset(PEM_read_bio_PrivateKey(keyBio.get(), nullptr, passwordCallback, nullptr)); CHECK_NE(ret.pkey.get(), nullptr); - auto certPath = android::base::GetExecutableDirectory() + "/data/server.crt"; + auto certPath = GetExecutableDirectory() + "/data/server.crt"; bssl::UniquePtr certBio(BIO_new_file(certPath.c_str(), "r")); ret.cert.reset(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr)); CHECK_NE(ret.cert.get(), nullptr); @@ -129,18 +134,18 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { CHECK_LT(kSock.size(), sizeof(addr.sun_path)); memcpy(&addr.sun_path, kSock.c_str(), kSock.size()); - std::vector connections; + std::vector connections; bool hangupBeforeShutdown = provider.ConsumeBool(); // b/260736889 - limit arbitrarily, due to thread resource exhaustion, which currently // aborts. Servers should consider RpcServer::setConnectionFilter instead. - constexpr size_t kMaxConnections = 1000; + constexpr size_t kMaxConnections = 10; while (provider.remaining_bytes() > 0) { if (connections.empty() || (connections.size() < kMaxConnections && provider.ConsumeBool())) { - base::unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); + unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); CHECK_NE(fd.get(), -1); CHECK_EQ(0, TEMP_FAILURE_RETRY( diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index 0035e4ee5aaaf201bf5aa3c680c3576cc06ac682..d3cd528e261aa7c23673ebc7e457a678e11839c0 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp @@ -340,7 +340,10 @@ void worker_fx(int num, int no_process, int iterations, int payload_size, for (int i = 0; i < server_count; i++) { // self service is in-process so just skip if (num == i) continue; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" workers.push_back(serviceMgr->getService(generateServiceName(i))); +#pragma clang diagnostic pop } // Client for each pair iterates here diff --git a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h index 8d2b714b5cf421bdcfd88bf9b004115c09b9bb52..993418adcc1c229d32ef361d51eb677ce5438356 100644 --- a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h @@ -74,7 +74,7 @@ static const std::vectorgetDebugPid(); }, [](FuzzedDataProvider*, const sp& bbinder) -> void { - (void)bbinder->setRpcClientDebug(android::base::unique_fd(), + (void)bbinder->setRpcClientDebug(binder::unique_fd(), sp::make()); }}; diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp index 910c9dc25c8ed2b7db5e0d1f0eda0b9b9fdc595f..a6fd487fe57aa8c2a4102b0bdda8de3fdcca7fdb 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp @@ -51,8 +51,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { sp session = RpcSession::make(); session->setMaxIncomingThreads(1); status_t status; - for (size_t tries = 0; tries < 5; tries++) { - usleep(10000); + + // b/274084938 - ASAN may be slow, wait a while + for (size_t tries = 0; tries < 50; tries++) { + usleep(100000); status = session->setupUnixDomainClient(addr.c_str()); if (status == OK) break; } diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h index 50794310587d72997833aad8432a24bbe450f46e..0a584bfe561598f972a0c4b9af2a44932c8c7350 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h @@ -26,7 +26,6 @@ #include #include -#include #include #include #include diff --git a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp index 09cb2162f742163bf338d7a08658dd487726fa0c..b80ac53ba28ddb33a6f25c5e1f69dbd0b4c064eb 100644 --- a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include "BufferedTextOutput.h" diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h index bf7c61347ffc8fda78b87dfcb6c8f3eaf19bf040..a6dc182588eb08feb1118929e81f0441a9321483 100644 --- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp index e4943662673b64eb362d17a054b8067c45c07c89..87b0fb666268ae3ba2dda7625fdc47b0ead93e40 100644 --- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp @@ -14,19 +14,20 @@ * limitations under the License. */ -#include #include #include #include "fuzzer/FuzzedDataProvider.h" +using android::binder::unique_fd; + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::FILE* intermediateFile = std::tmpfile(); fwrite(data, sizeof(uint8_t), size, intermediateFile); rewind(intermediateFile); int fileNumber = fileno(intermediateFile); - android::base::unique_fd fd(dup(fileNumber)); + unique_fd fd(dup(fileNumber)); auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd); @@ -35,8 +36,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (transaction.has_value()) { intermediateFile = std::tmpfile(); - android::base::unique_fd fdForWriting(fileno(intermediateFile)); - auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + unique_fd fdForWriting(dup(fileno(intermediateFile))); + auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting); std::fclose(intermediateFile); } diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp index 943fb9f2852570be96a6441efc1cfb7eb45aaec6..fa939e68e4f235e59188cbe9d580002ded6a35a5 100644 --- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include #include #include #include @@ -23,6 +22,7 @@ #include "fuzzer/FuzzedDataProvider.h" using android::fillRandomParcel; +using android::binder::unique_fd; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider provider = FuzzedDataProvider(data, size); @@ -54,8 +54,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (transaction.has_value()) { std::FILE* intermediateFile = std::tmpfile(); - android::base::unique_fd fdForWriting(fileno(intermediateFile)); - auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + unique_fd fdForWriting(dup(fileno(intermediateFile))); + auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting); std::fclose(intermediateFile); } diff --git a/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp index 5e3502aacedac2516eba8fe02cbe2f595bd89988..fe099782c05ea56412c3c18dad3c2da3521afd32 100644 --- a/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ +#include "../../file.h" + #include #include #include -#include "android-base/file.h" #include "android-base/test_utils.h" #include diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp index 8ec982345daa886560ce82c8b00856d3785baa8e..a8dabc3a1dcf2f0d49633e65897c497e5a811083 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -22,17 +22,34 @@ #endif #include +#include +#include #include "../OS.h" #include "TrustyStatus.h" -using android::base::Result; +#include -namespace android { +using android::binder::borrowed_fd; +using android::binder::unique_fd; -Result setNonBlocking(android::base::borrowed_fd /*fd*/) { +namespace android::binder::os { + +void trace_begin(uint64_t, const char*) {} + +void trace_end(uint64_t) {} + +uint64_t GetThreadId() { + return 0; +} + +bool report_sysprop_change() { + return false; +} + +status_t setNonBlocking(borrowed_fd /*fd*/) { // Trusty IPC syscalls are all non-blocking by default. - return {}; + return OK; } status_t getRandomBytes(uint8_t* data, size_t size) { @@ -61,16 +78,51 @@ std::unique_ptr makeDefaultRpcTransportCtxFactory() { ssize_t sendMessageOnSocket( const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */, - const std::vector>* /* ancillaryFds */) { + const std::vector>* /* ancillaryFds */) { errno = ENOTSUP; return -1; } ssize_t receiveMessageFromSocket( const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */, - std::vector>* /* ancillaryFds */) { + std::vector>* /* ancillaryFds */) { errno = ENOTSUP; return -1; } -} // namespace android +} // namespace android::binder::os + +int __android_log_print(int prio [[maybe_unused]], const char* tag, const char* fmt, ...) { +#ifdef TRUSTY_USERSPACE +#define trusty_tlog _tlog +#define trusty_vtlog _vtlog +#else + // mapping taken from kernel trusty_log.h (TLOGx) + int kernelLogLevel; + if (prio <= ANDROID_LOG_DEBUG) { + kernelLogLevel = LK_DEBUGLEVEL_ALWAYS; + } else if (prio == ANDROID_LOG_INFO) { + kernelLogLevel = LK_DEBUGLEVEL_SPEW; + } else if (prio == ANDROID_LOG_WARN) { + kernelLogLevel = LK_DEBUGLEVEL_INFO; + } else if (prio == ANDROID_LOG_ERROR) { + kernelLogLevel = LK_DEBUGLEVEL_CRITICAL; + } else { /* prio >= ANDROID_LOG_FATAL */ + kernelLogLevel = LK_DEBUGLEVEL_CRITICAL; + } +#if LK_DEBUGLEVEL_NO_ALIASES + auto LK_DEBUGLEVEL_kernelLogLevel = kernelLogLevel; +#endif + +#define trusty_tlog(...) _tlog(kernelLogLevel, __VA_ARGS__) +#define trusty_vtlog(...) _vtlog(kernelLogLevel, __VA_ARGS__) +#endif + + va_list args; + va_start(args, fmt); + trusty_tlog((tag[0] == '\0') ? "libbinder" : "libbinder-"); + trusty_vtlog(fmt, args); + va_end(args); + + return 1; +} diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp index 68b000849c2ffae4483827186e24dc1e1be94b88..1f857a0edb30686aea51716ed0b56f69d331d7a5 100644 --- a/libs/binder/trusty/RpcServerTrusty.cpp +++ b/libs/binder/trusty/RpcServerTrusty.cpp @@ -27,11 +27,11 @@ #include "../RpcState.h" #include "TrustyStatus.h" -using android::base::unexpected; +using android::binder::unique_fd; namespace android { -android::base::expected, int> RpcServerTrusty::make( +sp RpcServerTrusty::make( tipc_hset* handleSet, std::string&& portName, std::shared_ptr&& portAcl, size_t msgMaxSize, std::unique_ptr rpcTransportCtxFactory) { // Default is without TLS. @@ -39,18 +39,21 @@ android::base::expected, int> RpcServerTrusty::make( rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make(); auto ctx = rpcTransportCtxFactory->newServerCtx(); if (ctx == nullptr) { - return unexpected(ERR_NO_MEMORY); + ALOGE("Failed to create RpcServerTrusty: can't create server context"); + return nullptr; } auto srv = sp::make(std::move(ctx), std::move(portName), std::move(portAcl), msgMaxSize); if (srv == nullptr) { - return unexpected(ERR_NO_MEMORY); + ALOGE("Failed to create RpcServerTrusty: can't create server object"); + return nullptr; } int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps); if (rc != NO_ERROR) { - return unexpected(rc); + ALOGE("Failed to create RpcServerTrusty: can't add service: %d", rc); + return nullptr; } return srv; } @@ -67,7 +70,7 @@ RpcServerTrusty::RpcServerTrusty(std::unique_ptr ctx, std::stri // TODO(b/266741352): follow-up to prevent needing this in the future // Trusty needs to be set to the latest stable version that is in prebuilts there. - mRpcServer->setProtocolVersion(0); + LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0)); if (mPortAcl) { // Initialize the array of pointers to uuids. @@ -129,7 +132,7 @@ int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const u if (chanDup < 0) { return chanDup; } - base::unique_fd clientFd(chanDup); + unique_fd clientFd(chanDup); android::RpcTransportFd transportFd(std::move(clientFd)); std::array addr; diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp index d249b2e1ebe44ce3df10258becae477b5255451c..c74ba0a65e410226c4649d4f3af0bf5328425413 100644 --- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -29,7 +29,9 @@ namespace android { -namespace { +using namespace android::binder::impl; +using android::binder::borrowed_fd; +using android::binder::unique_fd; // RpcTransport for Trusty. class RpcTransportTipcTrusty : public RpcTransport { @@ -47,9 +49,8 @@ public: status_t interruptableWriteFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, - const std::optional>& /*altPoll*/, - const std::vector>* ancillaryFds) - override { + const std::optional>& /*altPoll*/, + const std::vector>* ancillaryFds) override { if (niovs < 0) { return BAD_VALUE; } @@ -117,8 +118,8 @@ public: status_t interruptableReadFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, - const std::optional>& /*altPoll*/, - std::vector>* ancillaryFds) override { + const std::optional>& /*altPoll*/, + std::vector>* ancillaryFds) override { if (niovs < 0) { return BAD_VALUE; } @@ -170,7 +171,7 @@ public: if (ancillaryFds != nullptr) { ancillaryFds->reserve(ancillaryFds->size() + mMessageInfo.num_handles); for (size_t i = 0; i < mMessageInfo.num_handles; i++) { - ancillaryFds->emplace_back(base::unique_fd(msgHandles[i])); + ancillaryFds->emplace_back(unique_fd(msgHandles[i])); } // Clear the saved number of handles so we don't accidentally @@ -282,8 +283,6 @@ public: std::vector getCertificate(RpcCertificateFormat) const override { return {}; } }; -} // namespace - std::unique_ptr RpcTransportCtxFactoryTipcTrusty::newServerCtx() const { return std::make_unique(); } diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json index d8b080f0d4472f64b567fe53c5f869cc839612c9..6e20b8a7ad2ef0bd912a1fc07adfd7e6096b4670 100644 --- a/libs/binder/trusty/binderRpcTest/manifest.json +++ b/libs/binder/trusty/binderRpcTest/manifest.json @@ -1,6 +1,6 @@ { "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b", "app_name": "binderRpcTest", - "min_heap": 163840, - "min_stack": 16384 + "min_heap": 262144, + "min_stack": 20480 } diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk index 975f689d1acb3f0736e716bb2e85274eb08123e4..e46ccfb96773a7390993b4131846ab7f23786653 100644 --- a/libs/binder/trusty/binderRpcTest/rules.mk +++ b/libs/binder/trusty/binderRpcTest/rules.mk @@ -21,6 +21,7 @@ MODULE := $(LOCAL_DIR) MANIFEST := $(LOCAL_DIR)/manifest.json MODULE_SRCS += \ + $(FMTLIB_DIR)/src/format.cc \ $(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \ $(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \ $(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \ diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json index 1c4f7ee0d1eb224a7150d6dcf6262db18c6136fa..d2a1fc0a4898f6c541ae728d338ca0b2eb8194f7 100644 --- a/libs/binder/trusty/binderRpcTest/service/manifest.json +++ b/libs/binder/trusty/binderRpcTest/service/manifest.json @@ -2,7 +2,7 @@ "uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94", "app_name": "binderRpcTestService", "min_heap": 65536, - "min_stack": 16384, + "min_stack": 20480, "mgmt_flags": { "restart_on_exit": true, "non_critical_app": true diff --git a/libs/binder/trusty/binderRpcTest/service/rules.mk b/libs/binder/trusty/binderRpcTest/service/rules.mk index 5d1a51dca252774a2a8107e425ebb1179db263bc..50ae3d25fa287aace5d3fcbae8bfc46f6884e073 100644 --- a/libs/binder/trusty/binderRpcTest/service/rules.mk +++ b/libs/binder/trusty/binderRpcTest/service/rules.mk @@ -21,6 +21,7 @@ MODULE := $(LOCAL_DIR) MANIFEST := $(LOCAL_DIR)/manifest.json MODULE_SRCS := \ + $(FMTLIB_DIR)/src/format.cc \ $(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \ $(LIBBINDER_TESTS_DIR)/binderRpcTestServiceTrusty.cpp \ diff --git a/libs/binder/trusty/binder_rpc_unstable/rules.mk b/libs/binder/trusty/binder_rpc_unstable/rules.mk new file mode 100644 index 0000000000000000000000000000000000000000..d8dbce54e832e0337b35e29595c3637581942f37 --- /dev/null +++ b/libs/binder/trusty/binder_rpc_unstable/rules.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := \ + $(LIBBINDER_DIR)/libbinder_rpc_unstable.cpp \ + +MODULE_EXPORT_INCLUDES += \ + $(LIBBINDER_DIR)/include_rpc_unstable \ + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + trusty/user/base/lib/libstdc++-trusty \ + +include make/library.mk diff --git a/libs/binder/trusty/fuzzer/Android.bp b/libs/binder/trusty/fuzzer/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..4f9b5c4d9d8f26615864c9fd226aa0dad12991bc --- /dev/null +++ b/libs/binder/trusty/fuzzer/Android.bp @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_fuzz { + name: "trusty_binder_fuzzer", + defaults: ["trusty_fuzzer_defaults"], + srcs: [":trusty_tipc_fuzzer"], + cflags: [ + "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"", + "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"", + "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"", + "-DTRUSTY_APP_MAX_CONNECTIONS=1", + ], +} + +cc_fuzz { + name: "trusty_binder_rpc_fuzzer", + defaults: ["trusty_fuzzer_defaults"], + srcs: [":trusty_tipc_fuzzer"], + cflags: [ + "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"", + "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"", + "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"", + "-DTRUSTY_APP_MAX_CONNECTIONS=1", + ], +} + +cc_fuzz { + name: "trusty_binder_fuzzer_multi_connection", + defaults: ["trusty_fuzzer_defaults"], + srcs: [":trusty_tipc_fuzzer"], + cflags: [ + "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"", + "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"", + "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"", + "-DTRUSTY_APP_MAX_CONNECTIONS=10", + ], +} + +cc_fuzz { + name: "trusty_binder_rpc_fuzzer_multi_connection", + defaults: ["trusty_fuzzer_defaults"], + srcs: [":trusty_tipc_fuzzer"], + cflags: [ + "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"", + "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"", + "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"", + "-DTRUSTY_APP_MAX_CONNECTIONS=10", + ], +} diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 6678eb8fecb6c284b36d111c5c4171c44fc90b80..f35d6c225261d67130a050129c5b4c931af3d783 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -16,13 +16,11 @@ #pragma once -#include -#include -#include #include #include #include #include +#include #include #include @@ -54,19 +52,22 @@ public: * The caller is responsible for calling tipc_run_event_loop() to start * the TIPC event loop after creating one or more services here. */ - static android::base::expected, int> make( + static sp make( tipc_hset* handleSet, std::string&& portName, std::shared_ptr&& portAcl, size_t msgMaxSize, std::unique_ptr rpcTransportCtxFactory = nullptr); - void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); } + [[nodiscard]] bool setProtocolVersion(uint32_t version) { + return mRpcServer->setProtocolVersion(version); + } void setSupportedFileDescriptorTransportModes( const std::vector& modes) { mRpcServer->setSupportedFileDescriptorTransportModes(modes); } void setRootObject(const sp& binder) { mRpcServer->setRootObject(binder); } void setRootObjectWeak(const wp& binder) { mRpcServer->setRootObjectWeak(binder); } - void setPerSessionRootObject(std::function(const void*, size_t)>&& object) { + void setPerSessionRootObject( + std::function(wp session, const void*, size_t)>&& object) { mRpcServer->setPerSessionRootObject(std::move(object)); } sp getRootObject() { return mRpcServer->getRootObject(); } @@ -80,7 +81,8 @@ private: // Both this class and RpcServer have multiple non-copyable fields, // including mPortAcl below which can't be copied because mUuidPtrs // holds pointers into it - DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty); + RpcServerTrusty(const RpcServerTrusty&) = delete; + void operator=(const RpcServerTrusty&) = delete; friend sp; explicit RpcServerTrusty(std::unique_ptr ctx, std::string&& portName, diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h deleted file mode 100644 index de84617343a4dac9495bedc3a946e4fa42d9af9a..0000000000000000000000000000000000000000 --- a/libs/binder/trusty/include/log/log.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 - -#define BINDER_LOG_LEVEL_NONE 0 -#define BINDER_LOG_LEVEL_NORMAL 1 -#define BINDER_LOG_LEVEL_VERBOSE 2 - -#ifndef BINDER_LOG_LEVEL -#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL -#endif // BINDER_LOG_LEVEL - -#ifndef TLOG_TAG -#ifdef LOG_TAG -#define TLOG_TAG "libbinder-" LOG_TAG -#else // LOG_TAG -#define TLOG_TAG "libbinder" -#endif // LOG_TAG -#endif // TLOG_TAG - -#include -#include - -static inline void __ignore_va_args__(...) {} - -#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL -#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__) -#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__) -#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__) -#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__) -#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL -#define ALOGD(fmt, ...) \ - while (0) { \ - __ignore_va_args__(__VA_ARGS__); \ - } -#define ALOGI(fmt, ...) \ - while (0) { \ - __ignore_va_args__(__VA_ARGS__); \ - } -#define ALOGW(fmt, ...) \ - while (0) { \ - __ignore_va_args__(__VA_ARGS__); \ - } -#define ALOGE(fmt, ...) \ - while (0) { \ - __ignore_va_args__(__VA_ARGS__); \ - } -#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL - -#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE -#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO) -#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__) -#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE -#define IF_ALOGV() if (false) -#define ALOGV(fmt, ...) \ - while (0) { \ - __ignore_va_args__(__VA_ARGS__); \ - } -#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE - -#define ALOGI_IF(cond, ...) \ - do { \ - if (cond) { \ - ALOGI(#cond ": " __VA_ARGS__); \ - } \ - } while (0) -#define ALOGE_IF(cond, ...) \ - do { \ - if (cond) { \ - ALOGE(#cond ": " __VA_ARGS__); \ - } \ - } while (0) -#define ALOGW_IF(cond, ...) \ - do { \ - if (cond) { \ - ALOGW(#cond ": " __VA_ARGS__); \ - } \ - } while (0) - -#define LOG_ALWAYS_FATAL(fmt, ...) \ - do { \ - TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \ - abort(); \ - } while (0) -#define LOG_ALWAYS_FATAL_IF(cond, ...) \ - do { \ - if (cond) { \ - LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \ - } \ - } while (0) -#define LOG_FATAL(fmt, ...) \ - do { \ - TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \ - abort(); \ - } while (0) -#define LOG_FATAL_IF(cond, ...) \ - do { \ - if (cond) { \ - LOG_FATAL(#cond ": " __VA_ARGS__); \ - } \ - } while (0) - -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) - -#define android_errorWriteLog(tag, subTag) \ - do { \ - TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \ - } while (0) - -// Override the definition of __assert from binder_status.h -#ifndef __BIONIC__ -#undef __assert -#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str) -#endif // __BIONIC__ diff --git a/libs/binder/trusty/include_mock/trusty_log.h b/libs/binder/trusty/include_mock/trusty_log.h index d51e75280c7585686bf0838e1b0642fea47bc90e..9aa90310c0363458299144d9e0e687ff28d0f7b3 100644 --- a/libs/binder/trusty/include_mock/trusty_log.h +++ b/libs/binder/trusty/include_mock/trusty_log.h @@ -24,3 +24,6 @@ #define TLOGW(fmt, ...) printf(fmt, ##__VA_ARGS__) #define TLOGE(fmt, ...) printf(fmt, ##__VA_ARGS__) #define TLOGC(fmt, ...) printf(fmt, ##__VA_ARGS__) + +#define _tlog(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define _vtlog(fmt, args) vprintf(fmt, args) diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk index ab7a50d3f7357ca56db6a9f293734c94181c1c90..788184d2ce8d6adcbd9bc8cb1b59d44f5b8ba4ad 100644 --- a/libs/binder/trusty/kernel/rules.mk +++ b/libs/binder/trusty/kernel/rules.mk @@ -18,13 +18,14 @@ LOCAL_DIR := $(GET_LOCAL_DIR) MODULE := $(LOCAL_DIR) LIBBINDER_DIR := frameworks/native/libs/binder +# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release LIBBASE_DIR := system/libbase -LIBCUTILS_DIR := system/core/libcutils -LIBUTILS_DIR := system/core/libutils +LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub +LIBUTILS_BINDER_DIR := system/core/libutils/binder FMTLIB_DIR := external/fmtlib MODULE_SRCS := \ - $(LOCAL_DIR)/../logging.cpp \ + $(LOCAL_DIR)/../OS.cpp \ $(LOCAL_DIR)/../TrustyStatus.cpp \ $(LIBBINDER_DIR)/Binder.cpp \ $(LIBBINDER_DIR)/BpBinder.cpp \ @@ -35,24 +36,14 @@ MODULE_SRCS := \ $(LIBBINDER_DIR)/Stability.cpp \ $(LIBBINDER_DIR)/Status.cpp \ $(LIBBINDER_DIR)/Utils.cpp \ - $(LIBBASE_DIR)/hex.cpp \ - $(LIBBASE_DIR)/stringprintf.cpp \ - $(LIBUTILS_DIR)/Errors.cpp \ - $(LIBUTILS_DIR)/misc.cpp \ - $(LIBUTILS_DIR)/RefBase.cpp \ - $(LIBUTILS_DIR)/StrongPointer.cpp \ - $(LIBUTILS_DIR)/Unicode.cpp \ - -# TODO: remove the following when libbinder supports std::string -# instead of String16 and String8 for Status and descriptors -MODULE_SRCS += \ - $(LIBUTILS_DIR)/SharedBuffer.cpp \ - $(LIBUTILS_DIR)/String16.cpp \ - $(LIBUTILS_DIR)/String8.cpp \ - -# TODO: disable dump() transactions to get rid of Vector -MODULE_SRCS += \ - $(LIBUTILS_DIR)/VectorImpl.cpp \ + $(LIBUTILS_BINDER_DIR)/Errors.cpp \ + $(LIBUTILS_BINDER_DIR)/RefBase.cpp \ + $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \ + $(LIBUTILS_BINDER_DIR)/String16.cpp \ + $(LIBUTILS_BINDER_DIR)/String8.cpp \ + $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \ + $(LIBUTILS_BINDER_DIR)/Unicode.cpp \ + $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \ MODULE_DEFINES += \ LK_DEBUGLEVEL_NO_ALIASES=1 \ @@ -63,17 +54,21 @@ MODULE_INCLUDES += \ GLOBAL_INCLUDES += \ $(LOCAL_DIR)/include \ $(LOCAL_DIR)/../include \ + $(LIBLOG_STUB_DIR)/include \ $(LIBBINDER_DIR)/include \ $(LIBBINDER_DIR)/ndk/include_cpp \ $(LIBBASE_DIR)/include \ - $(LIBCUTILS_DIR)/include \ - $(LIBUTILS_DIR)/include \ + $(LIBUTILS_BINDER_DIR)/include \ $(FMTLIB_DIR)/include \ GLOBAL_COMPILEFLAGS += \ -DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \ -DBINDER_NO_KERNEL_IPC \ -DBINDER_RPC_SINGLE_THREADED \ + -DBINDER_ENABLE_LIBLOG_ASSERT \ + -DBINDER_DISABLE_NATIVE_HANDLE \ + -DBINDER_DISABLE_BLOB \ + -DBINDER_NO_LIBBASE \ -D__ANDROID_VNDK__ \ MODULE_DEPS += \ diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp deleted file mode 100644 index b4243af891c1418aca5ceca41cac90309f04e9a4..0000000000000000000000000000000000000000 --- a/libs/binder/trusty/logging.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define TLOG_TAG "libbinder" - -#include "android-base/logging.h" - -#include -#include -#include - -#include -#include - -namespace android { -namespace base { - -static const char* GetFileBasename(const char* file) { - const char* last_slash = strrchr(file, '/'); - if (last_slash != nullptr) { - return last_slash + 1; - } - return file; -} - -// This splits the message up line by line, by calling log_function with a pointer to the start of -// each line and the size up to the newline character. It sends size = -1 for the final line. -template -static void SplitByLines(const char* msg, const F& log_function, Args&&... args) { - const char* newline; - while ((newline = strchr(msg, '\n')) != nullptr) { - log_function(msg, newline - msg, args...); - msg = newline + 1; - } - - log_function(msg, -1, args...); -} - -void DefaultAborter(const char* abort_message) { - TLOGC("aborting: %s\n", abort_message); - abort(); -} - -static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity, - const char* tag) { - switch (severity) { - case VERBOSE: - case DEBUG: - TLOGD("%s: %s\n", tag, msg); - break; - case INFO: - TLOGI("%s: %s\n", tag, msg); - break; - case WARNING: - TLOGW("%s: %s\n", tag, msg); - break; - case ERROR: - TLOGE("%s: %s\n", tag, msg); - break; - case FATAL_WITHOUT_ABORT: - case FATAL: - TLOGC("%s: %s\n", tag, msg); - break; - } -} - -void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, - const char*, unsigned int, const char* full_message) { - SplitByLines(full_message, TrustyLogLine, severity, tag); -} - -// This indirection greatly reduces the stack impact of having lots of -// checks/logging in a function. -class LogMessageData { -public: - LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag, - int error) - : file_(GetFileBasename(file)), - line_number_(line), - severity_(severity), - tag_(tag), - error_(error) {} - - const char* GetFile() const { return file_; } - - unsigned int GetLineNumber() const { return line_number_; } - - LogSeverity GetSeverity() const { return severity_; } - - const char* GetTag() const { return tag_; } - - int GetError() const { return error_; } - - std::ostream& GetBuffer() { return buffer_; } - - std::string ToString() const { return buffer_.str(); } - -private: - std::ostringstream buffer_; - const char* const file_; - const unsigned int line_number_; - const LogSeverity severity_; - const char* const tag_; - const int error_; - - DISALLOW_COPY_AND_ASSIGN(LogMessageData); -}; - -LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, - const char* tag, int error) - : LogMessage(file, line, severity, tag, error) {} - -LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, - int error) - : data_(new LogMessageData(file, line, severity, tag, error)) {} - -LogMessage::~LogMessage() { - // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM. - if (!WOULD_LOG(data_->GetSeverity())) { - return; - } - - // Finish constructing the message. - if (data_->GetError() != -1) { - data_->GetBuffer() << ": " << strerror(data_->GetError()); - } - std::string msg(data_->ToString()); - - LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), - msg.c_str()); - - // Abort if necessary. - if (data_->GetSeverity() == FATAL) { - DefaultAborter(msg.c_str()); - } -} - -std::ostream& LogMessage::stream() { - return data_->GetBuffer(); -} - -void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag, - const char* message) { - TrustyLogger(DEFAULT, severity, tag ?: "", file, line, message); -} - -bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) { - // This is controlled by Trusty's log level. - return true; -} - -} // namespace base -} // namespace android diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk index 42db29a2ef4d5be6b458928d2816dc274d863261..e0f821f2c947690679c856b6a90cd54b093dc48f 100644 --- a/libs/binder/trusty/rules.mk +++ b/libs/binder/trusty/rules.mk @@ -18,13 +18,13 @@ LOCAL_DIR := $(GET_LOCAL_DIR) MODULE := $(LOCAL_DIR) LIBBINDER_DIR := frameworks/native/libs/binder +# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release LIBBASE_DIR := system/libbase -LIBCUTILS_DIR := system/core/libcutils -LIBUTILS_DIR := system/core/libutils +LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub +LIBUTILS_BINDER_DIR := system/core/libutils/binder FMTLIB_DIR := external/fmtlib MODULE_SRCS := \ - $(LOCAL_DIR)/logging.cpp \ $(LOCAL_DIR)/OS.cpp \ $(LOCAL_DIR)/RpcServerTrusty.cpp \ $(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \ @@ -43,31 +43,22 @@ MODULE_SRCS := \ $(LIBBINDER_DIR)/Stability.cpp \ $(LIBBINDER_DIR)/Status.cpp \ $(LIBBINDER_DIR)/Utils.cpp \ - $(LIBBASE_DIR)/hex.cpp \ - $(LIBBASE_DIR)/stringprintf.cpp \ - $(LIBUTILS_DIR)/Errors.cpp \ - $(LIBUTILS_DIR)/misc.cpp \ - $(LIBUTILS_DIR)/RefBase.cpp \ - $(LIBUTILS_DIR)/StrongPointer.cpp \ - $(LIBUTILS_DIR)/Unicode.cpp \ - -# TODO: remove the following when libbinder supports std::string -# instead of String16 and String8 for Status and descriptors -MODULE_SRCS += \ - $(LIBUTILS_DIR)/SharedBuffer.cpp \ - $(LIBUTILS_DIR)/String16.cpp \ - $(LIBUTILS_DIR)/String8.cpp \ - -# TODO: disable dump() transactions to get rid of Vector -MODULE_SRCS += \ - $(LIBUTILS_DIR)/VectorImpl.cpp \ + $(LIBBINDER_DIR)/file.cpp \ + $(LIBUTILS_BINDER_DIR)/Errors.cpp \ + $(LIBUTILS_BINDER_DIR)/RefBase.cpp \ + $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \ + $(LIBUTILS_BINDER_DIR)/String16.cpp \ + $(LIBUTILS_BINDER_DIR)/String8.cpp \ + $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \ + $(LIBUTILS_BINDER_DIR)/Unicode.cpp \ + $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \ MODULE_EXPORT_INCLUDES += \ $(LOCAL_DIR)/include \ + $(LIBLOG_STUB_DIR)/include \ $(LIBBINDER_DIR)/include \ $(LIBBASE_DIR)/include \ - $(LIBCUTILS_DIR)/include \ - $(LIBUTILS_DIR)/include \ + $(LIBUTILS_BINDER_DIR)/include \ $(FMTLIB_DIR)/include \ # The android/binder_to_string.h header is shared between libbinder and @@ -77,6 +68,10 @@ MODULE_EXPORT_INCLUDES += \ MODULE_EXPORT_COMPILEFLAGS += \ -DBINDER_RPC_SINGLE_THREADED \ + -DBINDER_ENABLE_LIBLOG_ASSERT \ + -DBINDER_DISABLE_NATIVE_HANDLE \ + -DBINDER_DISABLE_BLOB \ + -DBINDER_NO_LIBBASE \ -D__ANDROID_VNDK__ \ # libbinder has some deprecated declarations that we want to produce warnings diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk new file mode 100644 index 0000000000000000000000000000000000000000..672d9b75ecf5af343e81ec200cd54808cb401c58 --- /dev/null +++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. +LIBBINDER_NDK_BINDGEN_FLAG_FILE := \ + $(LIBBINDER_DIR)/rust/libbinder_ndk_bindgen_flags.txt + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LIBBINDER_DIR)/rust/sys/lib.rs + +MODULE_CRATE_NAME := binder_ndk_sys + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + trusty/user/base/lib/trusty-sys \ + +MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp + +# Add the flags from the flag file +MODULE_BINDGEN_FLAGS += $(shell cat $(LIBBINDER_NDK_BINDGEN_FLAG_FILE)) +MODULE_SRCDEPS += $(LIBBINDER_NDK_BINDGEN_FLAG_FILE) + +include make/library.mk diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6f96566618ec9e729a046e84d07fbff739da3fb5 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2023 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 diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..c7036f45f78cb5eeabb2e8b42f2b711ebd52869e --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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. + */ + +//! Generated Rust bindings to binder_rpc_unstable + +#[allow(bad_style)] +mod sys { + include!(env!("BINDGEN_INC_FILE")); +} + +pub use sys::*; diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk new file mode 100644 index 0000000000000000000000000000000000000000..ef1b7c3cf8b6a33874a716a6e0dd87e332e1908a --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk @@ -0,0 +1,40 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LOCAL_DIR)/lib.rs + +MODULE_CRATE_NAME := binder_rpc_unstable_bindgen + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/binder_rpc_unstable \ + $(LIBBINDER_DIR)/trusty/ndk \ + $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/trusty-sys \ + +MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp + +MODULE_BINDGEN_FLAGS += \ + --blocklist-type="AIBinder" \ + --raw-line="use binder_ndk_sys::AIBinder;" \ + --rustified-enum="ARpcSession_FileDescriptorTransportMode" \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk new file mode 100644 index 0000000000000000000000000000000000000000..76f3b9401fc443a3a13424d8a67434f87358429d --- /dev/null +++ b/libs/binder/trusty/rust/rpcbinder/rules.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LIBBINDER_DIR)/rust/rpcbinder/src/lib.rs + +MODULE_CRATE_NAME := rpcbinder + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + $(LIBBINDER_DIR)/trusty/rust \ + $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ + external/rust/crates/foreign-types \ + trusty/user/base/lib/tipc/rust \ + trusty/user/base/lib/trusty-sys \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk new file mode 100644 index 0000000000000000000000000000000000000000..6de7eb5b3cf6c95e1988c22dffc012aaa1b80bcf --- /dev/null +++ b/libs/binder/trusty/rust/rules.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LIBBINDER_DIR)/rust/src/lib.rs + +MODULE_CRATE_NAME := binder + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ + external/rust/crates/downcast-rs \ + trusty/user/base/lib/trusty-sys \ + +# Trusty does not have `ProcessState`, so there are a few +# doc links in `IBinder` that are still broken. +MODULE_RUSTFLAGS += \ + --allow rustdoc::broken-intra-doc-links \ + +include make/library.mk diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index d4605ea13a32821ab4947af31278b9e282cb37f8..3fe71cefcee4b46108e16d79f6daf10b79701cd2 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -34,5 +34,7 @@ cc_library { "libbase", "liblog", ], + static_libs: ["libguiflags"], export_include_dirs: ["include"], + export_static_lib_headers: ["libguiflags"], } diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..365fc457d1baf506c15f3bf6e8691454d68a1348 --- /dev/null +++ b/libs/bufferstreams/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +aconfig_declarations { + name: "bufferstreams_flags", + package: "com.android.graphics.bufferstreams.flags", + srcs: [ + "aconfig/bufferstreams_flags.aconfig", + ], +} + +rust_aconfig_library { + name: "libbufferstreams_flags_rust", + crate_name: "bufferstreams_flags", + aconfig_declarations: "bufferstreams_flags", +} + +cc_aconfig_library { + name: "libbufferstreams_flags_cc", + aconfig_declarations: "bufferstreams_flags", +} diff --git a/libs/bufferstreams/OWNERS b/libs/bufferstreams/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..32b72b8592ddc5bbb996f4e0706801b9d3c560e3 --- /dev/null +++ b/libs/bufferstreams/OWNERS @@ -0,0 +1,7 @@ +carlosmr@google.com +hibrian@google.com +jreck@google.com +jshargo@google.com + +file:/services/surfaceflinger/OWNERS + diff --git a/libs/bufferstreams/README.md b/libs/bufferstreams/README.md new file mode 100644 index 0000000000000000000000000000000000000000..860adef281438dfd4303bc157bc05549c3d3d9ec --- /dev/null +++ b/libs/bufferstreams/README.md @@ -0,0 +1,13 @@ +# libbufferstreams: Reactive Streams for Graphics Buffers + +This library is currently **experimental** and **under active development**. +It is not production ready yet. + +For more information on reactive streams, please see + +## Contributing + +This library is natively written in Rust and exposes a C API. If you make changes to the Rust API, +you **must** update the C API in turn. To do so, with cbindgen installed, run: + +```$ ./update_include.sh``` diff --git a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig new file mode 100644 index 0000000000000000000000000000000000000000..e258725e3dec9327fe188491faf417f4a4da14d5 --- /dev/null +++ b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig @@ -0,0 +1,65 @@ +package: "com.android.graphics.bufferstreams.flags" + +flag { + name: "bufferstreams_steel_thread" + namespace: "core_graphics" + description: "Flag for bufferstreams steel thread milestone" + bug: "296101122" +} + +flag { + name: "bufferstreams_local" + namespace: "core_graphics" + description: "Flag for bufferstreams single-process functionality milestone" + bug: "296100790" +} + +flag { + name: "bufferstreams_pooling" + namespace: "core_graphics" + description: "Flag for bufferstreams buffer pooling milestone" + bug: "296101127" +} + +flag { + name: "bufferstreams_ipc" + namespace: "core_graphics" + description: "Flag for bufferstreams IPC milestone" + bug: "296099728" +} + +flag { + name: "bufferstreams_cpp" + namespace: "core_graphics" + description: "Flag for bufferstreams C/C++ milestone" + bug: "296100536" +} + +flag { + name: "bufferstreams_utils" + namespace: "core_graphics" + description: "Flag for bufferstreams extra utilities milestone" + bug: "285322189" +} + +flag { + name: "bufferstreams_demo" + namespace: "core_graphics" + description: "Flag for bufferstreams demo milestone" + bug: "297242965" +} + +flag { + name: "bufferstreams_perf" + namespace: "core_graphics" + description: "Flag for bufferstreams performance enhancement milestone" + bug: "297242843" +} + +flag { + name: "bufferstreams_tooling" + namespace: "core_graphics" + description: "Flag for bufferstreams tooling milestone" + bug: "297243180" +} + diff --git a/libs/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..bb573c596ce0ee9246ecacb7837abfb07e3bd4fc --- /dev/null +++ b/libs/bufferstreams/examples/app/Android.bp @@ -0,0 +1,49 @@ +// Copyright (C) 2023 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. + +android_app { + name: "BufferStreamsDemoApp", + srcs: ["java/**/*.kt"], + sdk_version: "current", + + jni_uses_platform_apis: true, + jni_libs: ["libbufferstreamdemoapp"], + use_embedded_native_libs: true, + kotlincflags: [ + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", + ], + optimize: { + proguard_flags_files: ["proguard-rules.pro"], + }, + + resource_dirs: ["res"], + + static_libs: [ + "androidx.activity_activity-compose", + "androidx.appcompat_appcompat", + "androidx.compose.foundation_foundation", + "androidx.compose.material3_material3", + "androidx.compose.runtime_runtime", + "androidx.compose.ui_ui", + "androidx.compose.ui_ui-graphics", + "androidx.compose.ui_ui-tooling-preview", + "androidx.core_core-ktx", + "androidx.lifecycle_lifecycle-runtime-ktx", + "androidx.navigation_navigation-common-ktx", + "androidx.navigation_navigation-compose", + "androidx.navigation_navigation-fragment-ktx", + "androidx.navigation_navigation-runtime-ktx", + "androidx.navigation_navigation-ui-ktx", + ], +} diff --git a/libs/bufferstreams/examples/app/AndroidManifest.xml b/libs/bufferstreams/examples/app/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5e2fa8a636f4e9d0ca76b67114cb2f6c7bf1e9e --- /dev/null +++ b/libs/bufferstreams/examples/app/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt new file mode 100644 index 0000000000000000000000000000000000000000..ff3ae5a090427e4640c08ca9624a3b3b2780a829 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt @@ -0,0 +1,40 @@ +package com.android.graphics.bufferstreamsdemoapp + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource + +@Composable +fun BufferDemosAppBar( + currentScreen: BufferDemoScreen, + canNavigateBack: Boolean, + navigateUp: () -> Unit, + modifier: Modifier = Modifier +) { + TopAppBar( + title = { Text(stringResource(currentScreen.title)) }, + colors = + TopAppBarDefaults.mediumTopAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer + ), + modifier = modifier, + navigationIcon = { + if (canNavigateBack) { + IconButton(onClick = navigateUp) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.back_button) + ) + } + } + } + ) +} \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt new file mode 100644 index 0000000000000000000000000000000000000000..ede77938de3dc6c26a4c6e33507fd1ca7fe5f593 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt @@ -0,0 +1,27 @@ +package com.android.graphics.bufferstreamsdemoapp + +class BufferStreamJNI { + // Used to load the 'bufferstreamsdemoapp' library on application startup. + init { + System.loadLibrary("bufferstreamdemoapp") + } + + /** + * A native method that is implemented by the 'bufferstreamsdemoapp' native library, which is + * packaged with this application. + */ + external fun stringFromJNI(): String; + external fun testBufferQueueCreation(); + + companion object { + fun companion_stringFromJNI(): String { + val instance = BufferStreamJNI() + return instance.stringFromJNI() + } + + fun companion_testBufferQueueCreation() { + val instance = BufferStreamJNI() + return instance.testBufferQueueCreation() + } + } +} \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt new file mode 100644 index 0000000000000000000000000000000000000000..95e415ecd5b8a8670b335764eb72855e65c718a1 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt @@ -0,0 +1,35 @@ +package com.android.graphics.bufferstreamsdemoapp + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun DemoScreen1(modifier: Modifier = Modifier) { + Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) { + LogOutput.getInstance().LogOutputComposable() + Row(modifier = Modifier.weight(1f, false).padding(16.dp)) { + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { BufferStreamJNI.companion_testBufferQueueCreation() }) { + Text("Run") + } + + OutlinedButton( + modifier = Modifier.fillMaxWidth(), + onClick = { LogOutput.getInstance().clearText() }) { + Text("Clear") + } + } + } + } +} diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt new file mode 100644 index 0000000000000000000000000000000000000000..5efee92f764fd0e9cfb9b59e46cee89ee02cbbfa --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt @@ -0,0 +1,7 @@ +package com.android.graphics.bufferstreamsdemoapp + +import androidx.compose.runtime.Composable + +@Composable +fun DemoScreen2() { +} \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt new file mode 100644 index 0000000000000000000000000000000000000000..8cba857737c3ceb53efabe05f26efa0eaed6b512 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt @@ -0,0 +1,7 @@ +package com.android.graphics.bufferstreamsdemoapp + +import androidx.compose.runtime.Composable + +@Composable +fun DemoScreen3() { +} \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt new file mode 100644 index 0000000000000000000000000000000000000000..3f0926f497fa74368910ac66af398e97047e9882 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt @@ -0,0 +1,65 @@ +package com.android.graphics.bufferstreamsdemoapp + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import java.util.Collections + +/* +LogOutput centralizes logging: storing, displaying, adding, and clearing log messages with +thread safety. It is a singleton that's also accessed from C++. The private constructor will +not allow this class to be initialized, limiting it to getInstance(). + */ +class LogOutput private constructor() { + val logs = Collections.synchronizedList(mutableStateListOf()) + + @Composable + fun LogOutputComposable() { + val rlogs = remember { logs } + + Card(modifier = Modifier.fillMaxWidth().padding(16.dp).height(400.dp)) { + Column( + modifier = + Modifier.padding(10.dp).size(380.dp).verticalScroll(rememberScrollState())) { + for (log in rlogs) { + Text(log, modifier = Modifier.padding(0.dp)) + } + } + } + } + + fun clearText() { + logs.clear() + } + + fun addLog(log: String) { + logs.add(log) + } + + companion object { + @Volatile private var instance: LogOutput? = null + + @JvmStatic + fun getInstance(): LogOutput { + if (instance == null) { + synchronized(this) { + if (instance == null) { + instance = LogOutput() + } + } + } + return instance!! + } + } +} diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..2ccd8d75efa83151fe2b23699695ca2fd45d7491 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt @@ -0,0 +1,132 @@ +package com.android.graphics.bufferstreamsdemoapp + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import com.android.graphics.bufferstreamsdemoapp.ui.theme.JetpackTheme +import java.util.* + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + JetpackTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background) { + BufferDemosApp() + } + } + } + } +} + +enum class BufferDemoScreen(val route: String, @StringRes val title: Int) { + Start(route = "start", title = R.string.start), + Demo1(route = "demo1", title = R.string.demo1), + Demo2(route = "demo2", title = R.string.demo2), + Demo3(route = "demo3", title = R.string.demo3); + + companion object { + fun findByRoute(route: String): BufferDemoScreen { + return values().find { it.route == route }!! + } + } +} + +@Composable +fun BufferDemosApp() { + var navController: NavHostController = rememberNavController() + // Get current back stack entry + val backStackEntry by navController.currentBackStackEntryAsState() + // Get the name of the current screen + val currentScreen = + BufferDemoScreen.findByRoute( + backStackEntry?.destination?.route ?: BufferDemoScreen.Start.route) + + Scaffold( + topBar = { + BufferDemosAppBar( + currentScreen = currentScreen, + canNavigateBack = navController.previousBackStackEntry != null, + navigateUp = { navController.navigateUp() }) + }) { + NavHost( + navController = navController, + startDestination = BufferDemoScreen.Start.route, + modifier = Modifier.padding(10.dp)) { + composable(route = BufferDemoScreen.Start.route) { + DemoList( + onButtonClicked = { navController.navigate(it) }, + ) + } + composable(route = BufferDemoScreen.Demo1.route) { + DemoScreen1(modifier = Modifier.fillMaxHeight().padding(top = 100.dp)) + } + composable(route = BufferDemoScreen.Demo2.route) { DemoScreen2() } + composable(route = BufferDemoScreen.Demo3.route) { DemoScreen3() } + } + } +} + +@Composable +fun DemoList(onButtonClicked: (String) -> Unit) { + var modifier = Modifier.fillMaxSize().padding(16.dp) + + Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp)) { + Spacer(modifier = Modifier.height(100.dp)) + Text(text = "Buffer Demos", style = MaterialTheme.typography.titleLarge) + Spacer(modifier = Modifier.height(8.dp)) + } + Row(modifier = Modifier.weight(2f, false)) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp)) { + for (item in BufferDemoScreen.values()) { + if (item.route != BufferDemoScreen.Start.route) + SelectDemoButton( + name = stringResource(item.title), + onClick = { onButtonClicked(item.route) }) + } + } + } + } +} + +@Composable +fun SelectDemoButton(name: String, onClick: () -> Unit, modifier: Modifier = Modifier) { + Button(onClick = onClick, modifier = modifier.widthIn(min = 250.dp)) { Text(name) } +} diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt new file mode 100644 index 0000000000000000000000000000000000000000..d85ea724dede12cde86c1102a6343f7bc1cbab3b --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt @@ -0,0 +1,11 @@ +package com.android.graphics.bufferstreamsdemoapp.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt new file mode 100644 index 0000000000000000000000000000000000000000..fccd93a10ba8683a9a5d5edffeb964672af253b6 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt @@ -0,0 +1,60 @@ +package com.android.graphics.bufferstreamsdemoapp.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 +) + +@Composable +fun JetpackTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt new file mode 100644 index 0000000000000000000000000000000000000000..06814ead8b247e10b03bc6446185fdd6eb856812 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt @@ -0,0 +1,18 @@ +package com.android.graphics.bufferstreamsdemoapp.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) +) \ No newline at end of file diff --git a/cmds/ip-up-vpn/Android.bp b/libs/bufferstreams/examples/app/jni/Android.bp similarity index 70% rename from cmds/ip-up-vpn/Android.bp rename to libs/bufferstreams/examples/app/jni/Android.bp index c746f7fde32f8f16422f8ce3ef03b1fd7c071e51..67910a1c4dab4386a105afb496e2e452949b6c97 100644 --- a/cmds/ip-up-vpn/Android.bp +++ b/libs/bufferstreams/examples/app/jni/Android.bp @@ -1,4 +1,4 @@ -// Copyright 2011 The Android Open Source Project +// Copyright (C) 2023 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. @@ -12,20 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -cc_binary { - name: "ip-up-vpn", - - srcs: ["ip-up-vpn.c"], +cc_library_shared { + name: "libbufferstreamdemoapp", cflags: [ - "-Wall", "-Werror", + "-Wno-error=unused-parameter", ], shared_libs: [ - "libcutils", - "liblog", + "libgui", + "libbase", + "libutils", ], + header_libs: ["jni_headers"], + srcs: ["*.cpp"], } diff --git a/libs/bufferstreams/examples/app/jni/main.cpp b/libs/bufferstreams/examples/app/jni/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..550ad22ae8fdcc9105299a140173f78926dbb0ee --- /dev/null +++ b/libs/bufferstreams/examples/app/jni/main.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2023 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 +#include + +#include + +void log(JNIEnv* env, std::string l) { + jclass clazz = env->FindClass("com/android/graphics/bufferstreamsdemoapp/LogOutput"); + jmethodID getInstance = env->GetStaticMethodID(clazz, "getInstance", + "()Lcom/android/graphics/bufferstreamsdemoapp/LogOutput;"); + jmethodID addLog = env->GetMethodID(clazz, "addLog", "(Ljava/lang/String;)V"); + jobject dmg = env->CallStaticObjectMethod(clazz, getInstance); + + jstring jlog = env->NewStringUTF(l.c_str()); + env->CallVoidMethod(dmg, addLog, jlog); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_stringFromJNI(JNIEnv* env, + jobject /* this */) { + const char* hello = "Hello from C++"; + return env->NewStringUTF(hello); +} + +JNIEXPORT void JNICALL +Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_testBufferQueueCreation( + JNIEnv* env, jobject /* thiz */) { + + log(env, "Calling testBufferQueueCreation."); + android::sp producer; + log(env, "Created producer."); + android::sp consumer; + log(env, "Created consumer."); + android::BufferQueue::createBufferQueue(&producer, &consumer); + log(env, "Created BufferQueue successfully."); + log(env, "Done!"); +} +} \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/proguard-rules.pro b/libs/bufferstreams/examples/app/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..7a987fc7c4b3efc1f979ffab6850c6d2c016715a --- /dev/null +++ b/libs/bufferstreams/examples/app/proguard-rules.pro @@ -0,0 +1,23 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-keep,allowoptimization,allowobfuscation class com.android.graphics.bufferstreamsdemoapp.** { *; } diff --git a/libs/bufferstreams/examples/app/res/drawable/ic_launcher_background.xml b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..07d5da9cbf141911847041df5d7b87f0dd5ef9d4 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/bufferstreams/examples/app/res/drawable/ic_launcher_foreground.xml b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000000000000000000000000000000000000..2b068d11462a4b96669193de13a711a3a36220a0 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher.xml b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000000000000000000000000000000000000..6f3b755bf50c6b03d8714a9c6184705e6a08389f --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher_round.xml b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000000000000000000000000000000000000..6f3b755bf50c6b03d8714a9c6184705e6a08389f --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..62b611da081676d42f6c3f78a2c91e7bcedddedb Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..28d4b77f9f036a47549d47db79c16788749dca10 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..9287f5083623b375139afb391af71cc533a7dd37 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..aa7d6427e6fa1074b79ccd52ef67ac15c5637e85 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..9126ae37cbc3587421d6889eadd1d91fbf1994d4 Binary files /dev/null and b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/libs/bufferstreams/examples/app/res/values/colors.xml b/libs/bufferstreams/examples/app/res/values/colors.xml new file mode 100644 index 0000000000000000000000000000000000000000..f8c6127d327620c93d2b2d00342a68e97b98a48d --- /dev/null +++ b/libs/bufferstreams/examples/app/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/values/strings.xml b/libs/bufferstreams/examples/app/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..75c8ab5e1c39b1111b99d3f3fae342f1af9abfcd --- /dev/null +++ b/libs/bufferstreams/examples/app/res/values/strings.xml @@ -0,0 +1,8 @@ + + Buffer Demos + Start + Demo 1 + Demo 2 + Demo 3 + Back + \ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/values/themes.xml b/libs/bufferstreams/examples/app/res/values/themes.xml new file mode 100644 index 0000000000000000000000000000000000000000..eeb308ae44318ef388436e7352a7a3dd5db3f0d2 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/values/themes.xml @@ -0,0 +1,4 @@ + + +