diff options
490 files changed, 17689 insertions, 19598 deletions
diff --git a/.clang-format b/.clang-format index 6725a1fde8..f63f670adc 100644 --- a/.clang-format +++ b/.clang-format @@ -12,3 +12,6 @@ IndentWidth: 4 PenaltyBreakBeforeFirstCallParameter: 100000 SpacesBeforeTrailingComments: 1 IncludeBlocks: Preserve + +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/Android.bp b/Android.bp index dec6716290..3992f82c3d 100644 --- a/Android.bp +++ b/Android.bp @@ -56,7 +56,8 @@ subdirs = [ cc_library_headers { name: "libandroid_sensor_headers", - vendor: true, + vendor_available: true, + host_supported: true, export_include_dirs: ["include_sensor"], } @@ -64,14 +65,20 @@ filegroup { name: "framework_native_aidl_binder", srcs: ["aidl/binder/**/*.aidl"], path: "aidl/binder", - visibility: ["//frameworks/native"], + visibility: [ + "//frameworks/native", + "//frameworks/native/libs/gui", + ], } filegroup { name: "framework_native_aidl_gui", srcs: ["aidl/gui/**/*.aidl"], path: "aidl/gui", - visibility: ["//frameworks/native"], + visibility: [ + "//frameworks/native", + "//frameworks/native/libs/gui", + ], } filegroup { @@ -82,7 +89,7 @@ filegroup { ], } -cc_library_headers{ +cc_library_headers { name: "libandroid_headers_private", export_include_dirs: ["include/private"], } diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 6d837c2cea..c71c4a0251 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,8 +1,10 @@ [Builtin Hooks] +rustfmt = true bpfmt = true clang_format = true [Builtin Hooks Options] +rustfmt = --config-path=rustfmt.toml # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ @@ -17,6 +19,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp libs/gui/ libs/input/ libs/nativedisplay/ + libs/nativewindow/ libs/renderengine/ libs/ui/ libs/vr/ diff --git a/TEST_MAPPING b/TEST_MAPPING index 260ee8dd4f..e66bca07ab 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -28,9 +28,6 @@ "include-filter": "*CropLatchingTest.*" }, { - "include-filter": "*ChildLayerTest.*" - }, - { "include-filter": "*ScreenCaptureTest.*" }, { diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl index 7e892205d5..bb3faaff79 100644 --- a/aidl/gui/android/view/Surface.aidl +++ b/aidl/gui/android/view/Surface.aidl @@ -17,4 +17,4 @@ package android.view; -parcelable Surface cpp_header "gui/view/Surface.h"; +@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h"; diff --git a/cmds/atrace/README.md b/cmds/atrace/README.md new file mode 100644 index 0000000000..faa43b222f --- /dev/null +++ b/cmds/atrace/README.md @@ -0,0 +1,48 @@ +# Atrace categories + +The atrace command (and the perfetto configuration) allow listing **categories** +to select subsets of events to be traced. + +Each category can include some userspace events and some ftrace events. + +## Vendor categories + +It's possible to extend exiting categories (or to define new categories) from +the /vendor partition in order to add hardware specific ftrace events. + +Since android 14, if the file `/vendor/etc/atrace/atrace_categories.txt`, atrace +and perfetto will consider the categories and ftrace events listed there. + +The file contains a list of categories, and for each category (on the following +lines, indented with one or more spaces of time), a list of ftrace events that +should be enabled when the category is enabled. + +Each ftrace event should be a subdirectory in `/sys/kernel/tracing/events/` and +should be of the form `group/event`. Listing a whole group is not supported, +each event needs to be listed explicitly. + +It is not an error if an ftrace event is listed in the file, but not present on +the tracing file system. + +Example: + +``` +gfx + mali/gpu_power_state + mali/mali_pm_status +thermal_tj + thermal_exynos/thermal_cpu_pressure + thermal_exynos/thermal_exynos_arm_update +``` + +The file lists two categories (`gfx` and `thermal_tj`). When the `gfx` category +is enabled, atrace (or perfetto) will enable +`/sys/kernel/tracing/events/mali/gpu_power_state` and +`/sys/kernel/tracing/events/mali/mali_pm_status`. When the `thermal_tj` category +is enabled, atrace (or perfetto) will enable +`/sys/kernel/tracing/events/thermal_exynos/thermal_cpu_pressure` and +`/sys/kernel/tracing/events/thermal_exynos/thermal_exynos_arm_update`. + +Since android 14, if the file `/vendor/etc/atrace/atrace_categories.txt` exists +on the file system, perfetto and atrace do not query the android.hardware.atrace +HAL (which is deprecated). diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 6fb9a4d112..81056262c3 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -49,6 +49,7 @@ #include <android-base/file.h> #include <android-base/macros.h> #include <android-base/properties.h> +#include <android-base/strings.h> #include <android-base/stringprintf.h> using namespace android; @@ -62,7 +63,7 @@ using hardware::atrace::V1_0::toString; using std::string; -#define MAX_SYS_FILES 12 +#define MAX_SYS_FILES 13 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags"; const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated"; @@ -73,7 +74,9 @@ const char* k_coreServiceCategory = "core_services"; const char* k_pdxServiceCategory = "pdx"; const char* k_coreServicesProp = "ro.atrace.core.services"; -typedef enum { OPT, REQ } requiredness ; +const char* kVendorCategoriesPath = "/vendor/etc/atrace/atrace_categories.txt"; + +typedef enum { OPT, REQ } requiredness; struct TracingCategory { // The name identifying the category. @@ -189,12 +192,15 @@ static const TracingCategory k_categories[] = { { OPT, "events/f2fs/f2fs_sync_file_exit/enable" }, { OPT, "events/f2fs/f2fs_write_begin/enable" }, { OPT, "events/f2fs/f2fs_write_end/enable" }, + { OPT, "events/f2fs/f2fs_iostat/enable" }, + { OPT, "events/f2fs/f2fs_iostat_latency/enable" }, { OPT, "events/ext4/ext4_da_write_begin/enable" }, { OPT, "events/ext4/ext4_da_write_end/enable" }, { OPT, "events/ext4/ext4_sync_file_enter/enable" }, { OPT, "events/ext4/ext4_sync_file_exit/enable" }, - { REQ, "events/block/block_rq_issue/enable" }, - { REQ, "events/block/block_rq_complete/enable" }, + { OPT, "events/block/block_bio_queue/enable" }, + { OPT, "events/block/block_bio_complete/enable" }, + { OPT, "events/ufs/ufshcd_command/enable" }, } }, { "mmc", "eMMC commands", 0, { { REQ, "events/mmc/enable" }, @@ -252,7 +258,20 @@ static const TracingCategory k_categories[] = { } }, }; -struct TracingVendorCategory { +// A category in the vendor categories file. +struct TracingVendorFileCategory { + // The name identifying the category. + std::string name; + + // If the category is enabled through command. + bool enabled = false; + + // Paths to the ftrace enable files (relative to g_traceFolder). + std::vector<std::string> ftrace_enable_paths; +}; + +// A category in the vendor HIDL HAL. +struct TracingVendorHalCategory { // The name identifying the category. std::string name; @@ -262,11 +281,8 @@ struct TracingVendorCategory { // If the category is enabled through command. bool enabled; - TracingVendorCategory(string &&name, string &&description, bool enabled) - : name(std::move(name)) - , description(std::move(description)) - , enabled(enabled) - {} + TracingVendorHalCategory(string&& name, string&& description, bool enabled) + : name(std::move(name)), description(std::move(description)), enabled(enabled) {} }; /* Command line options */ @@ -286,8 +302,9 @@ static bool g_tracePdx = false; static bool g_traceAborted = false; static bool g_categoryEnables[arraysize(k_categories)] = {}; static std::string g_traceFolder; +static std::vector<TracingVendorFileCategory> g_vendorFileCategories; static sp<IAtraceDevice> g_atraceHal; -static std::vector<TracingVendorCategory> g_vendorCategories; +static std::vector<TracingVendorHalCategory> g_vendorHalCategories; /* Sys file paths */ static const char* k_traceClockPath = @@ -644,6 +661,13 @@ static bool disableKernelTraceEvents() { } } } + for (const TracingVendorFileCategory& c : g_vendorFileCategories) { + for (const std::string& path : c.ftrace_enable_paths) { + if (fileIsWritable(path.c_str())) { + ok &= setKernelOptionEnable(path.c_str(), false); + } + } + } return ok; } @@ -723,7 +747,13 @@ static bool setKernelTraceFuncs(const char* funcs) static bool setCategoryEnable(const char* name) { bool vendor_found = false; - for (auto &c : g_vendorCategories) { + for (auto& c : g_vendorFileCategories) { + if (strcmp(name, c.name.c_str()) == 0) { + c.enabled = true; + vendor_found = true; + } + } + for (auto& c : g_vendorHalCategories) { if (strcmp(name, c.name.c_str()) == 0) { c.enabled = true; vendor_found = true; @@ -869,6 +899,16 @@ static bool setUpKernelTracing() } } + for (const TracingVendorFileCategory& c : g_vendorFileCategories) { + if (c.enabled) { + for (const std::string& path : c.ftrace_enable_paths) { + if (fileIsWritable(path.c_str())) { + ok &= setKernelOptionEnable(path.c_str(), true); + } + } + } + } + return ok; } @@ -1054,7 +1094,10 @@ static void listSupportedCategories() printf(" %10s - %s\n", c.name, c.longname); } } - for (const auto &c : g_vendorCategories) { + for (const auto& c : g_vendorFileCategories) { + printf(" %10s - (VENDOR)\n", c.name.c_str()); + } + for (const auto& c : g_vendorHalCategories) { printf(" %10s - %s (HAL)\n", c.name.c_str(), c.description.c_str()); } } @@ -1113,8 +1156,38 @@ bool findTraceFiles() return true; } -void initVendorCategories() -{ +void initVendorCategoriesFromFile() { + std::ifstream is(kVendorCategoriesPath); + for (std::string line; std::getline(is, line);) { + if (line.empty()) { + continue; + } + if (android::base::StartsWith(line, ' ') || android::base::StartsWith(line, '\t')) { + if (g_vendorFileCategories.empty()) { + fprintf(stderr, "Malformed vendor categories file\n"); + exit(1); + return; + } + std::string_view path = std::string_view(line).substr(1); + while (android::base::StartsWith(path, ' ') || android::base::StartsWith(path, '\t')) { + path.remove_prefix(1); + } + if (path.empty()) { + continue; + } + std::string enable_path = "events/"; + enable_path += path; + enable_path += "/enable"; + g_vendorFileCategories.back().ftrace_enable_paths.push_back(std::move(enable_path)); + } else { + TracingVendorFileCategory cat; + cat.name = line; + g_vendorFileCategories.push_back(std::move(cat)); + } + } +} + +void initVendorCategoriesFromHal() { g_atraceHal = IAtraceDevice::getService(); if (g_atraceHal == nullptr) { @@ -1122,27 +1195,34 @@ void initVendorCategories() return; } - Return<void> ret = g_atraceHal->listCategories( - [](const auto& list) { - g_vendorCategories.reserve(list.size()); - for (const auto& category : list) { - g_vendorCategories.emplace_back(category.name, category.description, false); - } - }); + Return<void> ret = g_atraceHal->listCategories([](const auto& list) { + g_vendorHalCategories.reserve(list.size()); + for (const auto& category : list) { + g_vendorHalCategories.emplace_back(category.name, category.description, false); + } + }); if (!ret.isOk()) { fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); } } -static bool setUpVendorTracing() -{ +void initVendorCategories() { + // If kVendorCategoriesPath exists on the filesystem, do not use the HAL. + if (access(kVendorCategoriesPath, F_OK) != -1) { + initVendorCategoriesFromFile(); + } else { + initVendorCategoriesFromHal(); + } +} + +static bool setUpVendorTracingWithHal() { if (g_atraceHal == nullptr) { // No atrace HAL return true; } std::vector<hidl_string> categories; - for (const auto &c : g_vendorCategories) { + for (const auto& c : g_vendorHalCategories) { if (c.enabled) { categories.emplace_back(c.name); } @@ -1163,15 +1243,14 @@ static bool setUpVendorTracing() return true; } -static bool cleanUpVendorTracing() -{ +static bool cleanUpVendorTracingWithHal() { if (g_atraceHal == nullptr) { // No atrace HAL return true; } - if (!g_vendorCategories.size()) { - // No vendor categories + if (!g_vendorHalCategories.size()) { + // No vendor HAL categories return true; } @@ -1325,7 +1404,7 @@ int main(int argc, char **argv) if (ok && traceStart && !onlyUserspace) { ok &= setUpKernelTracing(); - ok &= setUpVendorTracing(); + ok &= setUpVendorTracingWithHal(); ok &= startTrace(); } @@ -1396,7 +1475,7 @@ int main(int argc, char **argv) if (traceStop) { cleanUpUserspaceTracing(); if (!onlyUserspace) { - cleanUpVendorTracing(); + cleanUpVendorTracingWithHal(); cleanUpKernelTracing(); } } diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 5267b0294c..dff4c44671 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -181,6 +181,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/clk/clk_enable/enable chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_set_rate/enable chmod 0666 /sys/kernel/tracing/events/clk/clk_set_rate/enable + chmod 0666 /sys/kernel/debug/tracing/events/printk/console/enable + chmod 0666 /sys/kernel/tracing/events/printk/console/enable # disk chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable @@ -295,8 +297,18 @@ on late-init write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" # allow creating event triggers - chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger + + # allow enabling rss_stat_throttled + chmod 0666 /sys/kernel/tracing/events/synthetic/rss_stat_throttled/enable + chmod 0666 /sys/kernel/debug/tracing/events/synthetic/rss_stat_throttled/enable + +on late-init && property:ro.boot.fastboot.boottrace=enabled + setprop debug.atrace.tags.enableflags 802922 + setprop persist.traced.enable 0 + write /sys/kernel/debug/tracing/tracing_on 1 + write /sys/kernel/tracing/tracing_on 1 # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created @@ -386,6 +398,103 @@ on post-fs-data && property:persist.mm_events.enabled=true chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace +# Handle hyp tracing instance +on late-init && property:ro.boot.hypervisor.vm.supported=1 + +# Hypervisor tracing instance doesn't support changing trace_clock + chmod 0440 /sys/kernel/debug/tracing/hyp/trace_clock + chmod 0440 /sys/kernel/tracing/hyp/trace_clock + + chmod 0660 /sys/kernel/debug/tracing/hyp/buffer_size_kb + chmod 0660 /sys/kernel/tracing/hyp/buffer_size_kb + + chmod 0660 /sys/kernel/debug/tracing/hyp/tracing_on + chmod 0660 /sys/kernel/tracing/hyp/tracing_on + +# Tracing disabled by default + write /sys/kernel/debug/tracing/hyp/tracing_on 0 + write /sys/kernel/tracing/hyp/tracing_on 0 + +# Read and truncate the hyp trace. + chmod 0660 /sys/kernel/debug/tracing/hyp/trace + chmod 0660 /sys/kernel/tracing/hyp/trace + +# Read and truncate the per-CPU kernel trace. +# Cannot use wildcards in .rc files. Update this if there is a phone with +# TODO(b/249050813, ioffe): introduce per-cpu wildcard + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu0/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu0/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu1/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu1/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu2/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu2/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu3/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu3/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu4/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu4/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu5/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu5/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu6/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu6/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu7/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu7/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu8/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu8/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu9/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu9/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu10/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu10/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu11/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu11/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu12/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu12/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu13/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu13/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu14/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu14/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu15/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu15/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu16/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu16/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu17/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu17/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu18/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu18/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu19/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu19/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu20/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu20/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu21/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu21/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu22/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu22/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu23/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu23/trace + + chmod 0440 /sys/kernel/debug/tracing/hyp/events/header_page + chmod 0440 /sys/kernel/tracing/hyp/events/header_page + +# Hyp events start here + +# hyp_enter event + chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/enable + chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/enable +# TODO(b/249050813): should this be handled in kernel? + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/format + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/format + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/id + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/id + +# hyp_exit event + chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/enable + chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/enable +# TODO(b/249050813): should this be handled in kernel? + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/format + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/format + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/id + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/id + + on property:persist.debug.atrace.boottrace=1 start boottrace @@ -393,3 +502,10 @@ on property:persist.debug.atrace.boottrace=1 service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories 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/debug/tracing/tracing_on 0 + write /sys/kernel/tracing/tracing_on 0 + diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc index 9186514d0a..fa7be1816a 100644 --- a/cmds/atrace/atrace_userdebug.rc +++ b/cmds/atrace/atrace_userdebug.rc @@ -18,3 +18,9 @@ on post-fs chmod 0666 /sys/kernel/tracing/events/filemap/enable chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable + # Allow traced_probes to use the raw_syscall filters to trace only a subset + # of syscalls. + chmod 0666 /sys/kernel/tracing/events/raw_syscalls/sys_enter/filter + chmod 0666 /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/filter + chmod 0666 /sys/kernel/tracing/events/raw_syscalls/sys_exit/filter + chmod 0666 /sys/kernel/debug/tracing/events/raw_syscalls/sys_exit/filter diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md index eb0d898be1..3606827a4d 100644 --- a/cmds/bugreportz/readme.md +++ b/cmds/bugreportz/readme.md @@ -1,6 +1,6 @@ # bugreportz protocol -`bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using +`bugreportz` is used to generate a zipped bugreport whose path is passed back to `adb`, using the simple protocol defined below. # Version 1.1 diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index a2491e503f..a62bd01a5b 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -101,6 +101,7 @@ cc_defaults { "libhidlbase", "liblog", "libutils", + "libvintf", "libbinderdebug", "packagemanager_aidl-cpp", ], @@ -125,6 +126,7 @@ cc_binary { ], required: [ "atrace", + "bugreport_procdump", "dmabuf_dump", "ip", "iptables", diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING index 839a2c3023..649a13ee1e 100644 --- a/cmds/dumpstate/TEST_MAPPING +++ b/cmds/dumpstate/TEST_MAPPING @@ -9,15 +9,15 @@ ] }, { - "name": "dumpstate_smoke_test" - }, - { "name": "dumpstate_test" } ], "postsubmit": [ { "name": "BugreportManagerTestCases" + }, + { + "name": "dumpstate_smoke_test" } ], "imports": [ diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index ec1e89bce1..47a513be81 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "dumpstate" +#define ATRACE_TAG ATRACE_TAG_ALWAYS #include <dirent.h> #include <errno.h> @@ -76,6 +77,7 @@ #include <cutils/native_handle.h> #include <cutils/properties.h> #include <cutils/sockets.h> +#include <cutils/trace.h> #include <debuggerd/client.h> #include <dumpsys.h> #include <dumputils/dump_utils.h> @@ -88,6 +90,7 @@ #include <private/android_logger.h> #include <serviceutils/PriorityDumper.h> #include <utils/StrongPointer.h> +#include <vintf/VintfObject.h> #include "DumpstateInternal.h" #include "DumpstateService.h" #include "dumpstate.h" @@ -167,6 +170,7 @@ void add_mountinfo(); #define RECOVERY_DIR "/cache/recovery" #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" +#define UPDATE_ENGINE_PREF_DIR "/data/misc/update_engine/prefs" #define LOGPERSIST_DATA_DIR "/data/misc/logd" #define PREREBOOT_DATA_DIR "/data/misc/prereboot" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" @@ -180,6 +184,8 @@ void add_mountinfo(); #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list" #define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace" #define CGROUPFS_DIR "/sys/fs/cgroup" +#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk" +#define DROPBOX_DIR "/data/system/dropbox" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -189,6 +195,8 @@ static const std::string TOMBSTONE_DIR = "/data/tombstones/"; static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_"; static const std::string ANR_DIR = "/data/anr/"; static const std::string ANR_FILE_PREFIX = "anr_"; +static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/"; +static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-"; // TODO: temporary variables and functions used during C++ refactoring @@ -230,6 +238,7 @@ static const char* WAKE_LOCK_NAME = "dumpstate_wakelock"; // task and the log title of the duration report. static const std::string DUMP_TRACES_TASK = "DUMP TRACES"; static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT"; +static const std::string DUMP_NETSTATS_PROTO_TASK = "DUMP NETSTATS PROTO"; static const std::string DUMP_HALS_TASK = "DUMP HALS"; static const std::string DUMP_BOARD_TASK = "dumpstate_board()"; static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS"; @@ -516,6 +525,15 @@ static bool skip_not_stat(const char *path) { return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */ } +static bool skip_wtf_strictmode(const char *path) { + if (strstr(path, "_wtf")) { + return true; + } else if (strstr(path, "_strictmode")) { + return true; + } + return false; +} + static bool skip_none(const char* path __attribute__((unused))) { return false; } @@ -762,7 +780,7 @@ uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const { } void Dumpstate::PrintHeader() const { - std::string build, fingerprint, radio, bootloader, network; + std::string build, fingerprint, radio, bootloader, network, sdkversion; char date[80]; build = android::base::GetProperty("ro.build.display.id", "(unknown)"); @@ -770,6 +788,7 @@ void Dumpstate::PrintHeader() const { radio = android::base::GetProperty("gsm.version.baseband", "(unknown)"); bootloader = android::base::GetProperty("ro.bootloader", "(unknown)"); network = android::base::GetProperty("gsm.operator.alpha", "(unknown)"); + sdkversion = android::base::GetProperty("ro.build.version.sdk", "(unknown)"); strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_)); printf("========================================================\n"); @@ -787,9 +806,10 @@ void Dumpstate::PrintHeader() const { if (module_metadata_version != 0) { printf("Module Metadata version: %" PRId64 "\n", module_metadata_version); } - printf("SDK extension versions [r=%s s=%s]\n", - android::base::GetProperty("build.version.extensions.r", "-").c_str(), - android::base::GetProperty("build.version.extensions.s", "-").c_str()); + printf("Android SDK version: %s\n", sdkversion.c_str()); + printf("SDK extensions: "); + RunCommandToFd(STDOUT_FILENO, "", {SDK_EXT_INFO, "--header"}, + CommandOptions::WithTimeout(1).Always().DropRoot().Build()); printf("Kernel: "); DumpFileToFd(STDOUT_FILENO, "", "/proc/version"); @@ -829,7 +849,8 @@ status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, // Logging statement below is useful to time how long each entry takes, but it's too verbose. // MYLOGD("Adding zip entry %s\n", entry_name.c_str()); - int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress, + size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression; + int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), flags, get_mtime(fd, ds.now_)); if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(), @@ -921,7 +942,8 @@ void Dumpstate::AddDir(const std::string& dir, bool recursive) { bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) { MYLOGD("Adding zip text entry %s\n", entry_name.c_str()); - int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_); + size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression; + int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), flags, ds.now_); if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(), ZipWriter::ErrorCodeString(err)); @@ -1020,7 +1042,7 @@ static void DumpIncidentReport() { MYLOGE("Could not open %s to dump incident report.\n", path.c_str()); return; } - RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(120).Build()); + RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(20).Build()); bool empty = 0 == lseek(fd, 0, SEEK_END); if (!empty) { // Use a different name from "incident.proto" @@ -1033,6 +1055,26 @@ static void DumpIncidentReport() { } } +static void DumpNetstatsProto() { + const std::string path = ds.bugreport_internal_dir_ + "/tmp_netstats_proto"; + auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); + if (fd < 0) { + MYLOGE("Could not open %s to dump netstats proto.\n", path.c_str()); + return; + } + RunCommandToFd(fd, "", {"dumpsys", "netstats", "--proto"}, + CommandOptions::WithTimeout(120).Build()); + bool empty = 0 == lseek(fd, 0, SEEK_END); + if (!empty) { + ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "netstats" + kProtoExt, + path); + } else { + unlink(path.c_str()); + } +} + static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted // by the early call to MaybeSnapshotSystemTrace(), if any background @@ -1058,7 +1100,7 @@ static void DumpVisibleWindowViews() { return; } RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"}, - CommandOptions::WithTimeout(120).Build()); + CommandOptions::WithTimeout(10).Build()); bool empty = 0 == lseek(fd, 0, SEEK_END); if (!empty) { ds.AddZipEntry("visible_windows.zip", path); @@ -1079,6 +1121,16 @@ static void DumpIpTablesAsRoot() { RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"}); } +static void DumpShutdownCheckpoints() { + const bool shutdown_checkpoints_dumped = AddDumps( + ds.shutdown_checkpoints_.begin(), ds.shutdown_checkpoints_.end(), + "SHUTDOWN CHECKPOINTS", false /* add_to_zip */); + if (!shutdown_checkpoints_dumped) { + printf("*** NO SHUTDOWN CHECKPOINTS to dump in %s\n\n", + SHUTDOWN_CHECKPOINTS_DIR.c_str()); + } +} + static void DumpDynamicPartitionInfo() { if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) { return; @@ -1394,6 +1446,25 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { } } +// Dump all of the files that make up the vendor interface. +// See the files listed in dumpFileList() for the latest list of files. +static void DumpVintf() { + + const std::string sku = android::base::GetProperty("ro.boot.product.hardware.sku", ""); + const auto vintfFiles = android::vintf::details::dumpFileList(sku); + for (const auto vintfFile : vintfFiles) { + struct stat st; + if (stat(vintfFile.c_str(), &st) == 0) { + if (S_ISDIR(st.st_mode)) { + ds.AddDir(vintfFile, true /* recursive */); + } else { + ds.EnqueueAddZipEntryAndCleanupIfNeeded(ZIP_ROOT_DIR + vintfFile, + vintfFile); + } + } + } +} + static void DumpExternalFragmentationInfo() { struct stat st; if (stat("/proc/buddyinfo", &st) != 0) { @@ -1486,7 +1557,6 @@ static void DumpCheckins(int out_fd = STDOUT_FILENO) { dprintf(out_fd, "========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd); - RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd); RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd); @@ -1550,7 +1620,8 @@ static Dumpstate::RunStatus dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); // Enqueue slow functions into the thread pool, if the parallel run is enabled. - std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins; + std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins, + dump_netstats_report; if (ds.dump_pool_) { // Pool was shutdown in DumpstateDefaultAfterCritical method in order to // drop root user. Restarts it with two threads for the parallel run. @@ -1559,6 +1630,8 @@ static Dumpstate::RunStatus dumpstate() { dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1); dump_incident_report = ds.dump_pool_->enqueueTask( DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport); + dump_netstats_report = ds.dump_pool_->enqueueTask( + DUMP_NETSTATS_PROTO_TASK, &DumpNetstatsProto); dump_board = ds.dump_pool_->enqueueTaskWithFd( DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1); dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1); @@ -1574,7 +1647,8 @@ static Dumpstate::RunStatus dumpstate() { RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "BUGREPORT_PROCDUMP", {"bugreport_procdump"}, + CommandOptions::AS_ROOT); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpVisibleWindowViews); @@ -1591,9 +1665,6 @@ static Dumpstate::RunStatus dumpstate() { RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, - CommandOptions::AS_ROOT); - if (ds.dump_pool_) { WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_hals)); } else { @@ -1619,9 +1690,9 @@ static Dumpstate::RunStatus dumpstate() { do_dmesg(); } - RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); + DumpVintf(); - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); + RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); @@ -1652,6 +1723,8 @@ static Dumpstate::RunStatus dumpstate() { DoKmsg(); + DumpShutdownCheckpoints(); + DumpIpAddrAndRules(); dump_route_tables(); @@ -1755,6 +1828,13 @@ static Dumpstate::RunStatus dumpstate() { dump_frozen_cgroupfs(); if (ds.dump_pool_) { + WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_netstats_report)); + } else { + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_NETSTATS_PROTO_TASK, + DumpNetstatsProto); + } + + if (ds.dump_pool_) { WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_incident_report)); } else { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK, @@ -1799,11 +1879,14 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { if (!PropertiesHelper::IsDryRun()) { ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX); ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX); + ds.shutdown_checkpoints_ = GetDumpFds( + SHUTDOWN_CHECKPOINTS_DIR, SHUTDOWN_CHECKPOINTS_FILE_PREFIX); } ds.AddDir(RECOVERY_DIR, true); ds.AddDir(RECOVERY_DATA_DIR, true); ds.AddDir(UPDATE_ENGINE_LOG_DIR, true); + ds.AddDir(UPDATE_ENGINE_PREF_DIR, true); ds.AddDir(LOGPERSIST_DATA_DIR, false); if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(PROFILE_DATA_DIR_CUR, true); @@ -1815,6 +1898,11 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { DumpIpTablesAsRoot(); DumpDynamicPartitionInfo(); ds.AddDir(OTA_METADATA_DIR, true); + if (!PropertiesHelper::IsUserBuild()) { + // Include dropbox entry files inside ZIP, but exclude + // noisy WTF and StrictMode entries + dump_files("", DROPBOX_DIR, skip_wtf_strictmode, _add_file_from_fd); + } // Capture any IPSec policies in play. No keys are exposed here. RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build()); @@ -1836,6 +1924,9 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { DumpFile("PSI memory", "/proc/pressure/memory"); DumpFile("PSI io", "/proc/pressure/io"); + RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"}, + CommandOptions::WithTimeout(10).Always().DropRoot().Build()); + if (dump_pool_) { RETURN_IF_USER_DENIED_CONSENT(); WaitForTask(std::move(dump_traces)); @@ -2847,6 +2938,7 @@ void Dumpstate::Cancel() { } tombstone_data_.clear(); anr_data_.clear(); + shutdown_checkpoints_.clear(); // Instead of shutdown the pool, we delete temporary files directly since // shutdown blocking the call. @@ -3076,7 +3168,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); // Zip the (now complete) .tmp file within the internal directory. + ATRACE_BEGIN("FinalizeFile"); FinalizeFile(); + ATRACE_END(); // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; @@ -3128,6 +3222,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, tombstone_data_.clear(); anr_data_.clear(); + shutdown_checkpoints_.clear(); return (consent_callback_ != nullptr && consent_callback_->getResult() == UserConsentResult::UNAVAILABLE) @@ -3164,15 +3259,7 @@ void Dumpstate::MaybeSnapshotSystemTrace() { } void Dumpstate::MaybeSnapshotWinTrace() { - // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol. - for (const auto& service : {"window", "input_method"}) { - 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. - "", {"cmd", service, "tracing", "save-for-bugreport"}, - CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); - } - // Additionally, include the proto logging from WMShell. + // Include the proto logging from WMShell. RunCommand( // Empty name because it's not intended to be classified as a bugreport section. // Actual logging files can be found as "/data/misc/wmtrace/shell_log.winscope" @@ -3181,6 +3268,14 @@ void Dumpstate::MaybeSnapshotWinTrace() { "WMShell", "protolog", "save-for-bugreport"}, CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); + // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol. + for (const auto& service : {"window", "input_method"}) { + 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. + "", {"cmd", service, "tracing", "save-for-bugreport"}, + CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); + } } void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) { @@ -3396,6 +3491,9 @@ DurationReporter::DurationReporter(const std::string& title, bool logcat_only, b duration_fd_(duration_fd) { if (!title_.empty()) { started_ = Nanotime(); + if (title_.find("SHOW MAP") == std::string::npos) { + ATRACE_ASYNC_BEGIN(title_.c_str(), 0); + } } } @@ -3410,6 +3508,9 @@ DurationReporter::~DurationReporter() { dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } + if (title_.find("SHOW MAP") == std::string::npos) { + ATRACE_ASYNC_END(title_.c_str(), 0); + } } } @@ -3861,15 +3962,6 @@ void do_dmesg() { return; } -void do_showmap(int pid, const char *name) { - char title[255]; - char arg[255]; - - snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name); - snprintf(arg, sizeof(arg), "%d", pid); - RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT); -} - int Dumpstate::DumpFile(const std::string& title, const std::string& path) { DurationReporter duration_reporter(title); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index ee6b1aee18..7ffe80eba3 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -499,6 +499,9 @@ class Dumpstate { // List of open ANR dump files. std::vector<DumpData> anr_data_; + // List of open shutdown checkpoint files. + std::vector<DumpData> shutdown_checkpoints_; + // A thread pool to execute dump tasks simultaneously if the parallel run is enabled. std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_; @@ -619,9 +622,6 @@ void show_wchan(int pid, int tid, const char *name); /* Displays a processes times */ void show_showtime(int pid, const char *name); -/* Runs "showmap" for a process */ -void do_showmap(int pid, const char *name); - /* Gets the dmesg output for the kernel */ void do_dmesg(); diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 70b4e5c0d8..7234d419d6 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -1023,7 +1023,8 @@ class ZippedBugReportStreamTest : public DumpstateBaseTest { }; // Generate a quick LimitedOnly report redirected to a file, open it and verify entry exist. -TEST_F(ZippedBugReportStreamTest, StreamLimitedOnlyReport) { +// TODO: broken test tracked in b/249983726 +TEST_F(ZippedBugReportStreamTest, DISABLED_StreamLimitedOnlyReport) { std::string out_path = kTestDataPath + "StreamLimitedOnlyReportOut.zip"; android::base::unique_fd out_fd; CreateFd(out_path, &out_fd); diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index f0c19b93ec..b8e5ce1a63 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -60,6 +60,7 @@ class ServiceManagerMock : public IServiceManager { MOCK_METHOD1(isDeclared, bool(const String16&)); MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&)); MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&)); + MOCK_METHOD1(getUpdatableNames, Vector<String16>(const String16&)); MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&)); MOCK_METHOD2(registerForNotifications, status_t(const String16&, const sp<LocalRegistrationCallback>&)); diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index c9f680b266..ac101ecb29 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -25,6 +25,7 @@ cc_defaults { "CrateManager.cpp", "InstalldNativeService.cpp", "QuotaUtils.cpp", + "SysTrace.cpp", "dexopt.cpp", "execv_helper.cpp", "globals.cpp", @@ -72,8 +73,6 @@ cc_defaults { }, }, - clang: true, - tidy: true, tidy_checks: [ "-*", @@ -81,8 +80,9 @@ cc_defaults { "cert-*", "-cert-err58-cpp", ], - tidy_flags: [ - "-warnings-as-errors=clang-analyzer-security*,cert-*", + tidy_checks_as_errors: [ + "clang-analyzer-security*", + "cert-*", ], } @@ -127,7 +127,6 @@ cc_library_headers { cc_test_host { name: "run_dex2oat_test", test_suites: ["general-tests"], - clang: true, srcs: [ "run_dex2oat_test.cpp", "run_dex2oat.cpp", @@ -175,7 +174,7 @@ cc_binary { // Needs to be wherever installd is as it's execed by // installd. - required: ["migrate_legacy_obb_data.sh"], + required: ["migrate_legacy_obb_data"], } // OTA chroot tool @@ -187,7 +186,6 @@ cc_binary { "-Wall", "-Werror", ], - clang: true, srcs: [ "otapreopt_chroot.cpp", @@ -302,6 +300,6 @@ sh_binary { // Script to migrate legacy obb data. sh_binary { - name: "migrate_legacy_obb_data.sh", + name: "migrate_legacy_obb_data", src: "migrate_legacy_obb_data.sh", } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 6d3f7c3bab..bb6639e1a8 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -16,8 +16,6 @@ #include "InstalldNativeService.h" -#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER - #include <errno.h> #include <fts.h> #include <inttypes.h> @@ -75,6 +73,7 @@ #include "CrateManager.h" #include "MatchExtensionGen.h" #include "QuotaUtils.h" +#include "SysTrace.h" #ifndef LOG_TAG #define LOG_TAG "installd" @@ -128,8 +127,6 @@ static std::once_flag flag; namespace { -constexpr const char* kDump = "android.permission.DUMP"; - static binder::Status ok() { return binder::Status::ok(); } @@ -153,19 +150,6 @@ static binder::Status error(uint32_t code, const std::string& msg) { return binder::Status::fromServiceSpecificError(code, String8(msg.c_str())); } -binder::Status checkPermission(const char* permission) { - pid_t pid; - uid_t uid; - - if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid), - reinterpret_cast<int32_t*>(&uid))) { - return ok(); - } else { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission)); - } -} - binder::Status checkUid(uid_t expectedUid) { uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid == expectedUid || uid == AID_ROOT) { @@ -232,6 +216,19 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } } +binder::Status checkArgumentFileName(const std::string& path) { + if (path.empty()) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing name"); + } + for (const char& c : path) { + if (c == '\0' || c == '\n' || c == '/') { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Name %s is malformed", path.c_str())); + } + } + return ok(); +} + #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ @@ -268,6 +265,14 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } \ } +#define CHECK_ARGUMENT_FILE_NAME(path) \ + { \ + binder::Status status = checkArgumentFileName((path)); \ + if (!status.isOk()) { \ + return status; \ + } \ + } + #ifdef GRANULAR_LOCKS /** @@ -382,13 +387,7 @@ status_t InstalldNativeService::start() { return android::OK; } -status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) { - const binder::Status dump_permission = checkPermission(kDump); - if (!dump_permission.isOk()) { - dprintf(fd, "%s\n", dump_permission.toString8().c_str()); - return PERMISSION_DENIED; - } - +status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) { { std::lock_guard<std::recursive_mutex> lock(mMountsLock); dprintf(fd, "Storage mounts:\n"); @@ -502,10 +501,6 @@ static int prepare_app_cache_dir(const std::string& parent, const char* name, mo } static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { - if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { - return true; - } - int32_t uid = multiuser_get_uid(userId, appId); int shared_app_gid = multiuser_get_shared_gid(userId, appId); if (shared_app_gid == -1) { @@ -1008,6 +1003,12 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_FILE_NAME(profileName); + if (!base::EndsWith(profileName, ".prof")) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Profile name %s does not end with .prof", + profileName.c_str())); + } LOCK_PACKAGE(); binder::Status res = ok(); @@ -1302,7 +1303,7 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri const char* uuid_ = uuid ? uuid->c_str() : nullptr; for (auto userId : get_known_users(uuid_)) { LOCK_USER(); - ATRACE_BEGIN("fixup user"); + atrace_pm_begin("fixup user"); FTS* fts; FTSENT* p; auto ce_path = create_data_user_ce_path(uuid_, userId); @@ -1392,7 +1393,7 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri } } fts_close(fts); - ATRACE_END(); + atrace_pm_end(); } return ok(); } @@ -1871,8 +1872,9 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s binder::Status res = ok(); if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the user_de dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } auto sdk_sandbox_de_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId); @@ -1892,8 +1894,9 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the user_ce dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } auto sdk_sandbox_ce_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId); @@ -1901,8 +1904,9 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s res = error("Failed to delete " + sdk_sandbox_ce_path); } path = findDataMediaPath(uuid, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the media dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } } return res; @@ -1927,7 +1931,6 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> return error("Failed to determine free space for " + data_path); } - int64_t cleared = 0; int64_t needed = targetFreeBytes - free; if (!defy_target) { LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested " @@ -1943,7 +1946,7 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> // files from the UIDs which are most over their allocated quota // 1. Create trackers for every known UID - ATRACE_BEGIN("create"); + atrace_pm_begin("create"); const auto users = get_known_users(uuid_); #ifdef GRANULAR_LOCKS std::vector<UserLock> userLocks; @@ -2024,11 +2027,10 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> } fts_close(fts); } - ATRACE_END(); + atrace_pm_end(); // 2. Populate tracker stats and insert into priority queue - ATRACE_BEGIN("populate"); - int64_t cacheTotal = 0; + atrace_pm_begin("populate"); auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) { return (left->getCacheRatio() < right->getCacheRatio()); }; @@ -2037,13 +2039,12 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> for (const auto& it : trackers) { it.second->loadStats(); queue.push(it.second); - cacheTotal += it.second->cacheUsed; } - ATRACE_END(); + atrace_pm_end(); // 3. Bounce across the queue, freeing items from whichever tracker is // the most over their assigned quota - ATRACE_BEGIN("bounce"); + atrace_pm_begin("bounce"); std::shared_ptr<CacheTracker> active; while (active || !queue.empty()) { // Only look at apps under quota when explicitly requested @@ -2083,7 +2084,6 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> } active->cacheUsed -= item->size; needed -= item->size; - cleared += item->size; } if (!defy_target) { @@ -2100,7 +2100,7 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> } } } - ATRACE_END(); + atrace_pm_end(); } else { return error("Legacy cache logic no longer supported"); @@ -2445,84 +2445,84 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string flags &= ~FLAG_USE_QUOTA; } - ATRACE_BEGIN("obb"); + atrace_pm_begin("obb"); for (const auto& packageName : packageNames) { auto obbCodePath = create_data_media_package_path(uuid_, userId, "obb", packageName.c_str()); calculate_tree_size(obbCodePath, &extStats.codeSize); } - ATRACE_END(); + atrace_pm_end(); // Calculating the app size of the external storage owning app in a manual way, since // calculating it through quota apis also includes external media storage in the app storage // numbers if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) { - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, multiuser_get_shared_gid(0, appId)); } - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("quota"); + atrace_pm_begin("quota"); collectQuotaStats(uuidString, userId, appId, &stats, &extStats); - ATRACE_END(); + atrace_pm_end(); } else { - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize); } - ATRACE_END(); + atrace_pm_end(); for (size_t i = 0; i < packageNames.size(); i++) { const char* pkgname = packageNames[i].c_str(); - ATRACE_BEGIN("data"); + atrace_pm_begin("data"); auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]); collectManualStats(cePath, &stats); auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname); collectManualStats(dePath, &stats); - ATRACE_END(); + atrace_pm_end(); // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>), // collect individual stats of each subdirectory (shared, storage of each sdk etc.) if (appId >= AID_APP_START && appId <= AID_APP_END) { - ATRACE_BEGIN("sdksandbox"); + atrace_pm_begin("sdksandbox"); auto sdkSandboxCePath = create_data_misc_sdk_sandbox_package_path(uuid_, true, userId, pkgname); collectManualStatsForSubDirectories(sdkSandboxCePath, &stats); auto sdkSandboxDePath = create_data_misc_sdk_sandbox_package_path(uuid_, false, userId, pkgname); collectManualStatsForSubDirectories(sdkSandboxDePath, &stats); - ATRACE_END(); + atrace_pm_end(); } if (!uuid) { - ATRACE_BEGIN("profiles"); + atrace_pm_begin("profiles"); calculate_tree_size( create_primary_current_profile_package_dir_path(userId, pkgname), &stats.dataSize); calculate_tree_size( create_primary_reference_profile_package_dir_path(pkgname), &stats.codeSize); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("external"); + atrace_pm_begin("external"); auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname); collectManualStats(extPath, &extStats); auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname); calculate_tree_size(mediaPath, &extStats.dataSize); - ATRACE_END(); + atrace_pm_end(); } if (!uuid) { - ATRACE_BEGIN("dalvik"); + atrace_pm_begin("dalvik"); int32_t sharedGid = multiuser_get_shared_gid(0, appId); if (sharedGid != -1) { calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, sharedGid, -1); } - ATRACE_END(); + atrace_pm_end(); } } @@ -2668,41 +2668,41 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin } if (flags & FLAG_USE_QUOTA) { - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("data"); + atrace_pm_begin("data"); auto cePath = create_data_user_ce_path(uuid_, userId); collectManualStatsForUser(cePath, &stats, true); auto dePath = create_data_user_de_path(uuid_, userId); collectManualStatsForUser(dePath, &stats, true); - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("profile"); + atrace_pm_begin("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true); auto refProfilePath = create_primary_ref_profile_dir_path(); calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("external"); + atrace_pm_begin("external"); auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds); extStats.dataSize += sizes.totalSize; extStats.codeSize += sizes.obbSize; - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("dalvik"); + atrace_pm_begin("dalvik"); calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, -1, -1, true); calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize, -1, -1, true); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("quota"); + atrace_pm_begin("quota"); int64_t dataSize = extStats.dataSize; for (auto appId : appIds) { if (appId >= AID_APP_START) { @@ -2714,54 +2714,54 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin } } extStats.dataSize = dataSize; - ATRACE_END(); + atrace_pm_end(); } else { - ATRACE_BEGIN("obb"); + atrace_pm_begin("obb"); auto obbPath = create_data_path(uuid_) + "/media/obb"; calculate_tree_size(obbPath, &extStats.codeSize); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("data"); + atrace_pm_begin("data"); auto cePath = create_data_user_ce_path(uuid_, userId); collectManualStatsForUser(cePath, &stats); auto dePath = create_data_user_de_path(uuid_, userId); collectManualStatsForUser(dePath, &stats); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("sdksandbox"); + atrace_pm_begin("sdksandbox"); auto sdkSandboxCePath = create_data_misc_sdk_sandbox_path(uuid_, true, userId); collectManualStatsForUser(sdkSandboxCePath, &stats, false, true); auto sdkSandboxDePath = create_data_misc_sdk_sandbox_path(uuid_, false, userId); collectManualStatsForUser(sdkSandboxDePath, &stats, false, true); - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("profile"); + atrace_pm_begin("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); calculate_tree_size(userProfilePath, &stats.dataSize); auto refProfilePath = create_primary_ref_profile_dir_path(); calculate_tree_size(refProfilePath, &stats.codeSize); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("external"); + atrace_pm_begin("external"); auto dataMediaPath = create_data_media_path(uuid_, userId); collectManualExternalStatsForUser(dataMediaPath, &extStats); #if MEASURE_DEBUG LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache " << extStats.cacheSize; #endif - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("dalvik"); + atrace_pm_begin("dalvik"); calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize); calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize); - ATRACE_END(); + atrace_pm_end(); } } @@ -2809,16 +2809,16 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s } if (flags & FLAG_USE_QUOTA) { - ATRACE_BEGIN("quota"); + atrace_pm_begin("quota"); auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds); totalSize = sizes.totalSize; audioSize = sizes.audioSize; videoSize = sizes.videoSize; imageSize = sizes.imageSize; obbSize = sizes.obbSize; - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("apps"); + atrace_pm_begin("apps"); struct stats extStats; memset(&extStats, 0, sizeof(extStats)); for (auto appId : appIds) { @@ -2827,9 +2827,9 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s } } appSize = extStats.dataSize; - ATRACE_END(); + atrace_pm_end(); } else { - ATRACE_BEGIN("manual"); + atrace_pm_begin("manual"); FTS *fts; FTSENT *p; auto path = create_data_media_path(uuid_, userId); @@ -2872,13 +2872,16 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s } } fts_close(fts); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("obb"); + atrace_pm_begin("obb"); auto obbPath = StringPrintf("%s/Android/obb", create_data_media_path(uuid_, userId).c_str()); calculate_tree_size(obbPath, &obbSize); - ATRACE_END(); + if (!(flags & FLAG_USE_QUOTA)) { + totalSize -= obbSize; + } + atrace_pm_end(); } std::vector<int64_t> ret; @@ -3021,7 +3024,19 @@ binder::Status InstalldNativeService::copySystemProfile(const std::string& syste int32_t packageUid, const std::string& packageName, const std::string& profileName, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(systemProfile); + if (!base::EndsWith(systemProfile, ".prof")) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("System profile path %s does not end with .prof", + systemProfile.c_str())); + } CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_FILE_NAME(profileName); + if (!base::EndsWith(profileName, ".prof")) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Profile name %s does not end with .prof", + profileName.c_str())); + } LOCK_PACKAGE(); *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); @@ -3585,10 +3600,10 @@ binder::Status InstalldNativeService::tryMountDataMirror( return error("Failed to stat " + mirrorVolCePath); } - if (mirrorCeStat.st_ino == ceStat.st_ino) { + if (mirrorCeStat.st_ino == ceStat.st_ino && mirrorCeStat.st_dev == ceStat.st_dev) { // As it's being called by prepareUserStorage, it can be called multiple times. // Hence, we if we mount it already, we should skip it. - LOG(WARNING) << "CE dir is mounted already: " + cePath; + LOG(INFO) << "CE dir is mounted already: " + cePath; return ok(); } @@ -3728,7 +3743,7 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { ENFORCE_UID(AID_SYSTEM); // NOTE: The lint warning doesn't apply to the use of system(3) with // absolute parse and no command line arguments. - if (system("/system/bin/migrate_legacy_obb_data.sh") != 0) { // NOLINT(cert-env33-c) + if (system("/system/bin/migrate_legacy_obb_data") != 0) { // NOLINT(cert-env33-c) LOG(ERROR) << "Unable to migrate legacy obb data"; } diff --git a/cmds/installd/SysTrace.cpp b/cmds/installd/SysTrace.cpp new file mode 100644 index 0000000000..fa65c77a2b --- /dev/null +++ b/cmds/installd/SysTrace.cpp @@ -0,0 +1,30 @@ +/* + * 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 ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER + +#include "SysTrace.h" +#include <utils/Trace.h> + +namespace android::installd { +void atrace_pm_begin(const char* name) { + ATRACE_BEGIN(name); +} + +void atrace_pm_end() { + ATRACE_END(); +} +} /* namespace android::installd */ diff --git a/cmds/installd/SysTrace.h b/cmds/installd/SysTrace.h new file mode 100644 index 0000000000..18506a9258 --- /dev/null +++ b/cmds/installd/SysTrace.h @@ -0,0 +1,22 @@ +/* + * 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 + +namespace android::installd { +void atrace_pm_begin(const char*); +void atrace_pm_end(); +} /* namespace android::installd */ diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index ebb78913b1..34ea7597b4 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -1956,7 +1956,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter, debuggable, boot_complete, for_restore, target_sdk_version, enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image, - compilation_reason); + background_job_compile, compilation_reason); bool cancelled = false; pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc index 240aa495b7..5b08c776ac 100644 --- a/cmds/installd/installd.rc +++ b/cmds/installd/installd.rc @@ -1,6 +1,7 @@ service installd /system/bin/installd class main + capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL SETGID SETUID SYS_ADMIN on early-boot mkdir /config/sdcardfs/extensions/1055 diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index e978e79d86..6a3120c1b7 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -27,6 +27,7 @@ #include <sys/prctl.h> #include <sys/stat.h> #include <sys/mman.h> +#include <sys/wait.h> #include <android-base/logging.h> #include <android-base/macros.h> @@ -504,6 +505,11 @@ private: return 0; } + if (WIFSIGNALED(dexopt_result)) { + LOG(WARNING) << "Interrupted by signal " << WTERMSIG(dexopt_result) ; + return dexopt_result; + } + // If this was a profile-guided run, we may have profile version issues. Try to downgrade, // if possible. if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) { diff --git a/cmds/installd/otapreopt.rc b/cmds/installd/otapreopt.rc index 059ae752e7..0bad0c59ab 100644 --- a/cmds/installd/otapreopt.rc +++ b/cmds/installd/otapreopt.rc @@ -5,4 +5,4 @@ on post-fs-data # The dalvik-cache was not moved itself, so as to restrict the rights of otapreopt_slot. # But now the relabeling is annoying as there is no force option available here. So # explicitly list all the ISAs we know. - restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/mips /data/dalvik-cache/mips64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64 + restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/riscv64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64 diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index 51c4589440..4221a3a593 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -81,6 +81,7 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, bool enable_hidden_api_checks, bool generate_compact_dex, bool use_jitzygote, + bool background_job_compile, const char* compilation_reason) { PrepareBootImageFlags(use_jitzygote); @@ -92,7 +93,8 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, debuggable, target_sdk_version, enable_hidden_api_checks, generate_compact_dex, compilation_reason); - PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore); + PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore, + background_job_compile); const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); @@ -296,7 +298,8 @@ void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex, } void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, - bool for_restore) { + bool for_restore, + bool background_job_compile) { // CPU set { std::string cpu_set_format = "--cpu-set=%s"; @@ -306,7 +309,12 @@ void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete "dalvik.vm.restore-dex2oat-cpu-set", "dalvik.vm.dex2oat-cpu-set", cpu_set_format) - : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)) + : (background_job_compile + ? MapPropertyToArgWithBackup( + "dalvik.vm.background-dex2oat-cpu-set", + "dalvik.vm.dex2oat-cpu-set", + cpu_set_format) + : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))) : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format); AddArg(dex2oat_cpu_set_arg); } @@ -320,7 +328,12 @@ void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete "dalvik.vm.restore-dex2oat-threads", "dalvik.vm.dex2oat-threads", threads_format) - : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)) + : (background_job_compile + ? MapPropertyToArgWithBackup( + "dalvik.vm.background-dex2oat-threads", + "dalvik.vm.dex2oat-threads", + threads_format) + : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))) : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format); AddArg(dex2oat_threads_arg); } diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h index 559244f2b7..c13e1f1acb 100644 --- a/cmds/installd/run_dex2oat.h +++ b/cmds/installd/run_dex2oat.h @@ -51,6 +51,7 @@ class RunDex2Oat { bool enable_hidden_api_checks, bool generate_compact_dex, bool use_jitzygote, + bool background_job_compile, const char* compilation_reason); void Exec(int exit_code); @@ -76,7 +77,9 @@ class RunDex2Oat { bool enable_hidden_api_checks, bool generate_compact_dex, const char* compilation_reason); - void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore); + void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, + bool for_restore, + bool background_job_compile); virtual std::string GetProperty(const std::string& key, const std::string& default_value); virtual bool GetBoolProperty(const std::string& key, bool default_value); diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 2a8135a037..304ba7b04f 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -115,6 +115,7 @@ class RunDex2OatTest : public testing::Test { bool enable_hidden_api_checks = false; bool generate_compact_dex = true; bool use_jitzygote = false; + bool background_job_compile = false; const char* compilation_reason = nullptr; }; @@ -259,6 +260,7 @@ class RunDex2OatTest : public testing::Test { args->enable_hidden_api_checks, args->generate_compact_dex, args->use_jitzygote, + args->background_job_compile, args->compilation_reason); runner.Exec(/*exit_code=*/ 0); } @@ -375,6 +377,30 @@ TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) { VerifyExpectedFlags(); } +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground) { + setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "1,3"); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,3"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground_Backup) { + setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", ""); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) { setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2"); setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3"); @@ -481,6 +507,30 @@ TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) { VerifyExpectedFlags(); } +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground) { + setSystemProperty("dalvik.vm.background-dex2oat-threads", "2"); + setSystemProperty("dalvik.vm.dex2oat-threads", "3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground_Backup) { + setSystemProperty("dalvik.vm.background-dex2oat-threads", ""); + setSystemProperty("dalvik.vm.dex2oat-threads", "3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "3"); + VerifyExpectedFlags(); +} + TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) { setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4"); setSystemProperty("dalvik.vm.dex2oat-threads", "5"); diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index b3baca5c41..07f73b9029 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -11,7 +11,6 @@ package { cc_test { name: "installd_utils_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_utils_test.cpp"], cflags: [ "-Wall", @@ -36,7 +35,6 @@ cc_test { cc_test { name: "installd_cache_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_cache_test.cpp"], cflags: [ "-Wall", @@ -82,7 +80,6 @@ cc_test { cc_test { name: "installd_service_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_service_test.cpp"], cflags: [ "-Wall", @@ -130,7 +127,6 @@ cc_test { cc_test { name: "installd_dexopt_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_dexopt_test.cpp"], cflags: [ "-Wall", @@ -177,7 +173,6 @@ cc_test { cc_test { name: "installd_otapreopt_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_otapreopt_test.cpp"], cflags: [ "-Wall", @@ -198,7 +193,6 @@ cc_test { cc_test { name: "installd_file_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_file_test.cpp"], cflags: [ "-Wall", diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 4eb30e2542..3b589dc581 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -23,6 +23,7 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/macros.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> @@ -52,22 +53,7 @@ namespace installd { constexpr int kTimeoutMs = 60000; -// TODO(calin): try to dedup this code. -#if defined(__arm__) -static const std::string kRuntimeIsa = "arm"; -#elif defined(__aarch64__) -static const std::string kRuntimeIsa = "arm64"; -#elif defined(__mips__) && !defined(__LP64__) -static const std::string kRuntimeIsa = "mips"; -#elif defined(__mips__) && defined(__LP64__) -static const std::string kRuntimeIsa = "mips64"; -#elif defined(__i386__) -static const std::string kRuntimeIsa = "x86"; -#elif defined(__x86_64__) -static const std::string kRuntimeIsa = "x86_64"; -#else -static const std::string kRuntimeIsa = "none"; -#endif +static const std::string kRuntimeIsa = ABI_STRING; int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); @@ -920,6 +906,11 @@ TEST_F(DexoptTest, ResolveStartupConstStrings) { TEST_F(DexoptTest, DexoptDex2oat64Enabled) { LOG(INFO) << "DexoptDex2oat64Enabled"; + std::string zygote_prop = android::base::GetProperty("ro.zygote", ""); + ASSERT_GT(zygote_prop.size(), 0); + if (zygote_prop != "zygote32_64" && zygote_prop != "zygote64_32") { + GTEST_SKIP() << "DexoptDex2oat64Enabled skipped for single-bitness Zygote."; + } const std::string property = "dalvik.vm.dex2oat64.enabled"; const std::string previous_value = android::base::GetProperty(property, ""); auto restore_property = android::base::make_scope_guard([=]() { @@ -1366,6 +1357,58 @@ TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { /*has_user_id*/ true, /*expected_result*/ false); } +TEST_F(ProfileTest, ClearAppProfilesOk) { + LOG(INFO) << "ClearAppProfilesOk"; + + ASSERT_BINDER_SUCCESS(service_->clearAppProfiles(package_name_, "primary.prof")); + ASSERT_BINDER_SUCCESS(service_->clearAppProfiles(package_name_, "image_editor.split.prof")); +} + +TEST_F(ProfileTest, ClearAppProfilesFailWrongProfileName) { + LOG(INFO) << "ClearAppProfilesFailWrongProfileName"; + + ASSERT_BINDER_FAIL( + service_->clearAppProfiles(package_name_, + "../../../../dalvik-cache/arm64/" + "system@app@SecureElement@SecureElement.apk@classes.vdex")); + ASSERT_BINDER_FAIL(service_->clearAppProfiles(package_name_, "image_editor.split.apk")); +} + +TEST_F(ProfileTest, CopySystemProfileOk) { + LOG(INFO) << "CopySystemProfileOk"; + + bool result; + ASSERT_BINDER_SUCCESS( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, "primary.prof", &result)); +} + +TEST_F(ProfileTest, CopySystemProfileFailWrongSystemProfilePath) { + LOG(INFO) << "CopySystemProfileFailWrongSystemProfilePath"; + + bool result; + ASSERT_BINDER_FAIL(service_->copySystemProfile("../../secret.dat", kTestAppUid, package_name_, + "primary.prof", &result)); + ASSERT_BINDER_FAIL(service_->copySystemProfile("/data/user/package.name/secret.data", + kTestAppUid, package_name_, "primary.prof", + &result)); +} + +TEST_F(ProfileTest, CopySystemProfileFailWrongProfileName) { + LOG(INFO) << "CopySystemProfileFailWrongProfileName"; + + bool result; + ASSERT_BINDER_FAIL( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, + "../../../../dalvik-cache/arm64/test.vdex", &result)); + ASSERT_BINDER_FAIL( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, "/test.prof", &result)); + ASSERT_BINDER_FAIL( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, "base.apk", &result)); +} class BootProfileTest : public ProfileTest { public: diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 4d9b71016e..ffc082d5b2 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -523,7 +523,6 @@ int calculate_tree_size(const std::string& path, int64_t* size, */ bool is_valid_package_name(const std::string& packageName) { // This logic is borrowed from PackageParser.java - bool hasSep = false; bool front = true; auto it = packageName.begin(); @@ -539,7 +538,6 @@ bool is_valid_package_name(const std::string& packageName) { } } if (c == '.') { - hasSep = true; front = true; continue; } diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index ff73c9499f..e54f9d3df7 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -353,8 +353,16 @@ bool ListCommand::addEntryWithInstance(const TableEntry& entry, return false; } + auto vintfFqInstance = vintf::FqInstance::from(fqInstance.string()); + if (!vintfFqInstance.has_value()) { + err() << "Unable to convert " << fqInstance.string() << " to vintf::FqInstance" + << std::endl; + return false; + } + std::string e; - if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) { + if (!manifest->insertInstance(*vintfFqInstance, entry.transport, arch, vintf::HalFormat::HIDL, + &e)) { err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl; return false; } diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp index cbfbdc9223..af85666276 100644 --- a/cmds/lshal/libprocpartition/Android.bp +++ b/cmds/lshal/libprocpartition/Android.bp @@ -35,5 +35,6 @@ cc_library_static { ], export_include_dirs: [ "include", - ] + ], + min_sdk_version: "30", } diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp index b7e520f2f1..711038ce63 100644 --- a/cmds/servicemanager/Access.cpp +++ b/cmds/servicemanager/Access.cpp @@ -30,6 +30,7 @@ constexpr bool kIsVendor = true; constexpr bool kIsVendor = false; #endif +#ifdef __ANDROID__ static std::string getPidcon(pid_t pid) { android_errorWriteLog(0x534e4554, "121035042"); @@ -45,7 +46,6 @@ static std::string getPidcon(pid_t pid) { static struct selabel_handle* getSehandle() { static struct selabel_handle* gSehandle = nullptr; - if (gSehandle != nullptr && selinux_status_updated()) { selabel_close(gSehandle); gSehandle = nullptr; @@ -78,8 +78,10 @@ static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t ad->tname->c_str()); return 0; } +#endif Access::Access() { +#ifdef __ANDROID__ union selinux_callback cb; cb.func_audit = auditCallback; @@ -91,6 +93,7 @@ Access::Access() { CHECK(selinux_status_open(true /*fallback*/) >= 0); CHECK(getcon(&mThisProcessContext) == 0); +#endif } Access::~Access() { @@ -98,6 +101,7 @@ Access::~Access() { } Access::CallingContext Access::getCallingContext() { +#ifdef __ANDROID__ IPCThreadState* ipc = IPCThreadState::self(); const char* callingSid = ipc->getCallingSid(); @@ -108,6 +112,9 @@ Access::CallingContext Access::getCallingContext() { .uid = ipc->getCallingUid(), .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid), }; +#else + return CallingContext(); +#endif } bool Access::canFind(const CallingContext& ctx,const std::string& name) { @@ -124,6 +131,7 @@ bool Access::canList(const CallingContext& ctx) { bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm, const std::string& tname) { +#ifdef __ANDROID__ const char* tclass = "service_manager"; AuditCallbackData data = { @@ -133,9 +141,18 @@ bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const c return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm, reinterpret_cast<void*>(&data)); +#else + (void)sctx; + (void)tctx; + (void)perm; + (void)tname; + + return true; +#endif } bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) { +#ifdef __ANDROID__ char *tctx = nullptr; if (selabel_lookup(getSehandle(), &tctx, name.c_str(), SELABEL_CTX_ANDROID_SERVICE) != 0) { LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n"; @@ -145,6 +162,14 @@ bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::stri bool allowed = actionAllowed(sctx, tctx, perm, name); freecon(tctx); return allowed; +#else + (void)sctx; + (void)name; + (void)perm; + (void)kIsVendor; + + return true; +#endif } } // android diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 32922ca24c..1386660eb6 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -44,13 +44,6 @@ cc_binary { defaults: ["servicemanager_defaults"], init_rc: ["servicemanager.rc"], srcs: ["main.cpp"], -} - -cc_binary { - name: "servicemanager.microdroid", - defaults: ["servicemanager_defaults"], - init_rc: ["servicemanager.microdroid.rc"], - srcs: ["main.cpp"], bootstrap: true, } @@ -86,3 +79,22 @@ cc_test { ], static_libs: ["libgmock"], } + +cc_fuzz { + name: "servicemanager_fuzzer", + defaults: [ + "servicemanager_defaults", + "service_fuzzer_defaults", + ], + host_supported: true, + srcs: ["ServiceManagerFuzzer.cpp"], + fuzz_config: { + libfuzzer_options: [ + "max_len=50000", + ], + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + }, +} diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 3cfe5297ca..eadf67821f 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -49,14 +49,14 @@ static std::vector<ManifestWithDescription> GetManifestsWithDescription() { #ifdef __ANDROID_RECOVERY__ auto vintfObject = vintf::VintfObjectRecovery::GetInstance(); if (vintfObject == nullptr) { - LOG(ERROR) << "NULL VintfObjectRecovery!"; + ALOGE("NULL VintfObjectRecovery!"); return {}; } return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}}; #else auto vintfObject = vintf::VintfObject::GetInstance(); if (vintfObject == nullptr) { - LOG(ERROR) << "NULL VintfObject!"; + ALOGE("NULL VintfObject!"); return {}; } return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"}, @@ -68,10 +68,10 @@ static std::vector<ManifestWithDescription> GetManifestsWithDescription() { static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) { for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) { if (mwd.manifest == nullptr) { - LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description; - // note, we explicitly do not retry here, so that we can detect VINTF - // or other bugs (b/151696835) - continue; + ALOGE("NULL VINTF MANIFEST!: %s", mwd.description); + // note, we explicitly do not retry here, so that we can detect VINTF + // or other bugs (b/151696835) + continue; } if (func(mwd)) return true; } @@ -87,8 +87,9 @@ struct AidlName { size_t firstSlash = name.find('/'); size_t lastDot = name.rfind('.', firstSlash); if (firstSlash == std::string::npos || lastDot == std::string::npos) { - LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. " - << "some.package.foo.IFoo/default) but got: " << name; + ALOGE("VINTF HALs require names in the format type/instance (e.g. " + "some.package.foo.IFoo/default) but got: %s", + name.c_str()); return false; } aname->package = name.substr(0, lastDot); @@ -104,7 +105,7 @@ static bool isVintfDeclared(const std::string& name) { bool found = forEachManifest([&](const ManifestWithDescription& mwd) { if (mwd.manifest->hasAidlInstance(aname.package, aname.iface, aname.instance)) { - LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest."; + ALOGI("Found %s in %s VINTF manifest.", name.c_str(), mwd.description); return true; // break } return false; // continue @@ -113,8 +114,8 @@ static bool isVintfDeclared(const std::string& name) { if (!found) { // Although it is tested, explicitly rebuilding qualified name, in case it // becomes something unexpected. - LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/" - << aname.instance << " in the VINTF manifest."; + ALOGI("Could not find %s.%s/%s in the VINTF manifest.", aname.package.c_str(), + aname.iface.c_str(), aname.instance.c_str()); } return found; @@ -135,12 +136,33 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name) updatableViaApex = manifestInstance.updatableViaApex(); return false; // break (libvintf uses opposite convention) }); + if (updatableViaApex.has_value()) return true; // break (found match) return false; // continue }); return updatableViaApex; } +static std::vector<std::string> getVintfUpdatableInstances(const std::string& apexName) { + std::vector<std::string> instances; + + forEachManifest([&](const ManifestWithDescription& mwd) { + mwd.manifest->forEachInstance([&](const auto& manifestInstance) { + if (manifestInstance.format() == vintf::HalFormat::AIDL && + manifestInstance.updatableViaApex().has_value() && + manifestInstance.updatableViaApex().value() == apexName) { + std::string aname = manifestInstance.package() + "." + + manifestInstance.interface() + "/" + manifestInstance.instance(); + instances.push_back(aname); + } + return true; // continue (libvintf uses opposite convention) + }); + return false; // continue + }); + + return instances; +} + static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) { AidlName aname; if (!AidlName::fill(name, &aname)) return std::nullopt; @@ -173,7 +195,9 @@ static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& n static std::vector<std::string> getVintfInstances(const std::string& interface) { size_t lastDot = interface.rfind('.'); if (lastDot == std::string::npos) { - LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface; + ALOGE("VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) " + "but got: %s", + interface.c_str()); return {}; } const std::string package = interface.substr(0, lastDot); @@ -198,6 +222,13 @@ static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::s } #endif // !VENDORSERVICEMANAGER +ServiceManager::Service::~Service() { + if (!hasClients) { + // only expected to happen on process death + LOG(WARNING) << "a service was removed when there are clients"; + } +} + ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) { // TODO(b/151696835): reenable performance hack when we solve bug, since with // this hack and other fixes, it is unlikely we will see even an ephemeral @@ -269,8 +300,13 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN } if (out) { - // Setting this guarantee each time we hand out a binder ensures that the client-checking - // loop knows about the event even if the client immediately drops the service + // Force onClients to get sent, and then make sure the timerfd won't clear it + // by setting guaranteeClient again. This logic could be simplified by using + // a time-based guarantee. However, forcing onClients(true) to get sent + // right here is always going to be important for processes serving multiple + // lazy interfaces. + service->guaranteeClient = true; + CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); service->guaranteeClient = true; } @@ -308,7 +344,7 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi } if (!isValidServiceName(name)) { - LOG(ERROR) << "Invalid service name: " << name; + ALOGE("Invalid service name: %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name"); } @@ -322,22 +358,51 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi // implicitly unlinked when the binder is removed if (binder->remoteBinder() != nullptr && binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) { - LOG(ERROR) << "Could not linkToDeath when adding " << name; + ALOGE("Could not linkToDeath when adding %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure"); } + auto it = mNameToService.find(name); + if (it != mNameToService.end()) { + const Service& existing = it->second; + + // 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 + // only trying to detect when two different services are accidentally installed. + + if (existing.ctx.uid != ctx.uid) { + ALOGW("Service '%s' originally registered from UID %u but it is now being registered " + "from UID %u. Multiple instances installed?", + name.c_str(), existing.ctx.uid, ctx.uid); + } + + if (existing.ctx.sid != ctx.sid) { + ALOGW("Service '%s' originally registered from SID %s but it is now being registered " + "from SID %s. Multiple instances installed?", + name.c_str(), existing.ctx.sid.c_str(), ctx.sid.c_str()); + } + + ALOGI("Service '%s' originally registered from PID %d but it is being registered again " + "from PID %d. Bad state? Late death notification? Multiple instances installed?", + name.c_str(), existing.ctx.debugPid, ctx.debugPid); + } + // Overwrite the old service if it exists - mNameToService[name] = Service { - .binder = binder, - .allowIsolated = allowIsolated, - .dumpPriority = dumpPriority, - .debugPid = ctx.debugPid, + mNameToService[name] = Service{ + .binder = binder, + .allowIsolated = allowIsolated, + .dumpPriority = dumpPriority, + .ctx = ctx, }; - auto it = mNameToRegistrationCallback.find(name); - if (it != mNameToRegistrationCallback.end()) { + 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. + mNameToService[name].guaranteeClient = true; + CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); + mNameToService[name].guaranteeClient = true; + for (const sp<IServiceCallback>& cb : it->second) { - mNameToService[name].guaranteeClient = true; // permission checked in registerForNotifications cb->onRegistration(name, binder); } @@ -381,7 +446,7 @@ Status ServiceManager::registerForNotifications( } if (!isValidServiceName(name)) { - LOG(ERROR) << "Invalid service name: " << name; + ALOGE("Invalid service name: %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); } @@ -392,7 +457,7 @@ Status ServiceManager::registerForNotifications( if (OK != IInterface::asBinder(callback)->linkToDeath( sp<ServiceManager>::fromExisting(this))) { - LOG(ERROR) << "Could not linkToDeath when adding " << name; + ALOGE("Could not linkToDeath when adding %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } @@ -424,7 +489,7 @@ Status ServiceManager::unregisterForNotifications( } if (!found) { - LOG(ERROR) << "Trying to unregister callback, but none exists " << name; + ALOGE("Trying to unregister callback, but none exists %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } @@ -485,6 +550,30 @@ Status ServiceManager::updatableViaApex(const std::string& name, return Status::ok(); } +Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& apexName, + std::vector<std::string>* outReturn) { + auto ctx = mAccess->getCallingContext(); + + std::vector<std::string> apexUpdatableInstances; +#ifndef VENDORSERVICEMANAGER + apexUpdatableInstances = getVintfUpdatableInstances(apexName); +#endif + + outReturn->clear(); + + for (const std::string& instance : apexUpdatableInstances) { + if (mAccess->canFind(ctx, instance)) { + outReturn->push_back(instance); + } + } + + if (outReturn->size() == 0 && apexUpdatableInstances.size() != 0) { + return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial"); + } + + return Status::ok(); +} + Status ServiceManager::getConnectionInfo(const std::string& name, std::optional<ConnectionInfo>* outReturn) { auto ctx = mAccess->getCallingContext(); @@ -541,15 +630,17 @@ void ServiceManager::binderDied(const wp<IBinder>& who) { } void ServiceManager::tryStartService(const std::string& name) { - ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service", + 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()); std::thread([=] { if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) { - LOG(INFO) << "Tried to start aidl service " << name - << " as a lazy service, but was unable to. Usually this happens when a " - "service is not installed, but if the service is intended to be used as a " - "lazy service, then it may be configured incorrectly."; + ALOGI("Tried to start aidl service %s as a lazy service, but was unable to. Usually " + "this happens when a " + "service is not installed, but if the service is intended to be used as a " + "lazy service, then it may be configured incorrectly.", + name.c_str()); } }).detach(); } @@ -567,24 +658,25 @@ Status ServiceManager::registerClientCallback(const std::string& name, const sp< auto serviceIt = mNameToService.find(name); if (serviceIt == mNameToService.end()) { - LOG(ERROR) << "Could not add callback for nonexistent service: " << name; + ALOGE("Could not add callback for nonexistent service: %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); } - if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) { - LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")"; + if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) { + ALOGW("Only a server can register for client callbacks (for %s)", name.c_str()); return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION); } if (serviceIt->second.binder != service) { - LOG(WARNING) << "Tried to register client callback for " << name - << " but a different service is registered under this name."; + ALOGW("Tried to register client callback for %s but a different service is registered " + "under this name.", + name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); } if (OK != IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) { - LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name; + ALOGE("Could not linkToDeath when adding client callback for %s", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } @@ -621,67 +713,74 @@ ssize_t ServiceManager::Service::getNodeStrongRefCount() { void ServiceManager::handleClientCallbacks() { for (const auto& [name, service] : mNameToService) { - handleServiceClientCallback(name, true); + handleServiceClientCallback(1 /* sm has one refcount */, name, true); } } -ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName, - bool isCalledOnInterval) { +bool ServiceManager::handleServiceClientCallback(size_t knownClients, + const std::string& serviceName, + bool isCalledOnInterval) { auto serviceIt = mNameToService.find(serviceName); if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) { - return -1; + return true; // return we do have clients a.k.a. DON'T DO ANYTHING } Service& service = serviceIt->second; ssize_t count = service.getNodeStrongRefCount(); - // binder driver doesn't support this feature - if (count == -1) return count; + // binder driver doesn't support this feature, consider we have clients + if (count == -1) return true; - bool hasClients = count > 1; // this process holds a strong count + bool hasKernelReportedClients = static_cast<size_t>(count) > knownClients; if (service.guaranteeClient) { - // we have no record of this client - if (!service.hasClients && !hasClients) { - sendClientCallbackNotifications(serviceName, true); + if (!service.hasClients && !hasKernelReportedClients) { + sendClientCallbackNotifications(serviceName, true, + "service is guaranteed to be in use"); } // guarantee is temporary service.guaranteeClient = false; } - // only send notifications if this was called via the interval checking workflow - if (isCalledOnInterval) { - if (hasClients && !service.hasClients) { - // client was retrieved in some other way - sendClientCallbackNotifications(serviceName, true); - } + // Regardless of this situation, we want to give this notification as soon as possible. + // This way, we have a chance of preventing further thrashing. + if (hasKernelReportedClients && !service.hasClients) { + sendClientCallbackNotifications(serviceName, true, "we now have a record of a client"); + } - // there are no more clients, but the callback has not been called yet - if (!hasClients && service.hasClients) { - sendClientCallbackNotifications(serviceName, false); + // But limit rate of shutting down service. + if (isCalledOnInterval) { + if (!hasKernelReportedClients && service.hasClients) { + sendClientCallbackNotifications(serviceName, false, + "we now have no record of a client"); } } - return count; + // May be different than 'hasKernelReportedClients'. We intentionally delay + // information about clients going away to reduce thrashing. + return service.hasClients; } -void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) { +void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, + bool hasClients, const char* context) { auto serviceIt = mNameToService.find(serviceName); if (serviceIt == mNameToService.end()) { - LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName; + ALOGW("sendClientCallbackNotifications could not find service %s when %s", + serviceName.c_str(), context); return; } Service& service = serviceIt->second; - CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients - << " so we can't tell clients again that we have client: " << hasClients; + CHECK_NE(hasClients, service.hasClients) << context; - LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients; + ALOGI("Notifying %s they %s (previously: %s) have clients when %s", serviceName.c_str(), + hasClients ? "do" : "don't", service.hasClients ? "do" : "don't", context); auto ccIt = mNameToClientCallback.find(serviceName); CHECK(ccIt != mNameToClientCallback.end()) - << "sendClientCallbackNotifications could not find callbacks for service "; + << "sendClientCallbackNotifications could not find callbacks for service when " + << context; for (const auto& callback : ccIt->second) { callback->onClients(service.binder, hasClients); @@ -702,44 +801,47 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB auto serviceIt = mNameToService.find(name); if (serviceIt == mNameToService.end()) { - LOG(WARNING) << "Tried to unregister " << name - << ", but that service wasn't registered to begin with."; + ALOGW("Tried to unregister %s, but that service wasn't registered to begin with.", + name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } - if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) { - LOG(WARNING) << "Only a server can unregister itself (for " << name << ")"; + if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) { + ALOGW("Only a server can unregister itself (for %s)", name.c_str()); return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION); } sp<IBinder> storedBinder = serviceIt->second.binder; if (binder != storedBinder) { - LOG(WARNING) << "Tried to unregister " << name - << ", but a different service is registered under this name."; + ALOGW("Tried to unregister %s, but a different service is registered under this name.", + name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } + // important because we don't have timer-based guarantees, we don't want to clear + // this if (serviceIt->second.guaranteeClient) { - LOG(INFO) << "Tried to unregister " << name << ", but there is about to be a client."; + ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } - int clients = handleServiceClientCallback(name, false); - - // clients < 0: feature not implemented or other error. Assume clients. - // Otherwise: // - kernel driver will hold onto one refcount (during this transaction) // - servicemanager has a refcount (guaranteed by this transaction) - // So, if clients > 2, then at least one other service on the system must hold a refcount. - if (clients < 0 || clients > 2) { - // client callbacks are either disabled or there are other clients - LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients; - // Set this flag to ensure the clients are acknowledged in the next callback + constexpr size_t kKnownClients = 2; + + if (handleServiceClientCallback(kKnownClients, name, false)) { + ALOGI("Tried to unregister %s, but there are clients.", name.c_str()); + + // Since we had a failed registration attempt, and the HIDL implementation of + // delaying service shutdown for multiple periods wasn't ported here... this may + // help reduce thrashing, but we should be able to remove it. serviceIt->second.guaranteeClient = true; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } + ALOGI("Unregistering %s", name.c_str()); mNameToService.erase(name); return Status::ok(); @@ -754,7 +856,7 @@ Status ServiceManager::getServiceDebugInfo(std::vector<ServiceDebugInfo>* outRet for (auto const& [name, service] : mNameToService) { ServiceDebugInfo info; info.name = name; - info.debugPid = service.debugPid; + info.debugPid = service.ctx.debugPid; outReturn->push_back(std::move(info)); } @@ -762,4 +864,10 @@ Status ServiceManager::getServiceDebugInfo(std::vector<ServiceDebugInfo>* outRet return Status::ok(); } +void ServiceManager::clear() { + mNameToService.clear(); + mNameToRegistrationCallback.clear(); + mNameToClientCallback.clear(); +} + } // namespace android diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 5e403194d7..3aa6731eb3 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -49,6 +49,8 @@ public: binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override; binder::Status updatableViaApex(const std::string& name, std::optional<std::string>* outReturn) override; + binder::Status getUpdatableNames(const std::string& apexName, + std::vector<std::string>* outReturn) override; binder::Status getConnectionInfo(const std::string& name, std::optional<ConnectionInfo>* outReturn) override; binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service, @@ -58,6 +60,12 @@ public: void binderDied(const wp<IBinder>& who) override; void handleClientCallbacks(); + /** + * This API is added for debug purposes. It clears members which hold service and callback + * information. + */ + void clear(); + protected: virtual void tryStartService(const std::string& name); @@ -68,10 +76,12 @@ private: int32_t dumpPriority; bool hasClients = false; // notifications sent on true -> false. bool guaranteeClient = false; // forces the client check to true - pid_t debugPid = 0; // the process in which this service runs + Access::CallingContext ctx; // process that originally registers this // the number of clients of the service, including servicemanager itself ssize_t getNodeStrongRefCount(); + + ~Service(); }; using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>; @@ -83,9 +93,12 @@ private: void removeRegistrationCallback(const wp<IBinder>& who, ServiceCallbackMap::iterator* it, bool* found); - ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval); - // Also updates mHasClients (of what the last callback was) - void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients); + // returns whether there are known clients in addition to the count provided + bool handleServiceClientCallback(size_t knownClients, const std::string& serviceName, + bool isCalledOnInterval); + // Also updates mHasClients (of what the last callback was) + void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients, + const char* context); // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty // this updates the iterator to the next location void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it); diff --git a/cmds/servicemanager/ServiceManagerFuzzer.cpp b/cmds/servicemanager/ServiceManagerFuzzer.cpp new file mode 100644 index 0000000000..b76a6bd3cd --- /dev/null +++ b/cmds/servicemanager/ServiceManagerFuzzer.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzbinder/libbinder_driver.h> +#include <utils/StrongPointer.h> + +#include "Access.h" +#include "ServiceManager.h" + +using ::android::Access; +using ::android::fuzzService; +using ::android::ServiceManager; +using ::android::sp; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + auto accessPtr = std::make_unique<Access>(); + auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr)); + fuzzService(serviceManager, FuzzedDataProvider(data, size)); + serviceManager->clear(); + + return 0; +} diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 2fb9c2bc9a..c1a04dde89 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -15,6 +15,7 @@ */ #include <android-base/logging.h> +#include <android-base/properties.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/Status.h> @@ -26,15 +27,14 @@ #include "ServiceManager.h" using ::android::Access; -using ::android::sp; +using ::android::IPCThreadState; using ::android::Looper; using ::android::LooperCallback; using ::android::ProcessState; -using ::android::IPCThreadState; -using ::android::ProcessState; using ::android::ServiceManager; -using ::android::os::IServiceManager; using ::android::sp; +using ::android::base::SetProperty; +using ::android::os::IServiceManager; class BinderCallback : public LooperCallback { public: @@ -111,9 +111,7 @@ private: }; int main(int argc, char** argv) { -#ifdef __ANDROID_RECOVERY__ android::base::InitLogging(argv, android::base::KernelLogger); -#endif if (argc > 2) { LOG(FATAL) << "usage: " << argv[0] << " [binder driver]"; @@ -121,6 +119,8 @@ int main(int argc, char** argv) { const char* driver = argc == 2 ? argv[1] : "/dev/binder"; + LOG(INFO) << "Starting sm instance on " << driver; + sp<ProcessState> ps = ProcessState::initWithDriver(driver); ps->setThreadPoolMaxThreadCount(0); ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY); @@ -138,6 +138,12 @@ int main(int argc, char** argv) { BinderCallback::setupTo(looper); ClientCallbackCallback::setupTo(looper, manager); +#ifndef VENDORSERVICEMANAGER + if (!SetProperty("servicemanager.ready", "true")) { + LOG(ERROR) << "Failed to set servicemanager ready property"; + } +#endif + while(true) { looper->pollAll(-1); } diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc deleted file mode 100644 index e01f132c64..0000000000 --- a/cmds/servicemanager/servicemanager.microdroid.rc +++ /dev/null @@ -1,8 +0,0 @@ -service servicemanager /system/bin/servicemanager.microdroid - class core - user system - group system readproc - critical - onrestart restart apexd - task_profiles ServiceCapacityLow - shutdown critical diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index e5d689ff91..4f92b3a374 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -3,7 +3,9 @@ service servicemanager /system/bin/servicemanager user system group system readproc critical - onrestart restart apexd + file /dev/kmsg w + onrestart setprop servicemanager.ready false + onrestart restart --only-if-running apexd onrestart restart audioserver onrestart restart gatekeeperd onrestart class_restart --only-enabled main diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc index 067faf9c8f..b927c018df 100644 --- a/cmds/servicemanager/servicemanager.recovery.rc +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -1,4 +1,5 @@ service servicemanager /system/bin/servicemanager disabled group system readproc + 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 5d5a75e174..0fd8d8ee2a 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -14,13 +14,15 @@ * limitations under the License. */ +#include <android-base/properties.h> +#include <android-base/strings.h> #include <android/os/BnServiceCallback.h> #include <binder/Binder.h> -#include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <cutils/android_filesystem_config.h> -#include <gtest/gtest.h> #include <gmock/gmock.h> +#include <gtest/gtest.h> #include "Access.h" #include "ServiceManager.h" @@ -75,6 +77,11 @@ static sp<ServiceManager> getPermissiveServiceManager() { return sm; } +static bool isCuttlefish() { + return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""), + "vsoc_"); +} + TEST(AddService, HappyHappy) { auto sm = getPermissiveServiceManager(); EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, @@ -306,6 +313,49 @@ TEST(ListServices, CriticalServices) { EXPECT_THAT(out, ElementsAre("sa")); } +TEST(Vintf, UpdatableViaApex) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::optional<std::string> updatableViaApex; + EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider/internal/0", + &updatableViaApex) + .isOk()); + EXPECT_EQ(std::make_optional<std::string>("com.google.emulated.camera.provider.hal"), + updatableViaApex); +} + +TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::optional<std::string> updatableViaApex; + EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider", + &updatableViaApex) + .isOk()); // missing instance name + EXPECT_EQ(std::nullopt, updatableViaApex); +} + +TEST(Vintf, GetUpdatableNames) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::vector<std::string> names; + EXPECT_TRUE(sm->getUpdatableNames("com.google.emulated.camera.provider.hal", &names).isOk()); + EXPECT_EQ(std::vector< + std::string>{"android.hardware.camera.provider.ICameraProvider/internal/0"}, + names); +} + +TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::vector<std::string> names; + EXPECT_TRUE(sm->getUpdatableNames("non.existing.apex.name", &names).isOk()); + EXPECT_EQ(std::vector<std::string>{}, names); +} + class CallbackHistorian : public BnServiceCallback { Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { registrations.push_back(name); diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc index c9305a1c60..80af1d1377 100644 --- a/cmds/servicemanager/vndservicemanager.rc +++ b/cmds/servicemanager/vndservicemanager.rc @@ -2,6 +2,7 @@ service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder class core user system group system readproc + file /dev/kmsg w task_profiles ServiceCapacityLow onrestart class_restart main onrestart class_restart hal diff --git a/data/etc/android.software.ipsec_tunnel_migration.xml b/data/etc/android.software.ipsec_tunnel_migration.xml new file mode 100644 index 0000000000..c405e1efd6 --- /dev/null +++ b/data/etc/android.software.ipsec_tunnel_migration.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- + This is the feature indicating that the device has support for updating + source and destination addresses of IPsec tunnels +--> + +<permissions> + <feature name="android.software.ipsec_tunnel_migration" /> +</permissions> diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index e65b224a20..4746bc3c9d 100644 --- a/headers/media_plugin/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h @@ -318,6 +318,9 @@ typedef enum OMX_VIDEO_DOLBYVISIONLEVELTYPE { OMX_VIDEO_DolbyVisionLevelUhd30 = 0x40, OMX_VIDEO_DolbyVisionLevelUhd48 = 0x80, OMX_VIDEO_DolbyVisionLevelUhd60 = 0x100, + OMX_VIDEO_DolbyVisionLevelUhd120 = 0x200, + OMX_VIDEO_DolbyVisionLevel8k30 = 0x400, + OMX_VIDEO_DolbyVisionLevel8k60 = 0x800, OMX_VIDEO_DolbyVisionLevelmax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONLEVELTYPE; diff --git a/include/android/sensor.h b/include/android/sensor.h index eef69f4b32..ba81bc8d8e 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -596,12 +596,15 @@ typedef struct AHeadingEvent { float accuracy; } AHeadingEvent; +// LINT.IfChange /** * Information that describes a sensor event, refer to * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional * documentation. + * + * NOTE: changes to this struct has to be backward compatible and reflected in + * sensors_event_t */ -/* NOTE: changes to this struct has to be backward compatible */ typedef struct ASensorEvent { int32_t version; /* sizeof(struct ASensorEvent) */ int32_t sensor; /** The sensor that generates this event */ @@ -646,6 +649,7 @@ typedef struct ASensorEvent { uint32_t flags; int32_t reserved1[3]; } ASensorEvent; +// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h) struct ASensorManager; /** diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h index 571a361889..d725be1abe 100644 --- a/include/attestation/HmacKeyManager.h +++ b/include/attestation/HmacKeyManager.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef ATTESTATION_HMACKEYMANAGER_H +#define ATTESTATION_HMACKEYMANAGER_H + #include <array> namespace android { @@ -29,4 +32,6 @@ public: private: const std::array<uint8_t, 128> mHmacKey; }; -} // namespace android
\ No newline at end of file +} // namespace android + +#endif // ATTESTATION_HMACKEYMANAGER_H
\ No newline at end of file diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 50849506a4..1da78aa0c1 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -20,7 +20,6 @@ #include <android-base/result.h> #include <stdint.h> #include <utils/Errors.h> -#include <utils/KeyedVector.h> #include <utils/Tokenizer.h> #include <set> @@ -72,11 +71,11 @@ public: status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; - status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const; - status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; - status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; + std::vector<int32_t> findScanCodesForKey(int32_t keyCode) const; + std::optional<int32_t> findScanCodeForLed(int32_t ledCode) const; + std::optional<int32_t> findUsageCodeForLed(int32_t ledCode) const; - status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + std::optional<AxisInfo> mapAxis(int32_t scanCode) const; const std::string getLoadFileName() const; // Return pair of sensor type and sensor data index, for the input device abs code base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode); @@ -100,11 +99,11 @@ private: int32_t sensorDataIndex; }; - KeyedVector<int32_t, Key> mKeysByScanCode; - KeyedVector<int32_t, Key> mKeysByUsageCode; - KeyedVector<int32_t, AxisInfo> mAxes; - KeyedVector<int32_t, Led> mLedsByScanCode; - KeyedVector<int32_t, Led> mLedsByUsageCode; + std::unordered_map<int32_t, Key> mKeysByScanCode; + std::unordered_map<int32_t, Key> mKeysByUsageCode; + std::unordered_map<int32_t, AxisInfo> mAxes; + std::unordered_map<int32_t, Led> mLedsByScanCode; + std::unordered_map<int32_t, Led> mLedsByUsageCode; std::unordered_map<int32_t, Sensor> mSensorsByAbsCode; std::set<std::string> mRequiredKernelConfigs; std::string mLoadFileName; diff --git a/include/input/OWNERS b/include/input/OWNERS new file mode 100644 index 0000000000..c88bfe97ca --- /dev/null +++ b/include/input/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/INPUT_OWNERS diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index 15bd5c3913..ebc74fb3d8 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -23,8 +23,10 @@ #include <sys/eventfd.h> #include <sys/uio.h> +#include <atomic> #include <chrono> #include <deque> +#include <optional> #include <string> #include <string_view> #include <tuple> diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt index 7584ca3f53..f9f042ef5d 100644 --- a/libs/adbd_auth/libadbd_auth.map.txt +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -1,17 +1,17 @@ LIBADBD_AUTH { global: - adbd_auth_new; # apex introduced=30 - adbd_auth_delete; # apex introduced=30 - adbd_auth_run; # apex introduced=30 - adbd_auth_get_public_keys; #apex introduced=30 - adbd_auth_notify_auth; # apex introduced=30 - adbd_auth_notify_disconnect; # apex introduced=30 - adbd_auth_prompt_user; # apex introduced=30 - adbd_auth_prompt_user_with_id; # apex introduced=30 - adbd_auth_tls_device_connected; # apex introduced=30 - adbd_auth_tls_device_disconnected; # apex introduced=30 - adbd_auth_get_max_version; # apex introduced=30 - adbd_auth_supports_feature; # apex introduced=30 + adbd_auth_new; # systemapi introduced=30 + adbd_auth_delete; # systemapi introduced=30 + adbd_auth_run; # systemapi introduced=30 + adbd_auth_get_public_keys; # systemapi introduced=30 + adbd_auth_notify_auth; # systemapi introduced=30 + adbd_auth_notify_disconnect; # systemapi introduced=30 + adbd_auth_prompt_user; # systemapi introduced=30 + adbd_auth_prompt_user_with_id; # systemapi introduced=30 + adbd_auth_tls_device_connected; # systemapi introduced=30 + adbd_auth_tls_device_disconnected; # systemapi introduced=30 + adbd_auth_get_max_version; # systemapi introduced=30 + adbd_auth_supports_feature; # systemapi introduced=30 local: *; }; diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 76e3e66749..5e539f24e1 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -49,6 +49,9 @@ cc_library_headers { "com.android.media", "com.android.media.swcodec", ], + llndk: { + llndk_headers: true, + }, } cc_library_static { diff --git a/libs/arect/include/android/rect.h b/libs/arect/include/android/rect.h index b36728e934..d52861add0 100644 --- a/libs/arect/include/android/rect.h +++ b/libs/arect/include/android/rect.h @@ -57,7 +57,7 @@ typedef struct ARect { } ARect; #ifdef __cplusplus -}; +} #endif #endif // ANDROID_RECT_H diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp index ea3c341fe4..2bf15d45eb 100644 --- a/libs/attestation/Android.bp +++ b/libs/attestation/Android.bp @@ -28,11 +28,9 @@ cc_library_static { "-Werror", ], srcs: [ - "HmacKeyManager.cpp" + "HmacKeyManager.cpp", ], - clang: true, - shared_libs: [ "liblog", "libcrypto", diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index d8d2cf2652..baeb565b5e 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -60,100 +60,74 @@ cc_library_headers { // // Currently, these are only on system android (not vendor, not host) // TODO(b/183654927) - move these into separate libraries -libbinder_device_interface_sources = [ - "IPermissionController.cpp", - "PermissionCache.cpp", - "PermissionController.cpp", -] -cc_library { - name: "libbinder", - - version_script: "libbinder.map", - - // for vndbinder - vendor_available: true, - vndk: { - enabled: true, - }, - recovery_available: true, - double_loadable: true, - host_supported: true, - // TODO(b/153609531): remove when no longer needed. - native_bridge_supported: true, - - // TODO(b/31559095): get headers from bionic on host - include_dirs: [ - "bionic/libc/kernel/android/uapi/", - "bionic/libc/kernel/uapi/", +filegroup { + name: "libbinder_device_interface_sources", + srcs: [ + "IPermissionController.cpp", + "PermissionCache.cpp", + "PermissionController.cpp", ], +} - // libbinder does not offer a stable wire protocol. - // if a second copy of it is installed, then it may break after security - // or dessert updates. Instead, apex users should use libbinder_ndk. - apex_available: [ - "//apex_available:platform", - ], +cc_defaults { + name: "libbinder_common_defaults", + host_supported: true, srcs: [ "Binder.cpp", "BpBinder.cpp", - "BufferedTextOutput.cpp", "Debug.cpp", "FdTrigger.cpp", "IInterface.cpp", - "IMemory.cpp", - "IPCThreadState.cpp", "IResultReceiver.cpp", - "IServiceManager.cpp", - "IShellCallback.cpp", - "LazyServiceRegistrar.cpp", - "MemoryBase.cpp", - "MemoryDealer.cpp", - "MemoryHeapBase.cpp", "Parcel.cpp", - "ParcelableHolder.cpp", "ParcelFileDescriptor.cpp", - "PersistableBundle.cpp", - "ProcessState.cpp", + "RecordedTransaction.cpp", "RpcSession.cpp", "RpcServer.cpp", "RpcState.cpp", - "RpcTransportRaw.cpp", - "Static.cpp", "Stability.cpp", "Status.cpp", "TextOutput.cpp", + "Trace.cpp", "Utils.cpp", - ":libbinder_aidl", ], - target: { - android: { - srcs: libbinder_device_interface_sources, + shared_libs: [ + "libcutils", + "libutils", + ], - // NOT static to keep the wire protocol unfrozen - static: { - enabled: false, - }, - }, - vendor: { - exclude_srcs: libbinder_device_interface_sources, - }, - darwin: { - enabled: false, - }, + static_libs: [ + "libbase", + ], + + header_libs: [ + "libbinder_headers", + ], +} + +cc_defaults { + name: "libbinder_android_defaults", + + // TODO(b/31559095): get headers from bionic on host + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi/", + ], + + srcs: [ + "OS.cpp", + "RpcTransportRaw.cpp", + ], + + target: { host: { srcs: [ - "ServiceManagerHost.cpp", "UtilsHost.cpp", ], }, - recovery: { - exclude_header_libs: [ - "libandroid_runtime_vm_headers", - ], - }, }, aidl: { @@ -161,7 +135,6 @@ cc_library { }, cflags: [ - "-Wall", "-Wextra", "-Wextra-semi", "-Werror", @@ -176,30 +149,25 @@ cc_library { }, debuggable: { - cflags: ["-DBINDER_RPC_DEV_SERVERS"], + cflags: [ + "-DBINDER_RPC_DEV_SERVERS", + "-DBINDER_ENABLE_RECORDING", + ], }, }, shared_libs: [ "liblog", - "libcutils", - "libutils", - ], - - static_libs: [ - "libbase", ], header_libs: [ - "libbinder_headers", - "libandroid_runtime_vm_headers", + "jni_headers", ], export_header_lib_headers: [ "libbinder_headers", ], - clang: true, sanitize: { misc_undefined: ["integer"], }, @@ -217,6 +185,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "google-*", @@ -224,6 +193,161 @@ cc_library { "performance*", "portability*", ], +} + +cc_library_headers { + name: "trusty_mock_headers", + host_supported: true, + + export_include_dirs: [ + "trusty/include", + "trusty/include_mock", + ], + + visibility: [ + ":__subpackages__", + ], +} + +cc_defaults { + name: "trusty_mock_defaults", + host_supported: true, + + header_libs: [ + "trusty_mock_headers", + ], + + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + // Trusty libbinder uses vendor stability for its binders + "-D__ANDROID_VNDK__", + "-U__ANDROID__", + "-D__TRUSTY__", + "-DTRUSTY_USERSPACE", + // Flags from the Trusty build system + "-Werror", + "-Wsign-compare", + "-Wno-unused-function", + "-Wno-unused-label", + "-fno-common", + "-fno-omit-frame-pointer", + "-fno-threadsafe-statics", + ], + rtti: false, + + visibility: [ + ":__subpackages__", + ], +} + +cc_library_shared { + name: "libbinder_on_trusty_mock", + defaults: [ + "libbinder_common_defaults", + "trusty_mock_defaults", + ], + + srcs: [ + // Trusty-specific files + "trusty/logging.cpp", + "trusty/OS.cpp", + "trusty/RpcServerTrusty.cpp", + "trusty/RpcTransportTipcTrusty.cpp", + "trusty/TrustyStatus.cpp", + "trusty/socket.cpp", + ], +} + +cc_defaults { + name: "libbinder_kernel_defaults", + srcs: [ + "BufferedTextOutput.cpp", + "IPCThreadState.cpp", + "IServiceManager.cpp", + "ProcessState.cpp", + "Static.cpp", + ":libbinder_aidl", + ":libbinder_device_interface_sources", + ], + target: { + vendor: { + exclude_srcs: [ + ":libbinder_device_interface_sources", + ], + }, + host: { + srcs: [ + "ServiceManagerHost.cpp", + ], + }, + }, + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], + arch: { + // TODO(b/254713216): undefined symbol in BufferedTextOutput::getBuffer + riscv64: { + lto: { + thin: false, + }, + }, + }, +} + +cc_library { + name: "libbinder", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + "libbinder_kernel_defaults", + ], + + version_script: "libbinder.map", + + // for vndbinder + vendor_available: true, + vndk: { + enabled: true, + }, + recovery_available: true, + double_loadable: true, + // TODO(b/153609531): remove when no longer needed. + native_bridge_supported: true, + + // libbinder does not offer a stable wire protocol. + // if a second copy of it is installed, then it may break after security + // or dessert updates. Instead, apex users should use libbinder_ndk. + apex_available: [ + "//apex_available:platform", + ], + + srcs: [ + "IMemory.cpp", + "IShellCallback.cpp", + "LazyServiceRegistrar.cpp", + "MemoryBase.cpp", + "MemoryDealer.cpp", + "MemoryHeapBase.cpp", + "ParcelableHolder.cpp", + "PersistableBundle.cpp", + ], + + target: { + android: { + // NOT static to keep the wire protocol unfrozen + static: { + enabled: false, + }, + }, + darwin: { + enabled: false, + }, + recovery: { + exclude_header_libs: [ + "jni_headers", + ], + }, + }, afdo: true, @@ -232,6 +356,46 @@ cc_library { }, } +cc_library_static { + name: "libbinder_rpc_no_kernel", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_single_threaded", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + "libbinder_kernel_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_single_threaded_no_kernel", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + visibility: [ + ":__subpackages__", + ], +} + cc_defaults { name: "libbinder_tls_shared_deps", shared_libs: [ @@ -272,6 +436,34 @@ cc_library_shared { defaults: ["libbinder_tls_defaults"], } +cc_library_shared { + name: "libbinder_trusty", + vendor: true, + srcs: [ + "RpcTransportTipcAndroid.cpp", + "RpcTrusty.cpp", + ], + + shared_libs: [ + "libbinder", + "liblog", + "libtrusty", + "libutils", + ], + static_libs: [ + "libbase", + ], + export_include_dirs: ["include_trusty"], + + // Most of Android doesn't need this library and shouldn't use it, + // so we restrict its visibility to the Trusty-specific packages. + visibility: [ + ":__subpackages__", + "//system/core/trusty:__subpackages__", + "//vendor:__subpackages__", + ], +} + // For testing cc_library_static { name: "libbinder_tls_static", @@ -328,6 +520,10 @@ aidl_interface { enabled: false, }, }, + visibility: [ + ":__subpackages__", + "//system/tools/aidl:__subpackages__", + ], } // TODO(b/184872979): remove once the Rust API is created. @@ -338,6 +534,7 @@ cc_library { "libbase", "libbinder", "libbinder_ndk", + "libcutils_sockets", "liblog", "libutils", ], @@ -351,7 +548,11 @@ cc_library { // This library is intentionally limited to these targets, and it will be removed later. // Do not expand the visibility. visibility: [ - "//packages/modules/Virtualization:__subpackages__", + ":__subpackages__", + "//packages/modules/Virtualization/javalib/jni", + "//packages/modules/Virtualization/vm_payload", + "//device/google/cuttlefish/shared/minidroid:__subpackages__", + "//system/software_defined_vehicle:__subpackages__", ], } @@ -370,6 +571,7 @@ filegroup { cc_library { name: "libbatterystats_aidl", + host_supported: true, srcs: [ "IBatteryStats.cpp", ], @@ -382,6 +584,7 @@ cc_library { cc_library { name: "libprocessinfoservice_aidl", + host_supported: true, srcs: [ "IProcessInfoService.cpp", "ProcessInfoService.cpp", diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 39befbe8e0..3e49656575 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -27,14 +27,21 @@ #include <binder/IResultReceiver.h> #include <binder/IShellCallback.h> #include <binder/Parcel.h> +#include <binder/RecordedTransaction.h> #include <binder/RpcServer.h> +#include <cutils/compiler.h> #include <private/android_filesystem_config.h> +#include <pthread.h> #include <utils/misc.h> #include <inttypes.h> -#include <linux/sched.h> #include <stdio.h> +#ifdef __linux__ +#include <linux/sched.h> +#endif + +#include "BuildFlags.h" #include "RpcState.h" namespace android { @@ -49,10 +56,17 @@ static_assert(sizeof(IBinder) == 12); static_assert(sizeof(BBinder) == 20); #endif +// global b/c b/230079120 - consistent symbol table #ifdef BINDER_RPC_DEV_SERVERS -constexpr const bool kEnableRpcDevServers = true; +bool kEnableRpcDevServers = true; +#else +bool kEnableRpcDevServers = false; +#endif + +#ifdef BINDER_ENABLE_RECORDING +bool kEnableRecording = true; #else -constexpr const bool kEnableRpcDevServers = false; +bool kEnableRecording = false; #endif // Log any reply transactions for which the data exceeds this size @@ -156,10 +170,14 @@ status_t IBinder::getDebugPid(pid_t* out) { status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd, const sp<IBinder>& keepAliveBinder) { - if constexpr (!kEnableRpcDevServers) { + if (!kEnableRpcDevServers) { ALOGW("setRpcClientDebug disallowed because RPC is not enabled"); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } BBinder* local = this->localBinder(); if (local != nullptr) { @@ -193,6 +211,17 @@ void IBinder::withLock(const std::function<void()>& doWithLock) { proxy->withLock(doWithLock); } +sp<IBinder> IBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + BBinder* local = localBinder(); + if (local) { + return local->lookupOrCreateWeak(objectID, make, makeArgs); + } + BpBinder* proxy = this->remoteBinder(); + LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote"); + return proxy->lookupOrCreateWeak(objectID, make, makeArgs); +} + // --------------------------------------------------------------------------- class BBinder::RpcServerLink : public IBinder::DeathRecipient { @@ -201,8 +230,12 @@ public: RpcServerLink(const sp<RpcServer>& rpcServer, const sp<IBinder>& keepAliveBinder, const wp<BBinder>& binder) : mRpcServer(rpcServer), mKeepAliveBinder(keepAliveBinder), mBinder(binder) {} + virtual ~RpcServerLink(); void binderDied(const wp<IBinder>&) override { - LOG_RPC_DETAIL("RpcServerLink: binder died, shutting down RpcServer"); + auto promoted = mBinder.promote(); + ALOGI("RpcBinder: binder died, shutting down RpcServer for %s", + promoted ? String8(promoted->getInterfaceDescriptor()).c_str() : "<NULL>"); + if (mRpcServer == nullptr) { ALOGW("RpcServerLink: Unable to shut down RpcServer because it does not exist."); } else { @@ -211,11 +244,7 @@ public: } mRpcServer.clear(); - auto promoted = mBinder.promote(); - if (promoted == nullptr) { - ALOGW("RpcServerLink: Unable to remove link from parent binder object because parent " - "binder object is gone."); - } else { + if (promoted) { promoted->removeRpcServerLink(sp<RpcServerLink>::fromExisting(this)); } mBinder.clear(); @@ -226,26 +255,31 @@ private: sp<IBinder> mKeepAliveBinder; // hold to avoid automatically unlinking wp<BBinder> mBinder; }; +BBinder::RpcServerLink::~RpcServerLink() {} class BBinder::Extras { public: // unlocked objects - bool mRequestingSid = false; - bool mInheritRt = false; sp<IBinder> mExtension; +#ifdef __linux__ int mPolicy = SCHED_NORMAL; int mPriority = 0; +#endif + bool mRequestingSid = false; + bool mInheritRt = false; // for below objects Mutex mLock; std::set<sp<RpcServerLink>> mRpcServerLinks; BpBinder::ObjectManager mObjects; + + android::base::unique_fd mRecordingFd; }; // --------------------------------------------------------------------------- -BBinder::BBinder() : mExtras(nullptr), mStability(0), mParceled(false) {} +BBinder::BBinder() : mExtras(nullptr), mStability(0), mParceled(false), mRecordingOn(false) {} bool BBinder::isBinderAlive() const { @@ -257,13 +291,68 @@ status_t BBinder::pingBinder() return NO_ERROR; } +status_t BBinder::startRecordingTransactions(const Parcel& data) { + if (!kEnableRecording) { + ALOGW("Binder recording disallowed because recording is not enabled"); + return INVALID_OPERATION; + } + if (!kEnableKernelIpc) { + ALOGW("Binder recording disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (uid != AID_ROOT) { + ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid); + return PERMISSION_DENIED; + } + Extras* e = getOrCreateExtras(); + AutoMutex lock(e->mLock); + if (mRecordingOn) { + LOG(INFO) << "Could not start Binder recording. Another is already in progress."; + return INVALID_OPERATION; + } else { + status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd)); + if (readStatus != OK) { + return readStatus; + } + mRecordingOn = true; + LOG(INFO) << "Started Binder recording."; + return NO_ERROR; + } +} + +status_t BBinder::stopRecordingTransactions() { + if (!kEnableRecording) { + ALOGW("Binder recording disallowed because recording is not enabled"); + return INVALID_OPERATION; + } + if (!kEnableKernelIpc) { + ALOGW("Binder recording disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (uid != AID_ROOT) { + ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid); + return PERMISSION_DENIED; + } + Extras* e = getOrCreateExtras(); + AutoMutex lock(e->mLock); + if (mRecordingOn) { + e->mRecordingFd.reset(); + mRecordingOn = false; + LOG(INFO) << "Stopped Binder recording."; + return NO_ERROR; + } else { + LOG(INFO) << "Could not stop Binder recording. One is not in progress."; + return INVALID_OPERATION; + } +} + const String16& BBinder::getInterfaceDescriptor() const { - // This is a local static rather than a global static, - // to avoid static initializer ordering issues. - static String16 sEmptyDescriptor; - ALOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); - return sEmptyDescriptor; + static StaticString16 sBBinder(u"BBinder"); + ALOGW("Reached BBinder::getInterfaceDescriptor (this=%p). Override?", this); + return sBBinder; } // NOLINTNEXTLINE(google-default-arguments) @@ -281,6 +370,12 @@ status_t BBinder::transact( case PING_TRANSACTION: err = pingBinder(); break; + case START_RECORDING_TRANSACTION: + err = startRecordingTransactions(data); + break; + case STOP_RECORDING_TRANSACTION: + err = stopRecordingTransactions(); + break; case EXTENSION_TRANSACTION: CHECK(reply != nullptr); err = reply->writeStrongBinder(getExtension()); @@ -307,6 +402,26 @@ status_t BBinder::transact( } } + if (CC_UNLIKELY(kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION)) { + Extras* e = mExtras.load(std::memory_order_acquire); + AutoMutex lock(e->mLock); + if (mRecordingOn) { + Parcel emptyReply; + timespec ts; + timespec_get(&ts, TIME_UTC); + auto transaction = android::binder::debug::RecordedTransaction:: + fromDetails(getInterfaceDescriptor(), code, flags, ts, data, + 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; + } + } else { + LOG(INFO) << "Failed to create RecordedTransaction object."; + } + } + } + return err; } @@ -365,6 +480,14 @@ void BBinder::withLock(const std::function<void()>& doWithLock) { doWithLock(); } +sp<IBinder> BBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + Extras* e = getOrCreateExtras(); + LOG_ALWAYS_FATAL_IF(!e, "no memory"); + AutoMutex _l(e->mLock); + return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs); +} + BBinder* BBinder::localBinder() { return this; @@ -404,6 +527,7 @@ sp<IBinder> BBinder::getExtension() { return e->mExtension; } +#ifdef __linux__ void BBinder::setMinSchedulerPolicy(int policy, int priority) { LOG_ALWAYS_FATAL_IF(mParceled, "setMinSchedulerPolicy() should not be called after a binder object " @@ -448,6 +572,7 @@ int BBinder::getMinSchedulerPriority() { if (e == nullptr) return 0; return e->mPriority; } +#endif // __linux__ bool BBinder::isInheritRt() { Extras* e = mExtras.load(std::memory_order_acquire); @@ -475,7 +600,12 @@ void BBinder::setInheritRt(bool inheritRt) { } pid_t BBinder::getDebugPid() { +#ifdef __linux__ return getpid(); +#else + // TODO: handle other OSes + return 0; +#endif // __linux__ } void BBinder::setExtension(const sp<IBinder>& extension) { @@ -496,10 +626,14 @@ void BBinder::setParceled() { } status_t BBinder::setRpcClientDebug(const Parcel& data) { - if constexpr (!kEnableRpcDevServers) { + if (!kEnableRpcDevServers) { ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid != AID_ROOT) { ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid); @@ -521,10 +655,14 @@ status_t BBinder::setRpcClientDebug(const Parcel& data) { status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, const sp<IBinder>& keepAliveBinder) { - if constexpr (!kEnableRpcDevServers) { + if (!kEnableRpcDevServers) { ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } const int socketFdForPrint = socketFd.get(); LOG_RPC_DETAIL("%s(fd=%d)", __PRETTY_FUNCTION__, socketFdForPrint); @@ -539,7 +677,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, return UNEXPECTED_NULL; } - size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxThreadCount(); + size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxTotalThreadCount(); if (binderThreadPoolMaxCount <= 1) { ALOGE("%s: ProcessState thread pool max count is %zu. RPC is disabled for this service " "because RPC requires the service to support multithreading.", @@ -567,6 +705,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, return status; } rpcServer->setMaxThreads(binderThreadPoolMaxCount); + LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor(); rpcServer->start(); e->mRpcServerLinks.emplace(link); LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint); @@ -582,8 +721,24 @@ void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) { BBinder::~BBinder() { - if (!wasParceled() && getExtension()) { - ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + if (!wasParceled()) { + if (getExtension()) { + ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + } + if (isRequestingSid()) { + ALOGW("Binder %p destroyed when requesting SID before being parceled.", this); + } + if (isInheritRt()) { + 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); + } + if (getMinSchedulerPriority() != 0) { + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + } +#endif // __linux__ } Extras* e = mExtras.load(std::memory_order_relaxed); @@ -620,13 +775,14 @@ status_t BBinder::onTransact( for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } - sp<IShellCallback> shellCallback = IShellCallback::asInterface( - data.readStrongBinder()); + sp<IBinder> shellCallbackBinder = data.readStrongBinder(); sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface( data.readStrongBinder()); // XXX can't add virtuals until binaries are updated. - //return shellCommand(in, out, err, args, resultReceiver); + // sp<IShellCallback> shellCallback = IShellCallback::asInterface( + // shellCallbackBinder); + // return shellCommand(in, out, err, args, resultReceiver); (void)in; (void)out; (void)err; diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 921e57c7bf..53852d88ed 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -28,6 +28,10 @@ #include <stdio.h> +#include "BuildFlags.h" + +#include <android-base/file.h> + //#undef ALOGV //#define ALOGV(...) fprintf(stderr, __VA_ARGS__) @@ -43,6 +47,8 @@ std::atomic_bool BpBinder::sCountByUidEnabled(false); binder_proxy_limit_callback BpBinder::sLimitCallback; bool BpBinder::sBinderProxyThrottleCreate = false; +static StaticString16 kDescriptorUninit(u"<uninit descriptor>"); + // Arbitrarily high value that probably distinguishes a bad behaving app uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; // Another arbitrary value a binder count needs to drop below before another callback will be called @@ -98,6 +104,36 @@ void* BpBinder::ObjectManager::detach(const void* objectID) { return value; } +namespace { +struct Tag { + wp<IBinder> binder; +}; +} // namespace + +static void cleanWeak(const void* /* id */, void* obj, void* /* cookie */) { + delete static_cast<Tag*>(obj); +} + +sp<IBinder> BpBinder::ObjectManager::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + entry_t& e = mObjects[objectID]; + if (e.object != nullptr) { + if (auto attached = static_cast<Tag*>(e.object)->binder.promote()) { + return attached; + } + } else { + e.object = new Tag; + LOG_ALWAYS_FATAL_IF(!e.object, "no more memory"); + } + sp<IBinder> newObj = make(makeArgs); + + static_cast<Tag*>(e.object)->binder = newObj; + e.cleanupCookie = nullptr; + e.func = cleanWeak; + + return newObj; +} + void BpBinder::ObjectManager::kill() { const size_t N = mObjects.size(); @@ -115,6 +151,11 @@ void BpBinder::ObjectManager::kill() // --------------------------------------------------------------------------- sp<BpBinder> BpBinder::create(int32_t handle) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return nullptr; + } + int32_t trackedUid = -1; if (sCountByUidEnabled) { trackedUid = IPCThreadState::self()->getCallingUid(); @@ -172,11 +213,17 @@ BpBinder::BpBinder(Handle&& handle) mAlive(true), mObitsSent(false), mObituaries(nullptr), + mDescriptorCache(kDescriptorUninit), mTrackedUid(-1) { extendObjectLifetime(OBJECT_LIFETIME_WEAK); } BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + mTrackedUid = trackedUid; ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle()); @@ -214,12 +261,12 @@ std::optional<int32_t> BpBinder::getDebugBinderHandle() const { bool BpBinder::isDescriptorCached() const { Mutex::Autolock _l(mLock); - return mDescriptorCache.size() ? true : false; + return mDescriptorCache.string() != kDescriptorUninit.string(); } const String16& BpBinder::getInterfaceDescriptor() const { - if (isDescriptorCached() == false) { + if (!isDescriptorCached()) { sp<BpBinder> thiz = sp<BpBinder>::fromExisting(const_cast<BpBinder*>(this)); Parcel data; @@ -232,8 +279,7 @@ const String16& BpBinder::getInterfaceDescriptor() const Mutex::Autolock _l(mLock); // mDescriptorCache could have been assigned while the lock was // released. - if (mDescriptorCache.size() == 0) - mDescriptorCache = res; + if (mDescriptorCache.string() == kDescriptorUninit.string()) mDescriptorCache = res; } } @@ -257,6 +303,18 @@ status_t BpBinder::pingBinder() return transact(PING_TRANSACTION, data, &reply); } +status_t BpBinder::startRecordingBinder(const android::base::unique_fd& fd) { + Parcel send, reply; + send.writeUniqueFileDescriptor(fd); + return transact(START_RECORDING_TRANSACTION, send, &reply); +} + +status_t BpBinder::stopRecordingBinder() { + Parcel data, reply; + data.markForBinder(sp<BpBinder>::fromExisting(this)); + return transact(STOP_RECORDING_TRANSACTION, data, &reply); +} + status_t BpBinder::dump(int fd, const Vector<String16>& args) { Parcel send; @@ -279,7 +337,7 @@ status_t BpBinder::transact( if (mAlive) { bool privateVendor = flags & FLAG_PRIVATE_VENDOR; // don't send userspace flags to the kernel - flags = flags & ~FLAG_PRIVATE_VENDOR; + flags = flags & ~static_cast<uint32_t>(FLAG_PRIVATE_VENDOR); // user transactions require a given stability level if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) { @@ -303,15 +361,17 @@ status_t BpBinder::transact( status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply, flags); } else { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } + status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { Mutex::Autolock _l(mLock); ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d", - data.dataSize(), - mDescriptorCache.size() ? String8(mDescriptorCache).c_str() - : "<uncached descriptor>", - code); + data.dataSize(), String8(mDescriptorCache).c_str(), code); } if (status == DEAD_OBJECT) mAlive = 0; @@ -326,7 +386,25 @@ status_t BpBinder::transact( status_t BpBinder::linkToDeath( const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags) { - if (isRpcBinder()) return UNKNOWN_TRANSACTION; + if (isRpcBinder()) { + if (rpcSession()->getMaxIncomingThreads() < 1) { + ALOGE("Cannot register a DeathRecipient without any incoming threads. Need to set max " + "incoming threads to a value greater than 0 before calling linkToDeath."); + return INVALID_OPERATION; + } + } else if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } else { + if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0) { + ALOGW("Linking to death on %s but there are no threads (yet?) listening to incoming " + "transactions. See ProcessState::startThreadPool and " + "ProcessState::setThreadPoolMaxThreadCount. Generally you should setup the " + "binder " + "threadpool before other initialization steps.", + String8(getInterfaceDescriptor()).c_str()); + } + } Obituary ob; ob.recipient = recipient; @@ -346,10 +424,14 @@ status_t BpBinder::linkToDeath( return NO_MEMORY; } ALOGV("Requesting death notification: %p handle %d\n", this, binderHandle()); - getWeakRefs()->incWeak(this); - IPCThreadState* self = IPCThreadState::self(); - self->requestDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } } ssize_t res = mObituaries->add(ob); return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; @@ -364,7 +446,10 @@ status_t BpBinder::unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, wp<DeathRecipient>* outRecipient) { - if (isRpcBinder()) return UNKNOWN_TRANSACTION; + if (!kEnableKernelIpc && !isRpcBinder()) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } AutoMutex _l(mLock); @@ -384,9 +469,13 @@ status_t BpBinder::unlinkToDeath( mObituaries->removeAt(i); if (mObituaries->size() == 0) { ALOGV("Clearing death notification: %p handle %d\n", this, binderHandle()); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } delete mObituaries; mObituaries = nullptr; } @@ -399,7 +488,10 @@ status_t BpBinder::unlinkToDeath( void BpBinder::sendObituary() { - LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder."); + if (!kEnableKernelIpc && !isRpcBinder()) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(), mObitsSent ? "true" : "false"); @@ -411,9 +503,13 @@ void BpBinder::sendObituary() Vector<Obituary>* obits = mObituaries; if(obits != nullptr) { ALOGV("Clearing sent death notification: %p handle %d\n", this, binderHandle()); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } mObituaries = nullptr; } mObitsSent = 1; @@ -464,17 +560,27 @@ void BpBinder::withLock(const std::function<void()>& doWithLock) { doWithLock(); } +sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + AutoMutex _l(mLock); + return mObjects.lookupOrCreateWeak(objectID, make, makeArgs); +} + BpBinder* BpBinder::remoteBinder() { return this; } -BpBinder::~BpBinder() -{ - ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); - +BpBinder::~BpBinder() { if (CC_UNLIKELY(isRpcBinder())) return; + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); + IPCThreadState* ipc = IPCThreadState::self(); if (mTrackedUid >= 0) { @@ -505,21 +611,31 @@ BpBinder::~BpBinder() } } -void BpBinder::onFirstRef() -{ - ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); +void BpBinder::onFirstRef() { if (CC_UNLIKELY(isRpcBinder())) return; + + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->incStrongHandle(binderHandle(), this); } -void BpBinder::onLastStrongRef(const void* /*id*/) -{ - ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); +void BpBinder::onLastStrongRef(const void* /*id*/) { if (CC_UNLIKELY(isRpcBinder())) { (void)rpcSession()->sendDecStrong(this); return; } + + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); IF_ALOGV() { printRefs(); } @@ -531,7 +647,7 @@ void BpBinder::onLastStrongRef(const void* /*id*/) if(obits != nullptr) { if (!obits->isEmpty()) { ALOGI("onLastStrongRef automatically unlinking death recipients: %s", - mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>"); + String8(mDescriptorCache).c_str()); } if (ipc) ipc->clearDeathNotification(binderHandle(), this); @@ -552,6 +668,11 @@ 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 constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return false; + } + ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false; diff --git a/libs/binder/BuildFlags.h b/libs/binder/BuildFlags.h new file mode 100644 index 0000000000..3e9d1c2b4a --- /dev/null +++ b/libs/binder/BuildFlags.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +#ifdef BINDER_RPC_SINGLE_THREADED +constexpr bool kEnableRpcThreads = false; +#else +constexpr bool kEnableRpcThreads = true; +#endif + +#ifdef BINDER_WITH_KERNEL_IPC +constexpr bool kEnableKernelIpc = true; +#else // BINDER_WITH_KERNEL_IPC +constexpr bool kEnableKernelIpc = false; +#endif // BINDER_WITH_KERNEL_IPC + +} // namespace android diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index e4ac4b49a4..c6e4fb378d 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -15,6 +15,7 @@ */ #include "Debug.h" +#include "BuildFlags.h" #include <binder/ProcessState.h> @@ -301,6 +302,11 @@ void printHexData(int32_t indent, const void *buf, size_t length, } ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return 0; + } + sp<ProcessState> proc = ProcessState::selfOrNull(); if (proc.get() == nullptr) { return 0; diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 5e22593f69..8ee6cb0615 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -22,39 +22,70 @@ #include <poll.h> #include <android-base/macros.h> +#include <android-base/scopeguard.h> #include "RpcState.h" namespace android { std::unique_ptr<FdTrigger> FdTrigger::make() { auto ret = std::make_unique<FdTrigger>(); +#ifndef BINDER_RPC_SINGLE_THREADED if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) { ALOGE("Could not create pipe %s", strerror(errno)); return nullptr; } +#endif return ret; } void FdTrigger::trigger() { +#ifdef BINDER_RPC_SINGLE_THREADED + mTriggered = true; +#else mWrite.reset(); +#endif } bool FdTrigger::isTriggered() { +#ifdef BINDER_RPC_SINGLE_THREADED + return mTriggered; +#else return mWrite == -1; +#endif } -status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { - LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get()); - pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, - {.fd = mRead.get(), .events = 0, .revents = 0}}; +status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, int16_t event) { +#ifdef BINDER_RPC_SINGLE_THREADED + if (mTriggered) { + return DEAD_OBJECT; + } +#endif + + LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", + transportFd.fd.get()); + pollfd pfd[]{ + {.fd = transportFd.fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, +#ifndef BINDER_RPC_SINGLE_THREADED + {.fd = mRead.get(), .events = 0, .revents = 0}, +#endif + }; + + LOG_ALWAYS_FATAL_IF(transportFd.isInPollingState() == true, + "Only one thread should be polling on Fd!"); + + transportFd.setPollingState(true); + auto pollingStateGuard = + android::base::make_scope_guard([&]() { transportFd.setPollingState(false); }); + int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); if (ret < 0) { return -errno; } - LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", fd.get()); + LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", transportFd.fd.get()); // At least one FD has events. Check them. +#ifndef BINDER_RPC_SINGLE_THREADED // Detect explicit trigger(): DEAD_OBJECT if (pfd[1].revents & POLLHUP) { return DEAD_OBJECT; @@ -68,6 +99,7 @@ status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { // pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are // a subset of event | POLLHUP | POLLERR | POLLNVAL. +#endif // POLLNVAL: invalid FD number, e.g. not opened. if (pfd[0].revents & POLLNVAL) { diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h index a545d6cbea..5fbf2908ad 100644 --- a/libs/binder/FdTrigger.h +++ b/libs/binder/FdTrigger.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #include <memory> @@ -20,6 +21,8 @@ #include <android-base/unique_fd.h> #include <utils/Errors.h> +#include <binder/RpcTransport.h> + namespace android { /** This is not a pipe. */ @@ -52,10 +55,15 @@ public: * true - time to read! * false - trigger happened */ - [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event); + [[nodiscard]] status_t triggerablePoll(const android::RpcTransportFd& transportFd, + int16_t event); private: +#ifdef BINDER_RPC_SINGLE_THREADED + bool mTriggered = false; +#else base::unique_fd mWrite; base::unique_fd mRead; +#endif }; } // namespace android diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 3c97dcab93..da58251149 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -39,18 +39,17 @@ #include <sys/resource.h> #include <unistd.h> -#include "Static.h" #include "binder_module.h" #if LOG_NDEBUG #define IF_LOG_TRANSACTIONS() if (false) #define IF_LOG_COMMANDS() if (false) -#define LOG_REMOTEREFS(...) +#define LOG_REMOTEREFS(...) #define IF_LOG_REMOTEREFS() if (false) -#define LOG_THREADPOOL(...) -#define LOG_ONEWAY(...) +#define LOG_THREADPOOL(...) +#define LOG_ONEWAY(...) #else @@ -124,46 +123,57 @@ static const char* getReturnString(uint32_t cmd) return "unknown"; } -static const void* printBinderTransactionData(TextOutput& out, const void* data) -{ +static const void* printBinderTransactionData(std::ostream& out, const void* data) { const binder_transaction_data* btd = (const binder_transaction_data*)data; if (btd->target.handle < 1024) { /* want to print descriptors in decimal; guess based on value */ - out << "target.desc=" << btd->target.handle; + out << "\ttarget.desc=" << btd->target.handle; } else { - out << "target.ptr=" << btd->target.ptr; + out << "\ttarget.ptr=" << btd->target.ptr; } - out << " (cookie " << btd->cookie << ")" << endl - << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << endl - << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size - << " bytes)" << endl - << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size - << " bytes)"; + out << "\t (cookie " << btd->cookie << ")\n" + << "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n" + << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)\n" + << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)\n"; + return btd + 1; +} + +static const void* printBinderTransactionDataSecCtx(std::ostream& out, const void* data) { + const binder_transaction_data_secctx* btd = (const binder_transaction_data_secctx*)data; + + printBinderTransactionData(out, &btd->transaction_data); + + char* secctx = (char*)btd->secctx; + out << "\tsecctx=" << secctx << "\n"; + return btd+1; } -static const void* printReturnCommand(TextOutput& out, const void* _cmd) -{ +static const void* printReturnCommand(std::ostream& out, const void* _cmd) { static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); const int32_t* cmd = (const int32_t*)_cmd; uint32_t code = (uint32_t)*cmd++; size_t cmdIndex = code & 0xff; if (code == BR_ERROR) { - out << "BR_ERROR: " << (void*)(uint64_t)(*cmd++) << endl; + out << "\tBR_ERROR: " << (void*)(uint64_t)(*cmd++) << "\n"; return cmd; } else if (cmdIndex >= N) { - out << "Unknown reply: " << code << endl; + out << "\tUnknown reply: " << code << "\n"; return cmd; } - out << kReturnStrings[cmdIndex]; + out << "\t" << kReturnStrings[cmdIndex]; switch (code) { + case BR_TRANSACTION_SEC_CTX: { + out << ": "; + cmd = (const int32_t*)printBinderTransactionDataSecCtx(out, cmd); + } break; + case BR_TRANSACTION: case BR_REPLY: { - out << ": " << indent; - cmd = (const int32_t *)printBinderTransactionData(out, cmd); - out << dedent; + out << ": "; + cmd = (const int32_t*)printBinderTransactionData(out, cmd); } break; case BR_ACQUIRE_RESULT: { @@ -200,19 +210,18 @@ static const void* printReturnCommand(TextOutput& out, const void* _cmd) break; } - out << endl; + out << "\n"; return cmd; } -static const void* printCommand(TextOutput& out, const void* _cmd) -{ +static const void* printCommand(std::ostream& out, const void* _cmd) { static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); const int32_t* cmd = (const int32_t*)_cmd; uint32_t code = (uint32_t)*cmd++; size_t cmdIndex = code & 0xff; if (cmdIndex >= N) { - out << "Unknown command: " << code << endl; + out << "Unknown command: " << code << "\n"; return cmd; } out << kCommandStrings[cmdIndex]; @@ -220,9 +229,8 @@ static const void* printCommand(TextOutput& out, const void* _cmd) switch (code) { case BC_TRANSACTION: case BC_REPLY: { - out << ": " << indent; - cmd = (const int32_t *)printBinderTransactionData(out, cmd); - out << dedent; + out << ": "; + cmd = (const int32_t*)printBinderTransactionData(out, cmd); } break; case BC_ACQUIRE_RESULT: { @@ -274,7 +282,7 @@ static const void* printCommand(TextOutput& out, const void* _cmd) break; } - out << endl; + out << "\n"; return cmd; } @@ -400,14 +408,92 @@ void IPCThreadState::checkContextIsBinderForUse(const char* use) const { // context, so we don't abort } +constexpr uint32_t encodeExplicitIdentity(bool hasExplicitIdentity, pid_t callingPid) { + uint32_t as_unsigned = static_cast<uint32_t>(callingPid); + if (hasExplicitIdentity) { + return as_unsigned | (1 << 30); + } else { + return as_unsigned & ~(1 << 30); + } +} + +constexpr int64_t packCallingIdentity(bool hasExplicitIdentity, uid_t callingUid, + pid_t callingPid) { + // Calling PID is a 32-bit signed integer, but doesn't consume the entire 32 bit space. + // To future-proof this and because we have extra capacity, we decided to also support -1, + // since this constant is used to represent invalid UID in other places of the system. + // Thus, we pack hasExplicitIdentity into the 2nd bit from the left. This allows us to + // preserve the (left-most) bit for the sign while also encoding the value of + // hasExplicitIdentity. + // 32b | 1b | 1b | 30b + // token = [ calling uid | calling pid(sign) | has explicit identity | calling pid(rest) ] + uint64_t token = (static_cast<uint64_t>(callingUid) << 32) | + encodeExplicitIdentity(hasExplicitIdentity, callingPid); + return static_cast<int64_t>(token); +} + +constexpr bool unpackHasExplicitIdentity(int64_t token) { + return static_cast<int32_t>(token) & (1 << 30); +} + +constexpr uid_t unpackCallingUid(int64_t token) { + return static_cast<uid_t>(token >> 32); +} + +constexpr pid_t unpackCallingPid(int64_t token) { + int32_t encodedPid = static_cast<int32_t>(token); + if (encodedPid & (1 << 31)) { + return encodedPid | (1 << 30); + } else { + return encodedPid & ~(1 << 30); + } +} + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, 9999)) == true, + "pack true hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(true, 1000, 9999)) == 1000, "pack true uid"); + +static_assert(unpackCallingPid(packCallingIdentity(true, 1000, 9999)) == 9999, "pack true pid"); + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, 9999)) == false, + "pack false hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(false, 1000, 9999)) == 1000, "pack false uid"); + +static_assert(unpackCallingPid(packCallingIdentity(false, 1000, 9999)) == 9999, "pack false pid"); + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, -1)) == true, + "pack true (negative) hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(true, 1000, -1)) == 1000, + "pack true (negative) uid"); + +static_assert(unpackCallingPid(packCallingIdentity(true, 1000, -1)) == -1, + "pack true (negative) pid"); + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, -1)) == false, + "pack false (negative) hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(false, 1000, -1)) == 1000, + "pack false (negative) uid"); + +static_assert(unpackCallingPid(packCallingIdentity(false, 1000, -1)) == -1, + "pack false (negative) pid"); + int64_t IPCThreadState::clearCallingIdentity() { // ignore mCallingSid for legacy reasons - int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; + int64_t token = packCallingIdentity(mHasExplicitIdentity, mCallingUid, mCallingPid); clearCaller(); + mHasExplicitIdentity = true; return token; } +bool IPCThreadState::hasExplicitIdentity() { + return mHasExplicitIdentity; +} + void IPCThreadState::setStrictModePolicy(int32_t policy) { mStrictModePolicy = policy; @@ -480,9 +566,10 @@ ProcessState::CallRestriction IPCThreadState::getCallRestriction() const { void IPCThreadState::restoreCallingIdentity(int64_t token) { - mCallingUid = (int)(token>>32); + mCallingUid = unpackCallingUid(token); mCallingSid = nullptr; // not enough data to restore - mCallingPid = (int)token; + mCallingPid = unpackCallingPid(token); + mHasExplicitIdentity = unpackHasExplicitIdentity(token); } void IPCThreadState::clearCaller() @@ -548,8 +635,10 @@ status_t IPCThreadState::getAndExecuteCommand() if (IN < sizeof(int32_t)) return result; cmd = mIn.readInt32(); IF_LOG_COMMANDS() { - alog << "Processing top-level Command: " - << getReturnString(cmd) << endl; + std::ostringstream logStream; + logStream << "Processing top-level Command: " << getReturnString(cmd) << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } pthread_mutex_lock(&mProcess->mThreadCountLock); @@ -638,7 +727,9 @@ void IPCThreadState::processPostWriteDerefs() void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); - + pthread_mutex_lock(&mProcess->mThreadCountLock); + mProcess->mCurrentThreads++; + pthread_mutex_unlock(&mProcess->mThreadCountLock); mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); mIsLooper = true; @@ -666,6 +757,13 @@ void IPCThreadState::joinThreadPool(bool isMain) mOut.writeInt32(BC_EXIT_LOOPER); mIsLooper = false; talkWithDriver(false); + pthread_mutex_lock(&mProcess->mThreadCountLock); + LOG_ALWAYS_FATAL_IF(mProcess->mCurrentThreads == 0, + "Threadpool thread count = 0. Thread cannot exist and exit in empty " + "threadpool\n" + "Misconfiguration. Increase threadpool max threads configuration\n"); + mProcess->mCurrentThreads--; + pthread_mutex_unlock(&mProcess->mThreadCountLock); } status_t IPCThreadState::setupPolling(int* fd) @@ -677,6 +775,9 @@ status_t IPCThreadState::setupPolling(int* fd) mOut.writeInt32(BC_ENTER_LOOPER); flushCommands(); *fd = mProcess->mDriverFD; + pthread_mutex_lock(&mProcess->mThreadCountLock); + mProcess->mCurrentThreads++; + pthread_mutex_unlock(&mProcess->mThreadCountLock); return 0; } @@ -714,10 +815,11 @@ status_t IPCThreadState::transact(int32_t handle, flags |= TF_ACCEPT_FDS; IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " - << handle << " / code " << TypeCode(code) << ": " - << indent << data << dedent << endl; + std::ostringstream logStream; + logStream << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " << handle + << " / code " << TypeCode(code) << ": \t" << data << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), @@ -762,11 +864,15 @@ status_t IPCThreadState::transact(int32_t handle, #endif IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " - << handle << ": "; - if (reply) alog << indent << *reply << dedent << endl; - else alog << "(none requested)" << endl; + std::ostringstream logStream; + logStream << "BR_REPLY thr " << (void*)pthread_self() << " / hand " << handle << ": "; + if (reply) + logStream << "\t" << *reply << "\n"; + else + logStream << "(none requested)" + << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } } else { err = waitForResponse(nullptr, nullptr); @@ -876,6 +982,7 @@ IPCThreadState::IPCThreadState() mCallRestriction(mProcess->mCallRestriction) { pthread_setspecific(gTLS, this); clearCaller(); + mHasExplicitIdentity = false; mIn.setDataCapacity(256); mOut.setDataCapacity(256); } @@ -908,8 +1015,10 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) cmd = (uint32_t)mIn.readInt32(); IF_LOG_COMMANDS() { - alog << "Processing waitForResponse Command: " - << getReturnString(cmd) << endl; + std::ostringstream logStream; + logStream << "Processing waitForResponse Command: " << getReturnString(cmd) << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } switch (cmd) { @@ -922,6 +1031,10 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) if (!reply && !acquireResult) goto finish; break; + case BR_TRANSACTION_PENDING_FROZEN: + ALOGW("Sending oneway calls to frozen process."); + goto finish; + case BR_DEAD_REPLY: err = DEAD_OBJECT; goto finish; @@ -960,18 +1073,15 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) freeBuffer); } else { err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer); - freeBuffer(nullptr, - reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), - tr.offsets_size/sizeof(binder_size_t)); + freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), + tr.offsets_size / sizeof(binder_size_t)); } } else { - freeBuffer(nullptr, - reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), - tr.offsets_size/sizeof(binder_size_t)); + freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size, + reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), + tr.offsets_size / sizeof(binder_size_t)); continue; } } @@ -989,6 +1099,7 @@ finish: if (acquireResult) *acquireResult = err; if (reply) reply->setError(err); mLastError = err; + logExtendedError(); } return err; @@ -1023,17 +1134,19 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) } IF_LOG_COMMANDS() { - TextOutput::Bundle _b(alog); + std::ostringstream logStream; if (outAvail != 0) { - alog << "Sending commands to driver: " << indent; + logStream << "Sending commands to driver: "; const void* cmds = (const void*)bwr.write_buffer; - const void* end = ((const uint8_t*)cmds)+bwr.write_size; - alog << HexDump(cmds, bwr.write_size) << endl; - while (cmds < end) cmds = printCommand(alog, cmds); - alog << dedent; + const void* end = ((const uint8_t*)cmds) + bwr.write_size; + logStream << "\t" << HexDump(cmds, bwr.write_size) << "\n"; + while (cmds < end) cmds = printCommand(logStream, cmds); } - alog << "Size of receive buffer: " << bwr.read_size - << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; + logStream << "Size of receive buffer: " << bwr.read_size << ", needRead: " << needRead + << ", doReceive: " << doReceive << "\n"; + + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } // Return immediately if there is nothing to do. @@ -1044,7 +1157,10 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) status_t err; do { IF_LOG_COMMANDS() { - alog << "About to read/write, write size = " << mOut.dataSize() << endl; + std::ostringstream logStream; + logStream << "About to read/write, write size = " << mOut.dataSize() << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } #if defined(__ANDROID__) if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) @@ -1058,14 +1174,20 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) err = -EBADF; } IF_LOG_COMMANDS() { - alog << "Finished read/write, write size = " << mOut.dataSize() << endl; + std::ostringstream logStream; + logStream << "Finished read/write, write size = " << mOut.dataSize() << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } } while (err == -EINTR); IF_LOG_COMMANDS() { - alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: " - << bwr.write_consumed << " (of " << mOut.dataSize() - << "), read consumed: " << bwr.read_consumed << endl; + std::ostringstream logStream; + logStream << "Our err: " << (void*)(intptr_t)err + << ", write consumed: " << bwr.write_consumed << " (of " << mOut.dataSize() + << "), read consumed: " << bwr.read_consumed << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } if (err >= NO_ERROR) { @@ -1086,18 +1208,23 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) mIn.setDataPosition(0); } IF_LOG_COMMANDS() { - TextOutput::Bundle _b(alog); - alog << "Remaining data size: " << mOut.dataSize() << endl; - alog << "Received commands from driver: " << indent; + std::ostringstream logStream; + logStream << "Remaining data size: " << mOut.dataSize() << "\n"; + logStream << "Received commands from driver: "; const void* cmds = mIn.data(); const void* end = mIn.data() + mIn.dataSize(); - alog << HexDump(cmds, mIn.dataSize()) << endl; - while (cmds < end) cmds = printReturnCommand(alog, cmds); - alog << dedent; + logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n"; + while (cmds < end) cmds = printReturnCommand(logStream, cmds); + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } return NO_ERROR; } + ALOGE_IF(mProcess->mDriverFD >= 0, + "Driver returned error (%s). This is a bug in either libbinder or the driver. This " + "thread's connection to %s will no longer work.", + statusToString(err).c_str(), mProcess->mDriverName.c_str()); return err; } @@ -1254,6 +1381,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) const pid_t origPid = mCallingPid; const char* origSid = mCallingSid; const uid_t origUid = mCallingUid; + const bool origHasExplicitIdentity = mHasExplicitIdentity; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; const int32_t origWorkSource = mWorkSource; @@ -1267,6 +1395,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = tr.sender_pid; mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx); mCallingUid = tr.sender_euid; + mHasExplicitIdentity = false; mLastTransactionBinderFlags = tr.flags; // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid, @@ -1275,15 +1404,15 @@ status_t IPCThreadState::executeCommand(int32_t cmd) Parcel reply; status_t error; IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BR_TRANSACTION thr " << (void*)pthread_self() - << " / obj " << tr.target.ptr << " / code " - << TypeCode(tr.code) << ": " << indent << buffer - << dedent << endl - << "Data addr = " - << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer) - << ", offsets addr=" - << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl; + std::ostringstream logStream; + logStream << "BR_TRANSACTION thr " << (void*)pthread_self() << " / obj " + << tr.target.ptr << " / code " << TypeCode(tr.code) << ": \t" << buffer + << "\n" + << "Data addr = " << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer) + << ", offsets addr=" + << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } if (tr.target.ptr) { // We only have a weak reference on the target object, so we must first try to @@ -1308,25 +1437,32 @@ status_t IPCThreadState::executeCommand(int32_t cmd) LOG_ONEWAY("Sending reply to %d!", mCallingPid); if (error < NO_ERROR) reply.setError(error); + // b/238777741: clear buffer before we send the reply. + // Otherwise, there is a race where the client may + // receive the reply and send another transaction + // here and the space used by this transaction won't + // be freed for the client. + buffer.setDataSize(0); + constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF; sendReply(reply, (tr.flags & kForwardReplyFlags)); } else { if (error != OK) { - alog << "oneway function results for code " << tr.code - << " on binder at " - << reinterpret_cast<void*>(tr.target.ptr) - << " will be dropped but finished with status " - << statusToString(error); + std::ostringstream logStream; + logStream << "oneway function results for code " << tr.code << " on binder at " + << reinterpret_cast<void*>(tr.target.ptr) + << " will be dropped but finished with status " + << statusToString(error); // ideally we could log this even when error == OK, but it // causes too much logspam because some manually-written // interfaces have clients that call methods which always // write results, sometimes as oneway methods. if (reply.dataSize() != 0) { - alog << " and reply parcel size " << reply.dataSize(); + logStream << " and reply parcel size " << reply.dataSize(); } - - alog << endl; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } @@ -1335,15 +1471,18 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = origPid; mCallingSid = origSid; mCallingUid = origUid; + mHasExplicitIdentity = origHasExplicitIdentity; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; mWorkSource = origWorkSource; mPropagateWorkSource = origPropagateWorkSet; IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " - << tr.target.ptr << ": " << indent << reply << dedent << endl; + std::ostringstream logStream; + logStream << "BC_REPLY thr " << (void*)pthread_self() << " / obj " << tr.target.ptr + << ": \t" << reply << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } } @@ -1443,17 +1582,33 @@ status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { return ret; } -void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, - size_t /*dataSize*/, - const binder_size_t* /*objects*/, - size_t /*objectsSize*/) -{ +void IPCThreadState::logExtendedError() { + struct binder_extended_error ee = {.command = BR_OK}; + + if (!ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::EXTENDED_ERROR)) + return; + +#if defined(__ANDROID__) + if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0) { + ALOGE("Failed to get extended error: %s", strerror(errno)); + return; + } +#endif + + ALOGE_IF(ee.command != BR_OK, "Binder transaction failure: %d/%d/%d", + ee.id, ee.command, ee.param); +} + +void IPCThreadState::freeBuffer(const uint8_t* data, size_t /*dataSize*/, + const binder_size_t* /*objects*/, size_t /*objectsSize*/) { //ALOGI("Freeing parcel %p", &parcel); IF_LOG_COMMANDS() { - alog << "Writing BC_FREE_BUFFER for " << data << endl; + std::ostringstream logStream; + logStream << "Writing BC_FREE_BUFFER for " << data << "\n"; + std::string message = logStream.str(); + ALOGI("%s", message.c_str()); } ALOG_ASSERT(data != NULL, "Called with NULL data"); - if (parcel != nullptr) parcel->closeFileDescriptors(); IPCThreadState* state = self(); state->mOut.writeInt32(BC_FREE_BUFFER); state->mOut.writePointer((uintptr_t)data); diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index fd2d86857e..2408307459 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -14,13 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "ServiceManager" +#define LOG_TAG "ServiceManagerCppClient" #include <binder/IServiceManager.h> #include <inttypes.h> #include <unistd.h> +#include <android-base/properties.h> #include <android/os/BnServiceCallback.h> #include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> @@ -80,6 +81,7 @@ public: bool isDeclared(const String16& name) override; Vector<String16> getDeclaredInstances(const String16& interface) override; std::optional<String16> updatableViaApex(const String16& name) override; + Vector<String16> getUpdatableNames(const String16& apexName) override; std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override; class RegistrationWaiter : public android::os::BnServiceCallback { public: @@ -140,6 +142,16 @@ protected: sp<IServiceManager> defaultServiceManager() { std::call_once(gSmOnce, []() { +#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__) + /* wait for service manager */ { + using std::literals::chrono_literals::operator""s; + using android::base::WaitForProperty; + while (!WaitForProperty("servicemanager.ready", "true", 1s)) { + ALOGE("Waited for servicemanager.ready for a second, waiting another..."); + } + } +#endif + sp<AidlServiceManager> sm = nullptr; while (sm == nullptr) { sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr)); @@ -167,7 +179,7 @@ void setDefaultServiceManager(const sp<IServiceManager>& sm) { } } -#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__) +#if !defined(__ANDROID_VNDK__) // IPermissionController is not accessible to vendors bool checkCallingPermission(const String16& permission) @@ -369,6 +381,13 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) if (Status status = realGetService(name, &out); !status.isOk()) { ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(), status.toString8().c_str()); + if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) { + ALOGW("Got service, but may be racey because we could not wait efficiently for it. " + "Threadpool has 0 guaranteed threads. " + "Is the threadpool configured properly? " + "See ProcessState::startThreadPool and " + "ProcessState::setThreadPoolMaxThreadCount."); + } return nullptr; } if (out != nullptr) return out; @@ -399,7 +418,9 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) if (waiter->mBinder != nullptr) return waiter->mBinder; } - ALOGW("Waited one second for %s (is service started? are binder threads started and available?)", name.c_str()); + ALOGW("Waited one second for %s (is service started? Number of threads started in the " + "threadpool: %zu. Are binder threads started and available?)", + name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount()); // Handle race condition for lazy services. Here is what can happen: // - the service dies (not processed by init yet). @@ -423,7 +444,7 @@ bool ServiceManagerShim::isDeclared(const String16& name) { bool declared; if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared); !status.isOk()) { - ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(), + ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(), status.toString8().c_str()); return false; } @@ -459,6 +480,23 @@ std::optional<String16> ServiceManagerShim::updatableViaApex(const String16& nam return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt; } +Vector<String16> ServiceManagerShim::getUpdatableNames(const String16& apexName) { + std::vector<std::string> out; + if (Status status = mTheRealServiceManager->getUpdatableNames(String8(apexName).c_str(), &out); + !status.isOk()) { + ALOGW("Failed to getUpdatableNames for %s: %s", String8(apexName).c_str(), + status.toString8().c_str()); + return {}; + } + + Vector<String16> res; + res.setCapacity(out.size()); + for (const std::string& instance : out) { + res.push(String16(instance.c_str())); + } + return res; +} + std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnectionInfo( const String16& name) { std::optional<os::ConnectionInfo> connectionInfo; diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 8132d46940..8fe1d2bb3d 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -74,7 +74,7 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) fd = memfd_create_region(name ? name : "MemoryHeapBase", size); if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | - ((mFlags & MEMFD_ALLOW_SEALING) ? 0 : F_SEAL_SEAL); + ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL); if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) { ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name, SEAL_FLAGS, strerror(errno)); @@ -85,12 +85,9 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) } return; #else - mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING); + mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG); #endif } - if (mFlags & MEMFD_ALLOW_SEALING) { - LOG_ALWAYS_FATAL("Invalid Flags. MEMFD_ALLOW_SEALING only valid with FORCE_MEMFD."); - } fd = ashmem_create_region(name ? name : "MemoryHeapBase", size); ALOGE_IF(fd < 0, "MemoryHeapBase: error creating ashmem region: %s", strerror(errno)); if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; @@ -103,7 +100,7 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { - if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) { + if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) { LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); } int open_flags = O_RDWR; @@ -125,7 +122,7 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { - if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) { + if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) { LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); } const size_t pagesize = getpagesize(); diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp new file mode 100644 index 0000000000..ce60e33ba7 --- /dev/null +++ b/libs/binder/OS.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OS.h" + +#include <android-base/file.h> +#include <binder/RpcTransportRaw.h> +#include <log/log.h> +#include <string.h> + +using android::base::ErrnoError; +using android::base::Result; + +namespace android { + +// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets. +constexpr size_t kMaxFdsPerMsg = 253; + +Result<void> setNonBlocking(android::base::borrowed_fd fd) { + int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); + if (flags == -1) { + return ErrnoError() << "Could not get flags for fd"; + } + if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { + return ErrnoError() << "Could not set non-blocking flag for fd"; + } + return {}; +} + +status_t getRandomBytes(uint8_t* data, size_t size) { + int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (ret == -1) { + return -errno; + } + + base::unique_fd fd(ret); + if (!base::ReadFully(fd, data, size)) { + return -errno; + } + return OK; +} + +status_t dupFileDescriptor(int oldFd, int* newFd) { + int ret = fcntl(oldFd, F_DUPFD_CLOEXEC, 0); + if (ret < 0) { + return -errno; + } + + *newFd = ret; + return OK; +} + +std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() { + return RpcTransportCtxFactoryRaw::make(); +} + +ssize_t sendMessageOnSocket( + const RpcTransportFd& socket, iovec* iovs, int niovs, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + if (ancillaryFds != nullptr && !ancillaryFds->empty()) { + if (ancillaryFds->size() > kMaxFdsPerMsg) { + errno = EINVAL; + return -1; + } + + // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then + // use memcpy. + int fds[kMaxFdsPerMsg]; + for (size_t i = 0; i < ancillaryFds->size(); i++) { + fds[i] = std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i)); + } + const size_t fdsByteSize = sizeof(int) * ancillaryFds->size(); + + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(fdsByteSize); + memcpy(CMSG_DATA(cmsg), fds, fdsByteSize); + + msg.msg_controllen = CMSG_SPACE(fdsByteSize); + return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC)); + } + + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL)); +} + +ssize_t receiveMessageFromSocket( + const RpcTransportFd& socket, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + if (ancillaryFds != nullptr) { + int fdBuffer[kMaxFdsPerMsg]; + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + ssize_t processSize = TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL)); + if (processSize < 0) { + return -1; + } + + for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks + // application devs to memcpy the data to ensure memory alignment. + size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0); + LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // validity check + memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen); + size_t fdCount = dataLen / sizeof(int); + ancillaryFds->reserve(ancillaryFds->size() + fdCount); + for (size_t i = 0; i < fdCount; i++) { + ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i])); + } + break; + } + } + + if (msg.msg_flags & MSG_CTRUNC) { + errno = EPIPE; + return -1; + } + return processSize; + } + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + + return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL)); +} + +} // namespace android diff --git a/libs/binder/OS.h b/libs/binder/OS.h new file mode 100644 index 0000000000..fecae31763 --- /dev/null +++ b/libs/binder/OS.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stddef.h> +#include <cstdint> + +#include <android-base/result.h> +#include <android-base/unique_fd.h> +#include <binder/RpcTransport.h> +#include <utils/Errors.h> + +namespace android { + +android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); + +status_t getRandomBytes(uint8_t* data, size_t size); + +status_t dupFileDescriptor(int oldFd, int* newFd); + +std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory(); + +ssize_t sendMessageOnSocket( + const RpcTransportFd& socket, iovec* iovs, int niovs, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds); + +ssize_t receiveMessageFromSocket( + const RpcTransportFd& socket, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds); + +} // namespace android diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS index f954e74eba..bb17683a23 100644 --- a/libs/binder/OWNERS +++ b/libs/binder/OWNERS @@ -1,6 +1,4 @@ # Bug component: 32456 -ctate@google.com -hackbod@google.com maco@google.com smoreland@google.com tkjos@google.com diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 58b0b35323..0aca163eab 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -20,15 +20,14 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> -#include <linux/sched.h> #include <pthread.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> +#include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> -#include <sys/resource.h> #include <unistd.h> #include <binder/Binder.h> @@ -40,6 +39,7 @@ #include <binder/Status.h> #include <binder/TextOutput.h> +#include <android-base/scopeguard.h> #include <cutils/ashmem.h> #include <cutils/compiler.h> #include <utils/Flattenable.h> @@ -48,15 +48,31 @@ #include <utils/String8.h> #include <utils/misc.h> +#include "OS.h" #include "RpcState.h" #include "Static.h" #include "Utils.h" + +// A lot of code in this file uses definitions from the +// Linux kernel header for Binder <linux/android/binder.h> +// which is included indirectly via "binder_module.h". +// Non-Linux OSes do not have that header, so libbinder should be +// built for those targets without kernel binder support, i.e., +// without BINDER_WITH_KERNEL_IPC. For this reason, all code in this +// file that depends on kernel binder, including the header itself, +// is conditional on BINDER_WITH_KERNEL_IPC. +#ifdef BINDER_WITH_KERNEL_IPC +#include <linux/sched.h> #include "binder_module.h" +#else // BINDER_WITH_KERNEL_IPC +// Needed by {read,write}Pointer +typedef uintptr_t binder_uintptr_t; +#endif // BINDER_WITH_KERNEL_IPC #define LOG_REFS(...) -//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOG_ALLOC(...) -//#define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +// #define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) // --------------------------------------------------------------------------- @@ -87,7 +103,8 @@ static_assert(sizeof(Parcel) == 60); static std::atomic<size_t> gParcelGlobalAllocCount; static std::atomic<size_t> gParcelGlobalAllocSize; -static size_t gMaxFds = 0; +// Maximum number of file descriptors per Parcel. +constexpr size_t kMaxFds = 1024; // Maximum size of a blob to transfer in-place. static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; @@ -98,6 +115,7 @@ enum { BLOB_ASHMEM_MUTABLE = 2, }; +#ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { switch (obj.hdr.type) { @@ -150,6 +168,15 @@ static void release_object(const sp<ProcessState>& proc, const flat_binder_objec ALOGE("Invalid object type 0x%08x", obj.hdr.type); } +#endif // BINDER_WITH_KERNEL_IPC + +static int toRawFd(const std::variant<base::unique_fd, base::borrowed_fd>& v) { + return std::visit([](const auto& fd) { return fd.get(); }, v); +} + +Parcel::RpcFields::RpcFields(const sp<RpcSession>& session) : mSession(session) { + LOG_ALWAYS_FATAL_IF(mSession == nullptr); +} status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder) { @@ -173,22 +200,25 @@ status_t Parcel::finishUnflattenBinder( return OK; } +#ifdef BINDER_WITH_KERNEL_IPC static constexpr inline int schedPolicyMask(int policy, int priority) { return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT); } +#endif // BINDER_WITH_KERNEL_IPC status_t Parcel::flattenBinder(const sp<IBinder>& binder) { BBinder* local = nullptr; if (binder) local = binder->localBinder(); if (local) local->setParceled(); - if (isForRpc()) { + if (const auto* rpcFields = maybeRpcFields()) { if (binder) { status_t status = writeInt32(1); // non-null if (status != OK) return status; uint64_t address; // TODO(b/167966510): need to undo this if the Parcel is not sent - status = mSession->state()->onBinderLeaving(mSession, binder, &address); + status = rpcFields->mSession->state()->onBinderLeaving(rpcFields->mSession, binder, + &address); if (status != OK) return status; status = writeUint64(address); if (status != OK) return status; @@ -199,6 +229,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { return finishFlattenBinder(binder); } +#ifdef BINDER_WITH_KERNEL_IPC flat_binder_object obj; int schedBits = 0; @@ -255,13 +286,15 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { if (status != OK) return status; return finishFlattenBinder(binder); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::unflattenBinder(sp<IBinder>* out) const { - if (isForRpc()) { - LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel"); - + if (const auto* rpcFields = maybeRpcFields()) { int32_t isPresent; status_t status = readInt32(&isPresent); if (status != OK) return status; @@ -271,10 +304,14 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const if (isPresent & 1) { uint64_t addr; if (status_t status = readUint64(&addr); status != OK) return status; - if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder); + if (status_t status = + rpcFields->mSession->state()->onBinderEntering(rpcFields->mSession, addr, + &binder); status != OK) return status; - if (status_t status = mSession->state()->flushExcessBinderRefs(mSession, addr, binder); + if (status_t status = + rpcFields->mSession->state()->flushExcessBinderRefs(rpcFields->mSession, + addr, binder); status != OK) return status; } @@ -282,6 +319,7 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const return finishUnflattenBinder(binder, out); } +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* flat = readObject(false); if (flat) { @@ -299,6 +337,10 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const } } return BAD_TYPE; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } // --------------------------------------------------------------------------- @@ -333,6 +375,10 @@ size_t Parcel::dataSize() const return (mDataSize > mDataPos ? mDataSize : mDataPos); } +size_t Parcel::dataBufferSize() const { + return mDataSize; +} + size_t Parcel::dataAvail() const { size_t result = dataSize() - dataPosition(); @@ -378,8 +424,10 @@ void Parcel::setDataPosition(size_t pos) const } mDataPos = pos; - mNextObjectHint = 0; - mObjectsSorted = false; + if (const auto* kernelFields = maybeKernelFields()) { + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; + } } status_t Parcel::setDataCapacity(size_t size) @@ -406,25 +454,27 @@ status_t Parcel::setData(const uint8_t* buffer, size_t len) if (err == NO_ERROR) { memcpy(const_cast<uint8_t*>(data()), buffer, len); mDataSize = len; - mFdsKnown = false; + if (auto* kernelFields = maybeKernelFields()) { + kernelFields->mFdsKnown = false; + } } return err; } -status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) -{ - if (mSession != parcel->mSession) { +status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { + if (isForRpc() != parcel->isForRpc()) { ALOGE("Cannot append Parcel from one context to another. They may be different formats, " "and objects are specific to a context."); return BAD_TYPE; } + if (isForRpc() && maybeRpcFields()->mSession != parcel->maybeRpcFields()->mSession) { + ALOGE("Cannot append Parcels from different sessions"); + return BAD_TYPE; + } status_t err; - const uint8_t *data = parcel->mData; - const binder_size_t *objects = parcel->mObjects; - size_t size = parcel->mObjectsSize; + const uint8_t* data = parcel->mData; int startPos = mDataPos; - int firstIndex = -1, lastIndex = -2; if (len == 0) { return NO_ERROR; @@ -443,18 +493,6 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) return BAD_VALUE; } - // Count objects in range - for (int i = 0; i < (int) size; i++) { - size_t off = objects[i]; - if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) { - if (firstIndex == -1) { - firstIndex = i; - } - lastIndex = i; - } - } - int numObjects = lastIndex - firstIndex + 1; - if ((mDataSize+len) > mDataCapacity) { // grow data err = growData(len); @@ -470,43 +508,128 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) err = NO_ERROR; - if (numObjects > 0) { - const sp<ProcessState> proc(ProcessState::self()); - // grow objects - if (mObjectsCapacity < mObjectsSize + numObjects) { - if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow - if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow - size_t newSize = ((mObjectsSize + numObjects)*3)/2; - if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow - binder_size_t *objects = - (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); - if (objects == (binder_size_t*)nullptr) { - return NO_MEMORY; + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + auto* otherKernelFields = parcel->maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(otherKernelFields == nullptr); + + const binder_size_t* objects = otherKernelFields->mObjects; + size_t size = otherKernelFields->mObjectsSize; + // Count objects in range + int firstIndex = -1, lastIndex = -2; + for (int i = 0; i < (int)size; i++) { + size_t off = objects[i]; + if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) { + if (firstIndex == -1) { + firstIndex = i; + } + lastIndex = i; } - mObjects = objects; - mObjectsCapacity = newSize; } + int numObjects = lastIndex - firstIndex + 1; + if (numObjects > 0) { + const sp<ProcessState> proc(ProcessState::self()); + // grow objects + if (kernelFields->mObjectsCapacity < kernelFields->mObjectsSize + numObjects) { + if ((size_t)numObjects > SIZE_MAX - kernelFields->mObjectsSize) + return NO_MEMORY; // overflow + if (kernelFields->mObjectsSize + numObjects > SIZE_MAX / 3) + return NO_MEMORY; // overflow + size_t newSize = ((kernelFields->mObjectsSize + numObjects) * 3) / 2; + if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow + binder_size_t* objects = (binder_size_t*)realloc(kernelFields->mObjects, + newSize * sizeof(binder_size_t)); + if (objects == (binder_size_t*)nullptr) { + return NO_MEMORY; + } + kernelFields->mObjects = objects; + kernelFields->mObjectsCapacity = newSize; + } - // append and acquire objects - int idx = mObjectsSize; - for (int i = firstIndex; i <= lastIndex; i++) { - size_t off = objects[i] - offset + startPos; - mObjects[idx++] = off; - mObjectsSize++; + // append and acquire objects + int idx = kernelFields->mObjectsSize; + for (int i = firstIndex; i <= lastIndex; i++) { + size_t off = objects[i] - offset + startPos; + kernelFields->mObjects[idx++] = off; + kernelFields->mObjectsSize++; - flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(mData + off); - acquire_object(proc, *flat, this); + flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off); + acquire_object(proc, *flat, this); + + if (flat->hdr.type == BINDER_TYPE_FD) { + // If this is a file descriptor, we need to dup it so the + // new Parcel now owns its own fd, and can declare that we + // officially know we have fds. + flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); + flat->cookie = 1; + kernelFields->mHasFds = kernelFields->mFdsKnown = true; + if (!mAllowFds) { + err = FDS_NOT_ALLOWED; + } + } + } + } +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC + } else { + auto* rpcFields = maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + auto* otherRpcFields = parcel->maybeRpcFields(); + if (otherRpcFields == nullptr) { + return BAD_TYPE; + } + if (rpcFields->mSession != otherRpcFields->mSession) { + return BAD_TYPE; + } + + const size_t savedDataPos = mDataPos; + base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; }; + + rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size()); + if (otherRpcFields->mFds != nullptr) { + if (rpcFields->mFds == nullptr) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + } + rpcFields->mFds->reserve(otherRpcFields->mFds->size()); + } + for (size_t i = 0; i < otherRpcFields->mObjectPositions.size(); i++) { + const binder_size_t objPos = otherRpcFields->mObjectPositions[i]; + if (offset <= objPos && objPos < offset + len) { + size_t newDataPos = objPos - offset + startPos; + rpcFields->mObjectPositions.push_back(newDataPos); + + mDataPos = newDataPos; + int32_t objectType; + if (status_t status = readInt32(&objectType); status != OK) { + return status; + } + if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + continue; + } - if (flat->hdr.type == BINDER_TYPE_FD) { - // If this is a file descriptor, we need to dup it so the - // new Parcel now owns its own fd, and can declare that we - // officially know we have fds. - flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); - flat->cookie = 1; - mHasFds = mFdsKnown = true; if (!mAllowFds) { - err = FDS_NOT_ALLOWED; + return FDS_NOT_ALLOWED; + } + + // Read FD, duplicate, and add to list. + int32_t fdIndex; + if (status_t status = readInt32(&fdIndex); status != OK) { + return status; + } + int oldFd = toRawFd(otherRpcFields->mFds->at(fdIndex)); + // 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) { + ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status)); + } + rpcFields->mFds->emplace_back(base::unique_fd(newFd)); + // Fixup the index in the data. + mDataPos = newDataPos + 4; + if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) { + return status; } } } @@ -563,18 +686,28 @@ void Parcel::restoreAllowFds(bool lastValue) bool Parcel::hasFileDescriptors() const { - if (!mFdsKnown) { + if (const auto* rpcFields = maybeRpcFields()) { + return rpcFields->mFds != nullptr && !rpcFields->mFds->empty(); + } + auto* kernelFields = maybeKernelFields(); + if (!kernelFields->mFdsKnown) { scanForFds(); } - return mHasFds; + return kernelFields->mHasFds; } std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { std::vector<sp<IBinder>> ret; +#ifdef BINDER_WITH_KERNEL_IPC + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return ret; + } + size_t initPosition = dataPosition(); - for (size_t i = 0; i < mObjectsSize; i++) { - binder_size_t offset = mObjects[i]; + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + offset); if (flat->hdr.type != BINDER_TYPE_BINDER) continue; @@ -586,27 +719,39 @@ std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { } setDataPosition(initPosition); +#endif // BINDER_WITH_KERNEL_IPC + return ret; } std::vector<int> Parcel::debugReadAllFileDescriptors() const { std::vector<int> ret; - size_t initPosition = dataPosition(); - for (size_t i = 0; i < mObjectsSize; i++) { - binder_size_t offset = mObjects[i]; - const flat_binder_object* flat = - reinterpret_cast<const flat_binder_object*>(mData + offset); - if (flat->hdr.type != BINDER_TYPE_FD) continue; + if (const auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + size_t initPosition = dataPosition(); + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_FD) continue; - setDataPosition(offset); + setDataPosition(offset); - int fd = readFileDescriptor(); - LOG_ALWAYS_FATAL_IF(fd == -1); - ret.push_back(fd); + int fd = readFileDescriptor(); + LOG_ALWAYS_FATAL_IF(fd == -1); + ret.push_back(fd); + } + setDataPosition(initPosition); +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); +#endif + } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) { + for (const auto& fd : *rpcFields->mFds) { + ret.push_back(toRawFd(fd)); + } } - setDataPosition(initPosition); return ret; } @@ -621,17 +766,38 @@ status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* resu return BAD_VALUE; } *result = false; - for (size_t i = 0; i < mObjectsSize; i++) { - size_t pos = mObjects[i]; - if (pos < offset) continue; - if (pos + sizeof(flat_binder_object) > offset + len) { - if (mObjectsSorted) break; - else continue; - } - const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos); - if (flat->hdr.type == BINDER_TYPE_FD) { - *result = true; - break; + if (const auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + size_t pos = kernelFields->mObjects[i]; + if (pos < offset) continue; + if (pos + sizeof(flat_binder_object) > offset + len) { + if (kernelFields->mObjectsSorted) { + break; + } else { + continue; + } + } + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + pos); + if (flat->hdr.type == BINDER_TYPE_FD) { + *result = true; + break; + } + } +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC + } else if (const auto* rpcFields = maybeRpcFields()) { + for (uint32_t pos : rpcFields->mObjectPositions) { + if (offset <= pos && pos < limit) { + const auto* type = reinterpret_cast<const RpcFields::ObjectType*>(mData + pos); + if (*type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + *result = true; + break; + } + } } } return NO_ERROR; @@ -654,23 +820,28 @@ void Parcel::markForRpc(const sp<RpcSession>& session) { LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr, "format must be set before data is written OR on IPC data"); - LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session"); - mSession = session; + mVariantFields.emplace<RpcFields>(session); } bool Parcel::isForRpc() const { - return mSession != nullptr; + return std::holds_alternative<RpcFields>(mVariantFields); } void Parcel::updateWorkSourceRequestHeaderPosition() const { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + // Only update the request headers once. We only want to point // to the first headers read/written. - if (!mRequestHeaderPresent) { - mWorkSourceRequestHeaderPosition = dataPosition(); - mRequestHeaderPresent = true; + if (!kernelFields->mRequestHeaderPresent) { + kernelFields->mWorkSourceRequestHeaderPosition = dataPosition(); + kernelFields->mRequestHeaderPresent = true; } } +#ifdef BINDER_WITH_KERNEL_IPC #if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); #elif defined(__ANDROID_RECOVERY__) @@ -678,6 +849,7 @@ constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); #endif +#endif // BINDER_WITH_KERNEL_IPC // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) @@ -686,13 +858,18 @@ status_t Parcel::writeInterfaceToken(const String16& interface) } status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { - if (CC_LIKELY(!isForRpc())) { + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC const IPCThreadState* threadState = IPCThreadState::self(); writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); updateWorkSourceRequestHeaderPosition(); writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); writeInt32(kHeader); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } // currently the interface identification token is just its name as a string @@ -701,12 +878,16 @@ status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { bool Parcel::replaceCallingWorkSourceUid(uid_t uid) { - if (!mRequestHeaderPresent) { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return false; + } + if (!kernelFields->mRequestHeaderPresent) { return false; } const size_t initialPosition = dataPosition(); - setDataPosition(mWorkSourceRequestHeaderPosition); + setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition); status_t err = writeInt32(uid); setDataPosition(initialPosition); return err == NO_ERROR; @@ -714,12 +895,16 @@ bool Parcel::replaceCallingWorkSourceUid(uid_t uid) uid_t Parcel::readCallingWorkSourceUid() const { - if (!mRequestHeaderPresent) { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return false; + } + if (!kernelFields->mRequestHeaderPresent) { return IPCThreadState::kUnsetWorkSource; } const size_t initialPosition = dataPosition(); - setDataPosition(mWorkSourceRequestHeaderPosition); + setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition); uid_t uid = readInt32(); setDataPosition(initialPosition); return uid; @@ -740,7 +925,8 @@ bool Parcel::enforceInterface(const char16_t* interface, size_t len, IPCThreadState* threadState) const { - if (CC_LIKELY(!isForRpc())) { + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { @@ -766,6 +952,11 @@ bool Parcel::enforceInterface(const char16_t* interface, header); return false; } +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)threadState; + return false; +#endif // BINDER_WITH_KERNEL_IPC } // Interface descriptor. @@ -782,7 +973,15 @@ bool Parcel::enforceInterface(const char16_t* interface, } } +void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) { + mEnforceNoDataAvail = enforceNoDataAvail; +} + binder::Status Parcel::enforceNoDataAvail() const { + if (!mEnforceNoDataAvail) { + return binder::Status::ok(); + } + const auto n = dataAvail(); if (n == 0) { return binder::Status::ok(); @@ -795,7 +994,10 @@ binder::Status Parcel::enforceNoDataAvail() const { size_t Parcel::objectsCount() const { - return mObjectsSize; + if (const auto* kernelFields = maybeKernelFields()) { + return kernelFields->mObjectsSize; + } + return 0; } status_t Parcel::errorCheck() const @@ -1237,27 +1439,64 @@ status_t Parcel::writeNativeHandle(const native_handle* handle) return err; } -status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) -{ - if (isForRpc()) { - ALOGE("Cannot write file descriptor to remote binder."); - return BAD_TYPE; +status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { + if (auto* rpcFields = maybeRpcFields()) { + std::variant<base::unique_fd, base::borrowed_fd> fdVariant; + if (takeOwnership) { + fdVariant = base::unique_fd(fd); + } else { + fdVariant = base::borrowed_fd(fd); + } + if (!mAllowFds) { + return FDS_NOT_ALLOWED; + } + switch (rpcFields->mSession->getFileDescriptorTransportMode()) { + case RpcSession::FileDescriptorTransportMode::NONE: { + return FDS_NOT_ALLOWED; + } + case RpcSession::FileDescriptorTransportMode::UNIX: + case RpcSession::FileDescriptorTransportMode::TRUSTY: { + if (rpcFields->mFds == nullptr) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + } + size_t dataPos = mDataPos; + if (dataPos > UINT32_MAX) { + return NO_MEMORY; + } + if (status_t err = writeInt32(RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR); err != OK) { + return err; + } + if (status_t err = writeInt32(rpcFields->mFds->size()); err != OK) { + return err; + } + rpcFields->mObjectPositions.push_back(dataPos); + rpcFields->mFds->push_back(std::move(fdVariant)); + return OK; + } + } } +#ifdef BINDER_WITH_KERNEL_IPC flat_binder_object obj; obj.hdr.type = BINDER_TYPE_FD; - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + obj.flags = 0; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = fd; obj.cookie = takeOwnership ? 1 : 0; return writeObject(obj, true); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)fd; + (void)takeOwnership; + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::writeDupFileDescriptor(int fd) { - int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); - if (dupFd < 0) { - return -errno; + int dupFd; + if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) { + return err; } status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/); if (err != OK) { @@ -1274,9 +1513,9 @@ status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership) status_t Parcel::writeDupParcelFileDescriptor(int fd) { - int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); - if (dupFd < 0) { - return -errno; + int dupFd; + if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) { + return err; } status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/); if (err != OK) { @@ -1361,7 +1600,7 @@ status_t Parcel::write(const FlattenableHelperInterface& val) const size_t len = val.getFlattenedSize(); const size_t fd_count = val.getFdCount(); - if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { + if ((len > INT32_MAX) || (fd_count > kMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; @@ -1401,8 +1640,12 @@ status_t Parcel::write(const FlattenableHelperInterface& val) status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) { + auto* kernelFields = maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(kernelFields == nullptr, "Can't write flat_binder_object to RPC Parcel"); + +#ifdef BINDER_WITH_KERNEL_IPC const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; - const bool enoughObjects = mObjectsSize < mObjectsCapacity; + const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity; if (enoughData && enoughObjects) { restart_write: *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; @@ -1413,14 +1656,14 @@ restart_write: // fail before modifying our object index return FDS_NOT_ALLOWED; } - mHasFds = mFdsKnown = true; + kernelFields->mHasFds = kernelFields->mFdsKnown = true; } // Need to write meta-data? if (nullMetaData || val.binder != 0) { - mObjects[mObjectsSize] = mDataPos; + kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this); - mObjectsSize++; + kernelFields->mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); @@ -1431,17 +1674,24 @@ restart_write: if (err != NO_ERROR) return err; } if (!enoughObjects) { - if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow - if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow - size_t newSize = ((mObjectsSize+2)*3)/2; + if (kernelFields->mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow + if ((kernelFields->mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow + size_t newSize = ((kernelFields->mObjectsSize + 2) * 3) / 2; if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow - binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); + binder_size_t* objects = + (binder_size_t*)realloc(kernelFields->mObjects, newSize * sizeof(binder_size_t)); if (objects == nullptr) return NO_MEMORY; - mObjects = objects; - mObjectsCapacity = newSize; + kernelFields->mObjects = objects; + kernelFields->mObjectsCapacity = newSize; } goto restart_write; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)val; + (void)nullMetaData; + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::writeNoException() @@ -1452,55 +1702,70 @@ status_t Parcel::writeNoException() status_t Parcel::validateReadData(size_t upperBound) const { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + // Can't validate RPC Parcel reads because the location of binder + // objects is unknown. + return OK; + } + +#ifdef BINDER_WITH_KERNEL_IPC // Don't allow non-object reads on object data - if (mObjectsSorted || mObjectsSize <= 1) { -data_sorted: + if (kernelFields->mObjectsSorted || kernelFields->mObjectsSize <= 1) { + data_sorted: // Expect to check only against the next object - if (mNextObjectHint < mObjectsSize && upperBound > mObjects[mNextObjectHint]) { + if (kernelFields->mNextObjectHint < kernelFields->mObjectsSize && + upperBound > kernelFields->mObjects[kernelFields->mNextObjectHint]) { // For some reason the current read position is greater than the next object // hint. Iterate until we find the right object - size_t nextObject = mNextObjectHint; + size_t nextObject = kernelFields->mNextObjectHint; do { - if (mDataPos < mObjects[nextObject] + sizeof(flat_binder_object)) { + if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) { // Requested info overlaps with an object ALOGE("Attempt to read from protected data in Parcel %p", this); return PERMISSION_DENIED; } nextObject++; - } while (nextObject < mObjectsSize && upperBound > mObjects[nextObject]); - mNextObjectHint = nextObject; + } while (nextObject < kernelFields->mObjectsSize && + upperBound > kernelFields->mObjects[nextObject]); + kernelFields->mNextObjectHint = nextObject; } return NO_ERROR; } // Quickly determine if mObjects is sorted. - binder_size_t* currObj = mObjects + mObjectsSize - 1; + binder_size_t* currObj = kernelFields->mObjects + kernelFields->mObjectsSize - 1; binder_size_t* prevObj = currObj; - while (currObj > mObjects) { + while (currObj > kernelFields->mObjects) { prevObj--; if(*prevObj > *currObj) { goto data_unsorted; } currObj--; } - mObjectsSorted = true; + kernelFields->mObjectsSorted = true; goto data_sorted; data_unsorted: // Insertion Sort mObjects // Great for mostly sorted lists. If randomly sorted or reverse ordered mObjects become common, // switch to std::sort(mObjects, mObjects + mObjectsSize); - for (binder_size_t* iter0 = mObjects + 1; iter0 < mObjects + mObjectsSize; iter0++) { + for (binder_size_t* iter0 = kernelFields->mObjects + 1; + iter0 < kernelFields->mObjects + kernelFields->mObjectsSize; iter0++) { binder_size_t temp = *iter0; binder_size_t* iter1 = iter0 - 1; - while (iter1 >= mObjects && *iter1 > temp) { + while (iter1 >= kernelFields->mObjects && *iter1 > temp) { *(iter1 + 1) = *iter1; iter1--; } *(iter1 + 1) = temp; } - mNextObjectHint = 0; - mObjectsSorted = true; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = true; goto data_sorted; +#else // BINDER_WITH_KERNEL_IPC + (void)upperBound; + return NO_ERROR; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::read(void* outData, size_t len) const @@ -1513,7 +1778,8 @@ status_t Parcel::read(void* outData, size_t len) const if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { - if (mObjectsSize > 0) { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) { status_t err = validateReadData(mDataPos + pad_size(len)); if(err != NO_ERROR) { // Still increment the data position by the expected length @@ -1540,7 +1806,8 @@ const void* Parcel::readInplace(size_t len) const if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { - if (mObjectsSize > 0) { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) { status_t err = validateReadData(mDataPos + pad_size(len)); if(err != NO_ERROR) { // Still increment the data position by the expected length @@ -1587,7 +1854,8 @@ status_t Parcel::readAligned(T *pArg) const { static_assert(std::is_trivially_copyable_v<T>); if ((mDataPos+sizeof(T)) <= mDataSize) { - if (mObjectsSize > 0) { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) { status_t err = validateReadData(mDataPos + sizeof(T)); if(err != NO_ERROR) { // Still increment the data position by the expected length @@ -1965,8 +2233,32 @@ native_handle* Parcel::readNativeHandle() const return h; } -int Parcel::readFileDescriptor() const -{ +int Parcel::readFileDescriptor() const { + if (const auto* rpcFields = maybeRpcFields()) { + if (!std::binary_search(rpcFields->mObjectPositions.begin(), + rpcFields->mObjectPositions.end(), mDataPos)) { + ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the " + "object list", + this, mDataPos); + return BAD_TYPE; + } + + int32_t objectType = readInt32(); + if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + return BAD_TYPE; + } + + int32_t fdIndex = readInt32(); + if (rpcFields->mFds == nullptr || fdIndex < 0 || + static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) { + ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu", + fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0); + return BAD_VALUE; + } + return toRawFd(rpcFields->mFds->at(fdIndex)); + } + +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* flat = readObject(true); if (flat && flat->hdr.type == BINDER_TYPE_FD) { @@ -1974,10 +2266,13 @@ int Parcel::readFileDescriptor() const } return BAD_TYPE; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } -int Parcel::readParcelFileDescriptor() const -{ +int Parcel::readParcelFileDescriptor() const { int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { @@ -2017,7 +2312,12 @@ status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const return BAD_TYPE; } - val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); + int dupFd; + if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) { + return BAD_VALUE; + } + + val->reset(dupFd); if (val->get() < 0) { return BAD_VALUE; @@ -2034,7 +2334,12 @@ status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const return BAD_TYPE; } - val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); + int dupFd; + if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) { + return BAD_VALUE; + } + + val->reset(dupFd); if (val->get() < 0) { return BAD_VALUE; @@ -2086,7 +2391,7 @@ status_t Parcel::read(FlattenableHelperInterface& val) const const size_t len = this->readInt32(); const size_t fd_count = this->readInt32(); - if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { + if ((len > INT32_MAX) || (fd_count > kMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; @@ -2130,8 +2435,15 @@ status_t Parcel::read(FlattenableHelperInterface& val) const return err; } + +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* Parcel::readObject(bool nullMetaData) const { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return nullptr; + } + const size_t DPOS = mDataPos; if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { const flat_binder_object* obj @@ -2146,9 +2458,9 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const } // Ensure that this object is valid... - binder_size_t* const OBJS = mObjects; - const size_t N = mObjectsSize; - size_t opos = mNextObjectHint; + binder_size_t* const OBJS = kernelFields->mObjects; + const size_t N = kernelFields->mObjectsSize; + size_t opos = kernelFields->mNextObjectHint; if (N > 0) { ALOGV("Parcel %p looking for obj at %zu, hint=%zu", @@ -2167,7 +2479,7 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const // Found it! ALOGV("Parcel %p found obj %zu at index %zu with forward search", this, DPOS, opos); - mNextObjectHint = opos+1; + kernelFields->mNextObjectHint = opos + 1; ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } @@ -2180,7 +2492,7 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const // Found it! ALOGV("Parcel %p found obj %zu at index %zu with backward search", this, DPOS, opos); - mNextObjectHint = opos+1; + kernelFields->mNextObjectHint = opos + 1; ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } @@ -2190,21 +2502,29 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const } return nullptr; } +#endif // BINDER_WITH_KERNEL_IPC -void Parcel::closeFileDescriptors() -{ - size_t i = mObjectsSize; - if (i > 0) { - //ALOGI("Closing file descriptors for %zu objects...", i); - } - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); - if (flat->hdr.type == BINDER_TYPE_FD) { - //ALOGI("Closing fd: %ld", flat->handle); - close(flat->handle); +void Parcel::closeFileDescriptors() { + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; + if (i > 0) { + // ALOGI("Closing file descriptors for %zu objects...", i); } + while (i > 0) { + i--; + const flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); + if (flat->hdr.type == BINDER_TYPE_FD) { + // ALOGI("Closing fd: %ld", flat->handle); + close(flat->handle); + } + } +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); +#endif // BINDER_WITH_KERNEL_IPC + } else if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mFds.reset(); } } @@ -2220,35 +2540,44 @@ size_t Parcel::ipcDataSize() const uintptr_t Parcel::ipcObjects() const { - return reinterpret_cast<uintptr_t>(mObjects); + if (const auto* kernelFields = maybeKernelFields()) { + return reinterpret_cast<uintptr_t>(kernelFields->mObjects); + } + return 0; } size_t Parcel::ipcObjectsCount() const { - return mObjectsSize; + if (const auto* kernelFields = maybeKernelFields()) { + return kernelFields->mObjectsSize; + } + return 0; } -void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount, release_func relFunc) -{ +void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount, release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function"); freeData(); + auto* kernelFields = maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData. + mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; - mObjects = const_cast<binder_size_t*>(objects); - mObjectsSize = mObjectsCapacity = objectsCount; + kernelFields->mObjects = const_cast<binder_size_t*>(objects); + kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsCount; mOwner = relFunc; +#ifdef BINDER_WITH_KERNEL_IPC binder_size_t minOffset = 0; - for (size_t i = 0; i < mObjectsSize; i++) { - binder_size_t offset = mObjects[i]; + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; if (offset < minOffset) { ALOGE("%s: bad object offset %" PRIu64 " < %" PRIu64 "\n", __func__, (uint64_t)offset, (uint64_t)minOffset); - mObjectsSize = 0; + kernelFields->mObjectsSize = 0; break; } const flat_binder_object* flat @@ -2266,16 +2595,67 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, // WARNING: callers of ipcSetDataReference need to make sure they // don't rely on mObjectsSize in their release_func. - mObjectsSize = 0; + kernelFields->mObjectsSize = 0; break; } minOffset = offset + sizeof(flat_binder_object); } scanForFds(); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL_IF(objectsCount != 0, + "Non-zero objects count passed to Parcel with kernel driver disabled"); +#endif // BINDER_WITH_KERNEL_IPC } -void Parcel::print(TextOutput& to, uint32_t /*flags*/) const -{ +status_t Parcel::rpcSetDataReference( + const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, + const uint32_t* objectTable, size_t objectTableSize, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds, + release_func relFunc) { + // this code uses 'mOwner == nullptr' to understand whether it owns memory + LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function"); + + LOG_ALWAYS_FATAL_IF(session == nullptr); + + if (objectTableSize != ancillaryFds.size()) { + ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size()); + relFunc(data, dataSize, nullptr, 0); + return BAD_VALUE; + } + for (size_t i = 0; i < objectTableSize; i++) { + uint32_t minObjectEnd; + if (__builtin_add_overflow(objectTable[i], sizeof(RpcFields::ObjectType), &minObjectEnd) || + minObjectEnd >= dataSize) { + ALOGE("received out of range object position: %" PRIu32 " (parcel size is %zu)", + objectTable[i], dataSize); + relFunc(data, dataSize, nullptr, 0); + return BAD_VALUE; + } + } + + freeData(); + markForRpc(session); + + auto* rpcFields = maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. + + mData = const_cast<uint8_t*>(data); + mDataSize = mDataCapacity = dataSize; + mOwner = relFunc; + + rpcFields->mObjectPositions.reserve(objectTableSize); + for (size_t i = 0; i < objectTableSize; i++) { + rpcFields->mObjectPositions.push_back(objectTable[i]); + } + if (!ancillaryFds.empty()) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + *rpcFields->mFds = std::move(ancillaryFds); + } + + return OK; +} + +void Parcel::print(std::ostream& to, uint32_t /*flags*/) const { to << "Parcel("; if (errorCheck() != NO_ERROR) { @@ -2283,16 +2663,19 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const to << "Error: " << (void*)(intptr_t)err << " \"" << strerror(-err) << "\""; } else if (dataSize() > 0) { const uint8_t* DATA = data(); - to << indent << HexDump(DATA, dataSize()) << dedent; - const binder_size_t* OBJS = mObjects; - const size_t N = objectsCount(); - for (size_t i=0; i<N; i++) { - const flat_binder_object* flat - = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]); - to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " - << TypeCode(flat->hdr.type & 0x7f7f7f00) - << " = " << flat->binder; + to << "\t" << HexDump(DATA, dataSize()); +#ifdef BINDER_WITH_KERNEL_IPC + if (const auto* kernelFields = maybeKernelFields()) { + const binder_size_t* OBJS = kernelFields->mObjects; + const size_t N = objectsCount(); + for (size_t i = 0; i < N; i++) { + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(DATA + OBJS[i]); + to << "Object #" << i << " @ " << (void*)OBJS[i] << ": " + << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder; + } } +#endif // BINDER_WITH_KERNEL_IPC } else { to << "NULL"; } @@ -2302,36 +2685,48 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const void Parcel::releaseObjects() { - size_t i = mObjectsSize; + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; if (i == 0) { return; } sp<ProcessState> proc(ProcessState::self()); uint8_t* const data = mData; - binder_size_t* const objects = mObjects; + binder_size_t* const objects = kernelFields->mObjects; while (i > 0) { i--; - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(data+objects[i]); + const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); release_object(proc, *flat, this); } +#endif // BINDER_WITH_KERNEL_IPC } void Parcel::acquireObjects() { - size_t i = mObjectsSize; + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; if (i == 0) { return; } const sp<ProcessState> proc(ProcessState::self()); uint8_t* const data = mData; - binder_size_t* const objects = mObjects; + binder_size_t* const objects = kernelFields->mObjects; while (i > 0) { i--; - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(data+objects[i]); + const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); acquire_object(proc, *flat, this); } +#endif // BINDER_WITH_KERNEL_IPC } void Parcel::freeData() @@ -2345,7 +2740,11 @@ void Parcel::freeDataNoInit() if (mOwner) { LOG_ALLOC("Parcel %p: freeing other owner data", this); //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize); + auto* kernelFields = maybeKernelFields(); + // Close FDs before freeing, otherwise they will leak for kernel binder. + closeFileDescriptors(); + mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsSize : 0); } else { LOG_ALLOC("Parcel %p: freeing allocated data", this); releaseObjects(); @@ -2358,7 +2757,8 @@ void Parcel::freeDataNoInit() } free(mData); } - if (mObjects) free(mObjects); + auto* kernelFields = maybeKernelFields(); + if (kernelFields && kernelFields->mObjects) free(kernelFields->mObjects); } } @@ -2433,13 +2833,18 @@ status_t Parcel::restartWrite(size_t desired) ALOGV("restartWrite Setting data size of %p to %zu", this, mDataSize); ALOGV("restartWrite Setting data pos of %p to %zu", this, mDataPos); - free(mObjects); - mObjects = nullptr; - mObjectsSize = mObjectsCapacity = 0; - mNextObjectHint = 0; - mObjectsSorted = false; - mHasFds = false; - mFdsKnown = true; + if (auto* kernelFields = maybeKernelFields()) { + free(kernelFields->mObjects); + kernelFields->mObjects = nullptr; + kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = 0; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; + kernelFields->mHasFds = false; + kernelFields->mFdsKnown = true; + } else if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mObjectPositions.clear(); + rpcFields->mFds.reset(); + } mAllowFds = true; return NO_ERROR; @@ -2453,17 +2858,27 @@ status_t Parcel::continueWrite(size_t desired) return BAD_VALUE; } + auto* kernelFields = maybeKernelFields(); + auto* rpcFields = maybeRpcFields(); + // If shrinking, first adjust for any objects that appear // after the new data size. - size_t objectsSize = mObjectsSize; + size_t objectsSize = + kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size(); if (desired < mDataSize) { if (desired == 0) { objectsSize = 0; } else { - while (objectsSize > 0) { - if (mObjects[objectsSize-1] < desired) - break; - objectsSize--; + if (kernelFields) { + while (objectsSize > 0) { + if (kernelFields->mObjects[objectsSize - 1] < desired) break; + objectsSize--; + } + } else { + while (objectsSize > 0) { + if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break; + objectsSize--; + } } } } @@ -2484,7 +2899,7 @@ status_t Parcel::continueWrite(size_t desired) } binder_size_t* objects = nullptr; - if (objectsSize) { + if (kernelFields && objectsSize) { objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)); if (!objects) { free(data); @@ -2495,20 +2910,32 @@ status_t Parcel::continueWrite(size_t desired) // Little hack to only acquire references on objects // we will be keeping. - size_t oldObjectsSize = mObjectsSize; - mObjectsSize = objectsSize; + size_t oldObjectsSize = kernelFields->mObjectsSize; + kernelFields->mObjectsSize = objectsSize; acquireObjects(); - mObjectsSize = oldObjectsSize; + kernelFields->mObjectsSize = oldObjectsSize; + } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + free(data); + return status; + } } if (mData) { memcpy(data, mData, mDataSize < desired ? mDataSize : desired); } - if (objects && mObjects) { - memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t)); + if (objects && kernelFields && kernelFields->mObjects) { + memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t)); } - //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize); + // ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); + if (kernelFields) { + // TODO(b/239222407): This seems wrong. We should only free FDs when + // they are in a truncated section of the parcel. + closeFileDescriptors(); + } + mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsSize : 0); mOwner = nullptr; LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired); @@ -2516,43 +2943,55 @@ status_t Parcel::continueWrite(size_t desired) gParcelGlobalAllocCount++; mData = data; - mObjects = objects; mDataSize = (mDataSize < desired) ? mDataSize : desired; ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize); mDataCapacity = desired; - mObjectsSize = mObjectsCapacity = objectsSize; - mNextObjectHint = 0; - mObjectsSorted = false; + if (kernelFields) { + kernelFields->mObjects = objects; + kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsSize; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; + } } else if (mData) { - if (objectsSize < mObjectsSize) { + if (kernelFields && objectsSize < kernelFields->mObjectsSize) { +#ifdef BINDER_WITH_KERNEL_IPC // Need to release refs on any objects we are dropping. const sp<ProcessState> proc(ProcessState::self()); - for (size_t i=objectsSize; i<mObjectsSize; i++) { - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); + for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) { + const flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { // will need to rescan because we may have lopped off the only FDs - mFdsKnown = false; + kernelFields->mFdsKnown = false; } release_object(proc, *flat, this); } if (objectsSize == 0) { - free(mObjects); - mObjects = nullptr; - mObjectsCapacity = 0; + free(kernelFields->mObjects); + kernelFields->mObjects = nullptr; + kernelFields->mObjectsCapacity = 0; } else { binder_size_t* objects = - (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t)); + (binder_size_t*)realloc(kernelFields->mObjects, + objectsSize * sizeof(binder_size_t)); if (objects) { - mObjects = objects; - mObjectsCapacity = objectsSize; + kernelFields->mObjects = objects; + kernelFields->mObjectsCapacity = objectsSize; } } - mObjectsSize = objectsSize; - mNextObjectHint = 0; - mObjectsSorted = false; + kernelFields->mObjectsSize = objectsSize; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel"); +#endif // BINDER_WITH_KERNEL_IPC + } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + return status; + } } // We own the data, so we can just do a realloc(). @@ -2588,9 +3027,12 @@ status_t Parcel::continueWrite(size_t desired) return NO_MEMORY; } - if(!(mDataCapacity == 0 && mObjects == nullptr - && mObjectsCapacity == 0)) { - ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired); + if (!(mDataCapacity == 0 && + (kernelFields == nullptr || + (kernelFields->mObjects == nullptr && kernelFields->mObjectsCapacity == 0)))) { + ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, + kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsCapacity : 0, desired); } LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired); @@ -2607,6 +3049,35 @@ status_t Parcel::continueWrite(size_t desired) return NO_ERROR; } +status_t Parcel::truncateRpcObjects(size_t newObjectsSize) { + auto* rpcFields = maybeRpcFields(); + if (newObjectsSize == 0) { + rpcFields->mObjectPositions.clear(); + if (rpcFields->mFds) { + rpcFields->mFds->clear(); + } + return OK; + } + while (rpcFields->mObjectPositions.size() > newObjectsSize) { + uint32_t pos = rpcFields->mObjectPositions.back(); + rpcFields->mObjectPositions.pop_back(); + const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos); + if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + const auto fdIndex = + *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType)); + if (rpcFields->mFds == nullptr || fdIndex < 0 || + static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) { + ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu", + fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0); + return BAD_VALUE; + } + // In practice, this always removes the last element. + rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex); + } + } + return OK; +} + void Parcel::initState() { LOG_ALLOC("Parcel %p: initState", this); @@ -2617,39 +3088,24 @@ void Parcel::initState() mDataPos = 0; ALOGV("initState Setting data size of %p to %zu", this, mDataSize); ALOGV("initState Setting data pos of %p to %zu", this, mDataPos); - mSession = nullptr; - mObjects = nullptr; - mObjectsSize = 0; - mObjectsCapacity = 0; - mNextObjectHint = 0; - mObjectsSorted = false; - mHasFds = false; - mFdsKnown = true; + mVariantFields.emplace<KernelFields>(); mAllowFds = true; mDeallocZero = false; mOwner = nullptr; - mWorkSourceRequestHeaderPosition = 0; - mRequestHeaderPresent = false; - - // racing multiple init leads only to multiple identical write - if (gMaxFds == 0) { - struct rlimit result; - if (!getrlimit(RLIMIT_NOFILE, &result)) { - gMaxFds = (size_t)result.rlim_cur; - //ALOGI("parcel fd limit set to %zu", gMaxFds); - } else { - ALOGW("Unable to getrlimit: %s", strerror(errno)); - gMaxFds = 1024; - } - } + mEnforceNoDataAvail = true; } void Parcel::scanForFds() const { - status_t status = hasFileDescriptorsInRange(0, dataSize(), &mHasFds); + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + status_t status = hasFileDescriptorsInRange(0, dataSize(), &kernelFields->mHasFds); ALOGE_IF(status != NO_ERROR, "Error %d calling hasFileDescriptorsInRange()", status); - mFdsKnown = true; + kernelFields->mFdsKnown = true; } +#ifdef BINDER_WITH_KERNEL_IPC size_t Parcel::getBlobAshmemSize() const { // This used to return the size of all blobs that were written to ashmem, now we're returning @@ -2660,10 +3116,15 @@ size_t Parcel::getBlobAshmemSize() const size_t Parcel::getOpenAshmemSize() const { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return 0; + } + size_t openAshmemSize = 0; - for (size_t i = 0; i < mObjectsSize; i++) { + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { const flat_binder_object* flat = - reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]); + reinterpret_cast<const flat_binder_object*>(mData + kernelFields->mObjects[i]); // cookie is compared against zero for historical reasons // > obj.cookie = takeOwnership ? 1 : 0; @@ -2677,6 +3138,7 @@ size_t Parcel::getOpenAshmemSize() const } return openAshmemSize; } +#endif // BINDER_WITH_KERNEL_IPC // --- Parcel::Blob --- diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 4a01d8176d..5f1f50672a 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -19,6 +19,7 @@ #include <binder/ProcessState.h> #include <android-base/result.h> +#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> @@ -35,14 +36,15 @@ #include <errno.h> #include <fcntl.h> -#include <mutex> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> +#include <unistd.h> +#include <mutex> #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) #define DEFAULT_MAX_BINDER_THREADS 15 @@ -98,8 +100,17 @@ static void verifyNotForked(bool forked) { LOG_ALWAYS_FATAL_IF(forked, "libbinder ProcessState can not be used after fork"); } +bool ProcessState::isVndservicemanagerEnabled() { + return access("/vendor/bin/vndservicemanager", R_OK) == 0; +} + sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) { +#ifdef BINDER_IPC_32BIT + LOG_ALWAYS_FATAL("32-bit binder IPC is not supported for new devices starting in Android P. If " + "you do need to use this mode, please see b/232423610 or file an issue with " + "AOSP upstream as otherwise this will be removed soon."); +#endif if (driver == nullptr) { std::lock_guard<std::mutex> l(gProcessMutex); @@ -116,6 +127,11 @@ sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) driver = "/dev/binder"; } + if (0 == strcmp(driver, "/dev/vndbinder") && !isVndservicemanagerEnabled()) { + ALOGE("vndservicemanager is not started on this device, you can save resources/threads " + "by not initializing ProcessState with /dev/vndbinder."); + } + // we must install these before instantiating the gProcess object, // otherwise this would race with creating it, and there could be the // possibility of an invalid gProcess object forked by another thread @@ -170,6 +186,10 @@ void ProcessState::childPostFork() { // the thread handler is installed if (gProcess) { gProcess->mForked = true; + + // "O_CLOFORK" + close(gProcess->mDriverFD); + gProcess->mDriverFD = -1; } gProcessMutex.unlock(); } @@ -182,7 +202,6 @@ void ProcessState::startThreadPool() ALOGW("Extra binder thread started, but 0 threads requested. Do not use " "*startThreadPool when zero threads are requested."); } - mThreadPoolStarted = true; spawnPooledThread(true); } @@ -290,12 +309,17 @@ ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) return &mHandleToObject.editItemAt(handle); } +// see b/166779391: cannot change the VNDK interface, so access like this +extern sp<BBinder> the_context_object; + sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) { sp<IBinder> result; AutoMutex _l(mLock); + if (handle == 0 && the_context_object != nullptr) return the_context_object; + handle_entry* e = lookupHandleLocked(handle); if (e != nullptr) { @@ -386,6 +410,9 @@ void ProcessState::spawnPooledThread(bool isMain) ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = sp<PoolThread>::make(isMain); t->run(name.string()); + pthread_mutex_lock(&mThreadCountLock); + mKernelStartedThreads++; + pthread_mutex_unlock(&mThreadCountLock); } } @@ -402,12 +429,27 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { return result; } -size_t ProcessState::getThreadPoolMaxThreadCount() const { +size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { + pthread_mutex_lock(&mThreadCountLock); + base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); }; + // may actually be one more than this, if join is called - if (mThreadPoolStarted) return mMaxThreads; + if (mThreadPoolStarted) { + return mCurrentThreads < mKernelStartedThreads + ? mMaxThreads + : mMaxThreads + mCurrentThreads - mKernelStartedThreads; + } // must not be initialized or maybe has poll thread setup, we // currently don't track this in libbinder - return 0; + LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0, + "Expecting 0 kernel started threads but have" + " %zu", + mKernelStartedThreads); + return mCurrentThreads; +} + +bool ProcessState::isThreadPoolStarted() const { + return mThreadPoolStarted; } #define DRIVER_FEATURES_PATH "/dev/binderfs/features/" @@ -415,6 +457,8 @@ bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { static const char* const names[] = { [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] = DRIVER_FEATURES_PATH "oneway_spam_detection", + [static_cast<int>(DriverFeature::EXTENDED_ERROR)] = + DRIVER_FEATURES_PATH "extended_error", }; int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC); char on; @@ -491,6 +535,8 @@ ProcessState::ProcessState(const char* driver) mExecutingThreadsCount(0), mWaitingForThreads(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS), + mCurrentThreads(0), + mKernelStartedThreads(0), mStarvationStartTimeMs(0), mForked(false), mThreadPoolStarted(false), diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp new file mode 100644 index 0000000000..2e7030437c --- /dev/null +++ b/libs/binder/RecordedTransaction.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/RecordedTransaction.h> +#include <sys/mman.h> +#include <algorithm> + +using android::Parcel; +using android::base::borrowed_fd; +using android::base::unique_fd; +using android::binder::debug::RecordedTransaction; + +#define PADDING8(s) ((8 - (s) % 8) % 8) + +static_assert(PADDING8(0) == 0); +static_assert(PADDING8(1) == 7); +static_assert(PADDING8(7) == 1); +static_assert(PADDING8(8) == 0); + +// Transactions are sequentially recorded to a file descriptor. +// +// An individual RecordedTransaction is written with the following format: +// +// WARNING: Though the following format is designed to be stable and +// extensible, it is under active development and should be considered +// unstable until this warning is removed. +// +// A RecordedTransaction is written to a file as a sequence of Chunks. +// +// A Chunk consists of a ChunkDescriptor, Data, Padding, and a Checksum. +// +// The ChunkDescriptor identifies the type of Data in the chunk, and the size +// of the Data. +// +// The Data may be any uint32 number of bytes in length in [0-0xfffffff0]. +// +// Padding is between [0-7] zero-bytes after the Data such that the Chunk ends +// on an 8-byte boundary. The ChunkDescriptor's dataSize does not include the +// size of Padding. +// +// The checksum is a 64-bit wide XOR of all previous data from the start of the +// ChunkDescriptor to the end of Padding. +// +// ┌───────────────────────────┐ +// │Chunk │ +// │┌────────────────────────┐ │ +// ││ChunkDescriptor │ │ +// ││┌───────────┬──────────┐│ │ +// │││chunkType │dataSize ├┼─┼─┐ +// │││uint32_t │uint32_t ││ │ │ +// ││└───────────┴──────────┘│ │ │ +// │└────────────────────────┘ │ │ +// │┌─────────────────────────┐│ │ +// ││Data ││ │ +// ││bytes * dataSize │◀─┘ +// ││ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤│ +// ││ Padding ││ +// │└───┴─────────────────────┘│ +// │┌─────────────────────────┐│ +// ││checksum ││ +// ││uint64_t ││ +// │└─────────────────────────┘│ +// └───────────────────────────┘ +// +// A RecordedTransaction is written as a Header Chunk with fields about the +// transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk. +// ┌──────────────────────┐ +// │ Header Chunk │ +// ├──────────────────────┤ +// │ Sent Parcel Chunk │ +// ├──────────────────────┤ +// │ Reply Parcel Chunk │ +// ├──────────────────────┤ +// ║ End Chunk ║ +// ╚══════════════════════╝ +// +// On reading a RecordedTransaction, an unrecognized chunk is checksummed +// then skipped according to size information in the ChunkDescriptor. Chunks +// are read and either assimilated or skipped until an End Chunk is +// encountered. This has three notable implications: +// +// 1. Older and newer implementations should be able to read one another's +// Transactions, though there will be loss of information. +// 2. With the exception of the End Chunk, Chunks can appear in any order +// and even repeat, though this is not recommended. +// 3. If any Chunk is repeated, old values will be overwritten by versions +// encountered later in the file. +// +// No effort is made to ensure the expected chunks are present. A single +// End Chunk may therefore produce an empty, meaningless RecordedTransaction. + +RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept { + mData = t.mData; + mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize()); + mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize()); +} + +std::optional<RecordedTransaction> RecordedTransaction::fromDetails( + const String16& interfaceName, uint32_t code, uint32_t flags, timespec timestamp, + const Parcel& dataParcel, const Parcel& replyParcel, status_t err) { + RecordedTransaction t; + t.mData.mHeader = {code, + flags, + static_cast<int32_t>(err), + dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0), + static_cast<int64_t>(timestamp.tv_sec), + static_cast<int32_t>(timestamp.tv_nsec), + 0}; + + t.mData.mInterfaceName = std::string(String8(interfaceName).string()); + if (interfaceName.size() != t.mData.mInterfaceName.size()) { + LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte " + "utf-8: " + << interfaceName; + return std::nullopt; + } + + if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set sent parcel data."; + return std::nullopt; + } + + if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set reply parcel data."; + return std::nullopt; + } + + return std::optional<RecordedTransaction>(std::move(t)); +} + +enum { + HEADER_CHUNK = 1, + DATA_PARCEL_CHUNK = 2, + REPLY_PARCEL_CHUNK = 3, + INTERFACE_NAME_CHUNK = 4, + END_CHUNK = 0x00ffffff, +}; + +struct ChunkDescriptor { + uint32_t chunkType = 0; + uint32_t dataSize = 0; +}; +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<transaction_checksum_t*>(chunkOut); + return android::NO_ERROR; +} + +std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) { + RecordedTransaction t; + ChunkDescriptor chunk; + const long pageSize = sysconf(_SC_PAGE_SIZE); + do { + transaction_checksum_t checksum = 0; + if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) { + LOG(ERROR) << "Failed to read chunk descriptor."; + return std::nullopt; + } + off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); + off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize; + off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart; + + if (chunk.dataSize > kMaxChunkDataSize) { + LOG(ERROR) << "Chunk data exceeds maximum size."; + return std::nullopt; + } + + size_t chunkPayloadSize = + chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t); + + if (PADDING8(chunkPayloadSize) != 0) { + LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize; + return std::nullopt; + } + + transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>( + mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED, + fd.get(), mmapPageAlignedStart)); + payloadMap += mmapPayloadStartOffset / + 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); + return std::nullopt; + } + for (size_t checksumIndex = 0; + checksumIndex < chunkPayloadSize / sizeof(transaction_checksum_t); checksumIndex++) { + checksum ^= payloadMap[checksumIndex]; + } + if (checksum != 0) { + LOG(ERROR) << "Checksum failed."; + return std::nullopt; + } + lseek(fd.get(), chunkPayloadSize, SEEK_CUR); + + switch (chunk.chunkType) { + case HEADER_CHUNK: { + if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) { + LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected " + << sizeof(TransactionHeader) << "."; + return std::nullopt; + } + t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap); + break; + } + case INTERFACE_NAME_CHUNK: { + t.mData.mInterfaceName = + std::string(reinterpret_cast<char*>(payloadMap), chunk.dataSize); + break; + } + case DATA_PARCEL_CHUNK: { + if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap), + chunk.dataSize) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set sent parcel data."; + return std::nullopt; + } + break; + } + case REPLY_PARCEL_CHUNK: { + if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap), + chunk.dataSize) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set reply parcel data."; + return std::nullopt; + } + break; + } + case END_CHUNK: + break; + default: + LOG(INFO) << "Unrecognized chunk."; + continue; + } + } while (chunk.chunkType != END_CHUNK); + + return std::optional<RecordedTransaction>(std::move(t)); +} + +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"; + return BAD_VALUE; + } + ChunkDescriptor descriptor = {.chunkType = chunkType, + .dataSize = static_cast<uint32_t>(byteCount)}; + // Prepare Chunk content as byte * + const std::byte* descriptorBytes = reinterpret_cast<const std::byte*>(&descriptor); + const std::byte* dataBytes = reinterpret_cast<const std::byte*>(data); + + // Add Chunk to intermediate buffer, except checksum + std::vector<std::byte> buffer; + buffer.insert(buffer.end(), descriptorBytes, descriptorBytes + sizeof(ChunkDescriptor)); + buffer.insert(buffer.end(), dataBytes, dataBytes + byteCount); + std::byte zero{0}; + buffer.insert(buffer.end(), PADDING8(byteCount), zero); + + // Calculate checksum from buffer + transaction_checksum_t* checksumData = reinterpret_cast<transaction_checksum_t*>(buffer.data()); + transaction_checksum_t checksumValue = 0; + for (size_t idx = 0; idx < (buffer.size() / sizeof(transaction_checksum_t)); idx++) { + checksumValue ^= checksumData[idx]; + } + + // Write checksum to buffer + std::byte* checksumBytes = reinterpret_cast<std::byte*>(&checksumValue); + 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(); + return UNKNOWN_ERROR; + } + return NO_ERROR; +} + +android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { + if (NO_ERROR != + writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader), + reinterpret_cast<const uint8_t*>(&(mData.mHeader)))) { + LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get(); + return UNKNOWN_ERROR; + } + if (NO_ERROR != + writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t), + reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) { + LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get(); + return UNKNOWN_ERROR; + } + + if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) { + LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get(); + return UNKNOWN_ERROR; + } + if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) { + LOG(ERROR) << "Failed to write reply Parcel to fd " << 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(); + return UNKNOWN_ERROR; + } + return NO_ERROR; +} + +const std::string& RecordedTransaction::getInterfaceName() const { + return mData.mInterfaceName; +} + +uint32_t RecordedTransaction::getCode() const { + return mData.mHeader.code; +} + +uint32_t RecordedTransaction::getFlags() const { + return mData.mHeader.flags; +} + +int32_t RecordedTransaction::getReturnedStatus() const { + return mData.mHeader.statusReturned; +} + +timespec RecordedTransaction::getTimestamp() const { + time_t sec = mData.mHeader.timestampSeconds; + int32_t nsec = mData.mHeader.timestampNanoseconds; + return (timespec){.tv_sec = sec, .tv_nsec = nsec}; +} + +uint32_t RecordedTransaction::getVersion() const { + return mData.mHeader.version; +} + +const Parcel& RecordedTransaction::getDataParcel() const { + return mSent; +} + +const Parcel& RecordedTransaction::getReplyParcel() const { + return mReply; +} diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index ace5cd5052..0d06e9e83b 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -24,18 +24,22 @@ #include <thread> #include <vector> -#include <android-base/file.h> #include <android-base/hex.h> #include <android-base/scopeguard.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <log/log.h> +#include <utils/Compat.h> +#include "BuildFlags.h" #include "FdTrigger.h" +#include "OS.h" #include "RpcSocketAddress.h" #include "RpcState.h" +#include "RpcTransportUtils.h" #include "RpcWireFormat.h" +#include "Utils.h" namespace android { @@ -46,27 +50,29 @@ using base::unique_fd; RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {} RpcServer::~RpcServer() { - (void)shutdown(); + RpcMutexUniqueLock _l(mLock); + LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Must call shutdown() before destructor"); } sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { // Default is without TLS. if (rpcTransportCtxFactory == nullptr) - rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make(); + rpcTransportCtxFactory = makeDefaultRpcTransportCtxFactory(); auto ctx = rpcTransportCtxFactory->newServerCtx(); if (ctx == nullptr) return nullptr; return sp<RpcServer>::make(std::move(ctx)); } +status_t RpcServer::setupUnixDomainSocketBootstrapServer(unique_fd bootstrapFd) { + return setupExternalServer(std::move(bootstrapFd), &RpcServer::recvmsgSocketConnection); +} + status_t RpcServer::setupUnixDomainServer(const char* path) { return setupSocketServer(UnixSocketAddress(path)); } -status_t RpcServer::setupVsockServer(unsigned int port) { - // realizing value w/ this type at compile time to avoid ubsan abort - constexpr unsigned int kAnyCid = VMADDR_CID_ANY; - - return setupSocketServer(VsockSocketAddress(kAnyCid, port)); +status_t RpcServer::setupVsockServer(unsigned int bindCid, unsigned int port) { + return setupSocketServer(VsockSocketAddress(bindCid, port)); } status_t RpcServer::setupInetServer(const char* address, unsigned int port, @@ -83,7 +89,7 @@ status_t RpcServer::setupInetServer(const char* address, unsigned int port, LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet"); sockaddr_in addr{}; socklen_t len = sizeof(addr); - if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) { + if (0 != getsockname(mServer.fd.get(), reinterpret_cast<sockaddr*>(&addr), &len)) { int savedErrno = errno; ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(), strerror(savedErrno)); @@ -121,28 +127,42 @@ void RpcServer::setProtocolVersion(uint32_t version) { mProtocolVersion = version; } +void RpcServer::setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes) { + mSupportedFileDescriptorTransportModes.reset(); + for (RpcSession::FileDescriptorTransportMode mode : modes) { + mSupportedFileDescriptorTransportModes.set(static_cast<size_t>(mode)); + } +} + void RpcServer::setRootObject(const sp<IBinder>& binder) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); mRootObjectFactory = nullptr; mRootObjectWeak = mRootObject = binder; } void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectFactory = nullptr; mRootObjectWeak = binder; } void RpcServer::setPerSessionRootObject( - std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) { - std::lock_guard<std::mutex> _l(mLock); + std::function<sp<IBinder>(const void*, size_t)>&& makeObject) { + RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectWeak.clear(); mRootObjectFactory = std::move(makeObject); } +void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& filter) { + RpcMutexLockGuard _l(mLock); + LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined"); + mConnectionFilter = std::move(filter); +} + sp<IBinder> RpcServer::getRootObject() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); bool hasWeak = mRootObjectWeak.unsafe_get(); sp<IBinder> ret = mRootObjectWeak.promote(); ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr"); @@ -150,7 +170,7 @@ sp<IBinder> RpcServer::getRootObject() { } std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mCtx->getCertificate(format); } @@ -159,16 +179,61 @@ static void joinRpcServer(sp<RpcServer>&& thiz) { } void RpcServer::start() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!"); - mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this)); + mJoinThread = + std::make_unique<RpcMaybeThread>(&joinRpcServer, sp<RpcServer>::fromExisting(this)); + rpcJoinIfSingleThreaded(*mJoinThread); +} + +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) { + int savedErrno = errno; + ALOGE("Could not accept4 socket: %s", strerror(savedErrno)); + return -savedErrno; + } + + *out = std::move(clientSocket); + return OK; +} + +status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out) { + int zero = 0; + iovec iov{&zero, sizeof(zero)}; + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds; + + ssize_t num_bytes = receiveMessageFromSocket(server.mServer, &iov, 1, &fds); + if (num_bytes < 0) { + int savedErrno = errno; + ALOGE("Failed recvmsg: %s", strerror(savedErrno)); + return -savedErrno; + } + if (num_bytes == 0) { + return DEAD_OBJECT; + } + if (fds.size() != 1) { + ALOGE("Expected exactly one fd from recvmsg, got %zu", fds.size()); + return -EINVAL; + } + + unique_fd fd(std::move(std::get<unique_fd>(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(); + } + + *out = RpcTransportFd(std::move(fd)); + return OK; } void RpcServer::join() { { - std::lock_guard<std::mutex> _l(mLock); - LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join."); + RpcMutexLockGuard _l(mLock); + LOG_ALWAYS_FATAL_IF(!mServer.fd.ok(), "RpcServer must be setup to join."); + LOG_ALWAYS_FATAL_IF(mAcceptFn == nullptr, "RpcServer must have an accept() function"); LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined"); mJoinThreadRunning = true; mShutdownTrigger = FdTrigger::make(); @@ -177,40 +242,58 @@ void RpcServer::join() { status_t status; while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) { - sockaddr_storage addr; - socklen_t addrLen = sizeof(addr); + std::array<uint8_t, kRpcAddressSize> addr; + static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small"); + socklen_t addrLen = addr.size(); + + RpcTransportFd clientSocket; + if ((status = mAcceptFn(*this, &clientSocket)) != OK) { + if (status == DEAD_OBJECT) + break; + else + continue; + } - unique_fd clientFd( - TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr), - &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK))); + LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get()); - LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address"); + if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()), + &addrLen)) { + ALOGE("Could not getpeername socket: %s", strerror(errno)); + continue; + } - if (clientFd < 0) { - ALOGE("Could not accept4 socket: %s", strerror(errno)); + if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) { + ALOGE("Dropped client connection fd %d", clientSocket.fd.get()); continue; } - LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); { - std::lock_guard<std::mutex> _l(mLock); - std::thread thread = - std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this), - std::move(clientFd), addr, addrLen); - mConnectingThreads[thread.get_id()] = std::move(thread); + RpcMutexLockGuard _l(mLock); + RpcMaybeThread thread = + RpcMaybeThread(&RpcServer::establishConnection, + sp<RpcServer>::fromExisting(this), std::move(clientSocket), addr, + addrLen, RpcSession::join); + + auto& threadRef = mConnectingThreads[thread.get_id()]; + threadRef = std::move(thread); + rpcJoinIfSingleThreaded(threadRef); } } LOG_RPC_DETAIL("RpcServer::join exiting with %s", statusToString(status).c_str()); - { - std::lock_guard<std::mutex> _l(mLock); + if constexpr (kEnableRpcThreads) { + RpcMutexLockGuard _l(mLock); mJoinThreadRunning = false; + } else { + // Multi-threaded builds clear this in shutdown(), but we need it valid + // so the loop above exits cleanly + mShutdownTrigger = nullptr; } mShutdownCv.notify_all(); } bool RpcServer::shutdown() { - std::unique_lock<std::mutex> _l(mLock); + RpcMutexUniqueLock _l(mLock); if (mShutdownTrigger == nullptr) { LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)"); return false; @@ -221,10 +304,16 @@ bool RpcServer::shutdown() { for (auto& [id, session] : mSessions) { (void)id; // server lock is a more general lock - std::lock_guard<std::mutex> _lSession(session->mMutex); + RpcMutexLockGuard _lSession(session->mMutex); session->mShutdownTrigger->trigger(); } + if constexpr (!kEnableRpcThreads) { + // In single-threaded mode we're done here, everything else that + // needs to happen should be at the end of RpcServer::join() + return true; + } + while (mJoinThreadRunning || !mConnectingThreads.empty() || !mSessions.empty()) { if (std::cv_status::timeout == mShutdownCv.wait_for(_l, std::chrono::seconds(1))) { ALOGE("Waiting for RpcServer to shut down (1s w/o progress). Join thread running: %d, " @@ -252,7 +341,7 @@ bool RpcServer::shutdown() { } std::vector<sp<RpcSession>> RpcServer::listSessions() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); std::vector<sp<RpcSession>> sessions; for (auto& [id, session] : mSessions) { (void)id; @@ -262,12 +351,14 @@ std::vector<sp<RpcSession>> RpcServer::listSessions() { } size_t RpcServer::numUninitializedSessions() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mConnectingThreads.size(); } -void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen) { +void RpcServer::establishConnection( + sp<RpcServer>&& server, RpcTransportFd clientFd, std::array<uint8_t, kRpcAddressSize> addr, + size_t addrLen, + std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn) { // mShutdownTrigger can only be cleared once connection threads have joined. // It must be set before this thread is started LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr); @@ -275,7 +366,7 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie status_t status = OK; - int clientFdForLog = clientFd.get(); + int clientFdForLog = clientFd.fd.get(); auto client = server->mCtx->newTransport(std::move(clientFd), server->mShutdownTrigger.get()); if (client == nullptr) { ALOGE("Dropping accept4()-ed socket because sslAccept fails"); @@ -288,7 +379,8 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie RpcConnectionHeader header; if (status == OK) { iovec iov{&header, sizeof(header)}; - status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {}); + status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, + std::nullopt, /*ancillaryFds=*/nullptr); if (status != OK) { ALOGE("Failed to read ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -302,8 +394,8 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie if (header.sessionIdSize == kSessionIdBytes) { sessionId.resize(header.sessionIdSize); iovec iov{sessionId.data(), sessionId.size()}; - status = - client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {}); + status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, + std::nullopt, /*ancillaryFds=*/nullptr); if (status != OK) { ALOGE("Failed to read session ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -333,7 +425,8 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie }; iovec iov{&response, sizeof(response)}; - status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, {}); + status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, + std::nullopt, nullptr); if (status != OK) { ALOGE("Failed to send new session response: %s", statusToString(status).c_str()); // still need to cleanup before we can return @@ -341,12 +434,12 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie } } - std::thread thisThread; + RpcMaybeThread thisThread; sp<RpcSession> session; { - std::unique_lock<std::mutex> _l(server->mLock); + RpcMutexUniqueLock _l(server->mLock); - auto threadId = server->mConnectingThreads.find(std::this_thread::get_id()); + auto threadId = server->mConnectingThreads.find(rpc_this_thread::get_id()); LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(), "Must establish connection on owned thread"); thisThread = std::move(threadId->second); @@ -380,24 +473,34 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie return; } - base::unique_fd fd(TEMP_FAILURE_RETRY( - open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW))); - if (!base::ReadFully(fd, sessionId.data(), sessionId.size())) { - ALOGE("Could not read from /dev/urandom to create session ID"); + auto status = getRandomBytes(sessionId.data(), sessionId.size()); + if (status != OK) { + ALOGE("Failed to read random session ID: %s", strerror(-status)); return; } } while (server->mSessions.end() != server->mSessions.find(sessionId)); - session = RpcSession::make(); + session = sp<RpcSession>::make(nullptr); session->setMaxIncomingThreads(server->mMaxThreads); if (!session->setProtocolVersion(protocolVersion)) return; + if (header.fileDescriptorTransportMode < + server->mSupportedFileDescriptorTransportModes.size() && + server->mSupportedFileDescriptorTransportModes.test( + header.fileDescriptorTransportMode)) { + session->setFileDescriptorTransportMode( + static_cast<RpcSession::FileDescriptorTransportMode>( + header.fileDescriptorTransportMode)); + } else { + ALOGE("Rejecting connection: FileDescriptorTransportMode is not supported: %hhu", + header.fileDescriptorTransportMode); + return; + } + // if null, falls back to server root sp<IBinder> sessionSpecificRoot; if (server->mRootObjectFactory != nullptr) { - sessionSpecificRoot = - server->mRootObjectFactory(reinterpret_cast<const sockaddr*>(&addr), - addrLen); + sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen); if (sessionSpecificRoot == nullptr) { ALOGE("Warning: server returned null from root object factory"); } @@ -438,40 +541,42 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie // avoid strong cycle server = nullptr; - RpcSession::join(std::move(session), std::move(setupResult)); + joinFn(std::move(session), std::move(setupResult)); } status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str()); LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server."); - unique_fd serverFd(TEMP_FAILURE_RETRY( + unique_fd socket_fd(TEMP_FAILURE_RETRY( socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); - if (serverFd == -1) { + if (!socket_fd.ok()) { int savedErrno = errno; ALOGE("Could not create socket: %s", strerror(savedErrno)); return -savedErrno; } - - if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) { + 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)); return -savedErrno; } + return setupRawSocketServer(std::move(socket_fd)); +} + +status_t RpcServer::setupRawSocketServer(unique_fd socket_fd) { + LOG_ALWAYS_FATAL_IF(!socket_fd.ok(), "Socket must be setup to listen."); + // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client, // the backlog is increased to a large number. // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced // to 1. - if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 50 /*backlog*/))) { + if (0 != TEMP_FAILURE_RETRY(listen(socket_fd.get(), 50 /*backlog*/))) { int savedErrno = errno; - ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); + ALOGE("Could not listen initialized Unix socket: %s", strerror(savedErrno)); return -savedErrno; } - - LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str()); - - if (status_t status = setupExternalServer(std::move(serverFd)); status != OK) { + if (status_t status = setupExternalServer(std::move(socket_fd)); status != OK) { ALOGE("Another thread has set up server while calling setupSocketServer. Race?"); return status; } @@ -484,7 +589,7 @@ void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) LOG_RPC_DETAIL("Dropping session with address %s", base::HexString(id.data(), id.size()).c_str()); - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); auto it = mSessions.find(id); LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s", base::HexString(id.data(), id.size()).c_str()); @@ -498,23 +603,40 @@ void RpcServer::onSessionIncomingThreadEnded() { } bool RpcServer::hasServer() { - std::lock_guard<std::mutex> _l(mLock); - return mServer.ok(); + RpcMutexLockGuard _l(mLock); + return mServer.fd.ok(); } unique_fd RpcServer::releaseServer() { - std::lock_guard<std::mutex> _l(mLock); - return std::move(mServer); + RpcMutexLockGuard _l(mLock); + return std::move(mServer.fd); } -status_t RpcServer::setupExternalServer(base::unique_fd serverFd) { - std::lock_guard<std::mutex> _l(mLock); - if (mServer.ok()) { +status_t RpcServer::setupExternalServer( + base::unique_fd serverFd, + std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn) { + RpcMutexLockGuard _l(mLock); + if (mServer.fd.ok()) { ALOGE("Each RpcServer can only have one server."); return INVALID_OPERATION; } mServer = std::move(serverFd); + mAcceptFn = std::move(acceptFn); return OK; } +status_t RpcServer::setupExternalServer(base::unique_fd serverFd) { + return setupExternalServer(std::move(serverFd), &RpcServer::acceptSocketConnection); +} + +bool RpcServer::hasActiveRequests() { + RpcMutexLockGuard _l(mLock); + for (const auto& [_, session] : mSessions) { + if (session->hasActiveRequests()) { + return true; + } + } + return !mServer.isInPollingState(); +} + } // namespace android diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index d40778a3d8..ce6ef2becf 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -21,7 +21,6 @@ #include <dlfcn.h> #include <inttypes.h> #include <poll.h> -#include <pthread.h> #include <unistd.h> #include <string_view> @@ -34,21 +33,21 @@ #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <binder/Stability.h> +#include <utils/Compat.h> #include <utils/String8.h> +#include "BuildFlags.h" #include "FdTrigger.h" +#include "OS.h" #include "RpcSocketAddress.h" #include "RpcState.h" +#include "RpcTransportUtils.h" #include "RpcWireFormat.h" #include "Utils.h" -#ifdef __GLIBC__ -extern "C" pid_t gettid(); -#endif - -#ifndef __ANDROID_RECOVERY__ -#include <android_runtime/vm.h> +#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) #include <jni.h> +extern "C" JavaVM* AndroidRuntimeGetJavaVM(); #endif namespace android { @@ -63,14 +62,14 @@ RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ct RpcSession::~RpcSession() { LOG_RPC_DETAIL("RpcSession destroyed %p", this); - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mConnections.mIncoming.size() != 0, "Should not be able to destroy a session with servers in use."); } sp<RpcSession> RpcSession::make() { // Default is without TLS. - return make(RpcTransportCtxFactoryRaw::make()); + return make(makeDefaultRpcTransportCtxFactory()); } sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { @@ -80,34 +79,30 @@ sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTrans } void RpcSession::setMaxIncomingThreads(size_t threads) { - std::lock_guard<std::mutex> _l(mMutex); - LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), - "Must set max incoming threads before setting up connections, but has %zu " - "client(s) and %zu server(s)", - mConnections.mOutgoing.size(), mConnections.mIncoming.size()); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, + "Must set max incoming threads before setting up connections"); mMaxIncomingThreads = threads; } size_t RpcSession::getMaxIncomingThreads() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mMaxIncomingThreads; } void RpcSession::setMaxOutgoingThreads(size_t threads) { - std::lock_guard<std::mutex> _l(mMutex); - LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), - "Must set max outgoing threads before setting up connections, but has %zu " - "client(s) and %zu server(s)", - mConnections.mOutgoing.size(), mConnections.mIncoming.size()); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, + "Must set max outgoing threads before setting up connections"); mMaxOutgoingThreads = threads; } size_t RpcSession::getMaxOutgoingThreads() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mMaxOutgoingThreads; } -bool RpcSession::setProtocolVersion(uint32_t version) { +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 " @@ -116,7 +111,9 @@ bool RpcSession::setProtocolVersion(uint32_t version) { return false; } - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(checkStarted && mStartedSetup, + "Must set protocol version before setting up connections"); if (mProtocolVersion && version > *mProtocolVersion) { ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u", *mProtocolVersion, version); @@ -127,15 +124,58 @@ bool RpcSession::setProtocolVersion(uint32_t version) { return true; } +bool RpcSession::setProtocolVersion(uint32_t version) { + return setProtocolVersionInternal(version, true); +} + std::optional<uint32_t> RpcSession::getProtocolVersion() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mProtocolVersion; } +void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) { + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, + "Must set file descriptor transport mode before setting up connections"); + mFileDescriptorTransportMode = mode; +} + +RpcSession::FileDescriptorTransportMode RpcSession::getFileDescriptorTransportMode() { + return mFileDescriptorTransportMode; +} + status_t RpcSession::setupUnixDomainClient(const char* path) { return setupSocketClient(UnixSocketAddress(path)); } +status_t RpcSession::setupUnixDomainSocketBootstrapClient(unique_fd bootstrapFd) { + mBootstrapTransport = + mCtx->newTransport(RpcTransportFd(std::move(bootstrapFd)), mShutdownTrigger.get()); + return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) { + int socks[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, socks) < 0) { + int savedErrno = errno; + ALOGE("Failed socketpair: %s", strerror(savedErrno)); + return -savedErrno; + } + unique_fd clientFd(socks[0]), serverFd(socks[1]); + + int zero = 0; + iovec iov{&zero, sizeof(zero)}; + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds; + fds.push_back(std::move(serverFd)); + + status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov, + 1, std::nullopt, &fds); + if (status != OK) { + ALOGE("Failed to send fd over bootstrap transport: %s", strerror(-status)); + return status; + } + + return initAndAddConnection(RpcTransportFd(std::move(clientFd)), sessionId, incoming); + }); +} + status_t RpcSession::setupVsockClient(unsigned int cid, unsigned int port) { return setupSocketClient(VsockSocketAddress(cid, port)); } @@ -151,14 +191,9 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { return NAME_NOT_FOUND; } -status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) { - // Why passing raw fd? When fd is passed as reference, Clang analyzer sees that the variable - // `fd` is a moved-from object. To work-around the issue, unwrap the raw fd from the outer `fd`, - // pass the raw fd by value to the lambda, and then finally wrap it in unique_fd inside the - // lambda. - return setupClient([&, raw = fd.release()](const std::vector<uint8_t>& sessionId, - bool incoming) -> status_t { - unique_fd fd(raw); +status_t RpcSession::setupPreconnectedClient(base::unique_fd fd, + std::function<unique_fd()>&& request) { + return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t { if (!fd.ok()) { fd = request(); if (!fd.ok()) return BAD_VALUE; @@ -167,7 +202,11 @@ status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_ ALOGE("setupPreconnectedClient: %s", res.error().message().c_str()); return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); } - return initAndAddConnection(std::move(fd), sessionId, incoming); + + RpcTransportFd transportFd(std::move(fd)); + status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming); + fd = unique_fd(); // Explicitly reset after move to avoid analyzer warning. + return status; }); } @@ -183,7 +222,8 @@ status_t RpcSession::addNullDebuggingClient() { return -savedErrno; } - auto server = mCtx->newTransport(std::move(serverFd), mShutdownTrigger.get()); + RpcTransportFd transportFd(std::move(serverFd)); + auto server = mCtx->newTransport(std::move(transportFd), mShutdownTrigger.get()); if (server == nullptr) { ALOGE("Unable to set up RpcTransport"); return UNKNOWN_ERROR; @@ -208,7 +248,7 @@ status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) { } bool RpcSession::shutdownAndWait(bool wait) { - std::unique_lock<std::mutex> _l(mMutex); + RpcMutexUniqueLock _l(mMutex); LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Shutdown trigger not installed"); mShutdownTrigger->trigger(); @@ -221,6 +261,12 @@ bool RpcSession::shutdownAndWait(bool wait) { } _l.unlock(); + + if (status_t res = state()->sendObituaries(sp<RpcSession>::fromExisting(this)); res != OK) { + ALOGE("Failed to send obituaries as the RpcSession is shutting down: %s", + statusToString(res).c_str()); + } + mRpcBinderState->clear(); return true; @@ -255,7 +301,7 @@ status_t RpcSession::sendDecStrongToTarget(uint64_t address, size_t target) { status_t RpcSession::readId() { { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client."); } @@ -278,25 +324,27 @@ void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded( } void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() { + mShutdownCount += 1; mCv.notify_all(); } -void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock, +void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session) { - while (session->mConnections.mIncoming.size() > 0) { + while (mShutdownCount < session->mConnections.mMaxIncoming) { if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) { ALOGE("Waiting for RpcSession to shut down (1s w/o progress): %zu incoming connections " - "still.", - session->mConnections.mIncoming.size()); + "still %zu/%zu fully shutdown.", + session->mConnections.mIncoming.size(), mShutdownCount.load(), + session->mConnections.mMaxIncoming); } } } -void RpcSession::preJoinThreadOwnership(std::thread thread) { - LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread"); +void RpcSession::preJoinThreadOwnership(RpcMaybeThread thread) { + LOG_ALWAYS_FATAL_IF(thread.get_id() != rpc_this_thread::get_id(), "Must own this thread"); { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); mConnections.mThreads[thread.get_id()] = std::move(thread); } } @@ -323,7 +371,7 @@ RpcSession::PreJoinSetupResult RpcSession::preJoinSetup( } namespace { -#ifdef __ANDROID_RECOVERY__ +#if !defined(__ANDROID__) || defined(__ANDROID_RECOVERY__) class JavaThreadAttacher {}; #else // RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If @@ -360,10 +408,11 @@ public: "Unable to detach thread. No JavaVM, but it was present before!"); LOG_RPC_DETAIL("Detaching current thread from JVM"); - if (vm->DetachCurrentThread() != JNI_OK) { + int ret = vm->DetachCurrentThread(); + if (ret == JNI_OK) { mAttached = false; } else { - ALOGW("Unable to detach current thread from JVM"); + ALOGW("Unable to detach current thread from JVM (%d)", ret); } } @@ -403,8 +452,8 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult sp<RpcSession::EventListener> listener; { - std::lock_guard<std::mutex> _l(session->mMutex); - auto it = session->mConnections.mThreads.find(std::this_thread::get_id()); + RpcMutexLockGuard _l(session->mMutex); + auto it = session->mConnections.mThreads.find(rpc_this_thread::get_id()); LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end()); it->second.detach(); session->mConnections.mThreads.erase(it); @@ -437,10 +486,17 @@ sp<RpcServer> RpcSession::server() { status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>& connectAndInit) { { - std::lock_guard<std::mutex> _l(mMutex); - LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0, - "Must only setup session once, but already has %zu clients", - mConnections.mOutgoing.size()); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once"); + mStartedSetup = true; + + if constexpr (!kEnableRpcThreads) { + LOG_ALWAYS_FATAL_IF(mMaxIncomingThreads > 0, + "Incoming threads are not supported on single-threaded libbinder"); + // mMaxIncomingThreads should not change from here to its use below, + // since we set mStartedSetup==true and setMaxIncomingThreads checks + // for that + } } if (auto status = initShutdownTrigger(); status != OK) return status; @@ -464,6 +520,9 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< mProtocolVersion = oldProtocolVersion; mConnections = {}; + + // clear mStartedSetup so that we can reuse this RpcSession + mStartedSetup = false; }); if (status_t status = connectAndInit({}, false /*incoming*/); status != OK) return status; @@ -481,7 +540,7 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< sp<RpcSession>::fromExisting(this), &version); status != OK) return status; - if (!setProtocolVersion(version)) return BAD_VALUE; + if (!setProtocolVersionInternal(version, false)) return BAD_VALUE; } // TODO(b/189955605): we should add additional sessions dynamically @@ -549,12 +608,14 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, return -savedErrno; } - if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { + RpcTransportFd transportFd(std::move(serverFd)); + + if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) { int connErrno = errno; if (connErrno == EAGAIN || connErrno == EINPROGRESS) { // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or // EINPROGRESS (for others). Call poll() and getsockopt() to get the error. - status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT); + status_t pollStatus = mShutdownTrigger->triggerablePoll(transportFd, POLLOUT); if (pollStatus != OK) { ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s", statusToString(pollStatus).c_str()); @@ -562,8 +623,8 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, } // Set connErrno to the errno that connect() would have set if the fd were blocking. socklen_t connErrnoLen = sizeof(connErrno); - int ret = - getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &connErrno, &connErrnoLen); + int ret = getsockopt(transportFd.fd.get(), SOL_SOCKET, SO_ERROR, &connErrno, + &connErrnoLen); if (ret == -1) { int savedErrno = errno; ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s. " @@ -585,16 +646,17 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, return -connErrno; } } - LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get()); + LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), + transportFd.fd.get()); - return initAndAddConnection(std::move(serverFd), sessionId, incoming); + return initAndAddConnection(std::move(transportFd), sessionId, incoming); } ALOGE("Ran out of retries to connect to %s", addr.toString().c_str()); return UNKNOWN_ERROR; } -status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_t>& sessionId, +status_t RpcSession::initAndAddConnection(RpcTransportFd fd, const std::vector<uint8_t>& sessionId, bool incoming) { LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr); auto server = mCtx->newTransport(std::move(fd), mShutdownTrigger.get()); @@ -613,6 +675,7 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ RpcConnectionHeader header{ .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION), .options = 0, + .fileDescriptorTransportMode = static_cast<uint8_t>(mFileDescriptorTransportMode), .sessionIdSize = static_cast<uint16_t>(sessionId.size()), }; @@ -621,8 +684,8 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ } iovec headerIov{&header, sizeof(header)}; - auto sendHeaderStatus = - server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, {}); + auto sendHeaderStatus = server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, + std::nullopt, nullptr); if (sendHeaderStatus != OK) { ALOGE("Could not write connection header to socket: %s", statusToString(sendHeaderStatus).c_str()); @@ -633,7 +696,8 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())), sessionId.size()}; auto sendSessionIdStatus = - server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, {}); + server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, + std::nullopt, nullptr); if (sendSessionIdStatus != OK) { ALOGE("Could not write session ID ('%s') to socket: %s", base::HexString(sessionId.data(), sessionId.size()).c_str(), @@ -652,14 +716,14 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ } status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) { - std::mutex mutex; - std::condition_variable joinCv; - std::unique_lock<std::mutex> lock(mutex); - std::thread thread; + RpcMutex mutex; + RpcConditionVariable joinCv; + RpcMutexUniqueLock lock(mutex); + RpcMaybeThread thread; sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this); bool ownershipTransferred = false; - thread = std::thread([&]() { - std::unique_lock<std::mutex> threadLock(mutex); + thread = RpcMaybeThread([&]() { + RpcMutexUniqueLock threadLock(mutex); std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) sp<RpcSession> session = thiz; @@ -675,6 +739,7 @@ status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTran RpcSession::join(std::move(session), std::move(setupResult)); }); + rpcJoinIfSingleThreaded(thread); joinCv.wait(lock, [&] { return ownershipTransferred; }); LOG_ALWAYS_FATAL_IF(!ownershipTransferred); return OK; @@ -694,9 +759,9 @@ status_t RpcSession::initShutdownTrigger() { status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) { sp<RpcConnection> connection = sp<RpcConnection>::make(); { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); connection->rpcTransport = std::move(rpcTransport); - connection->exclusiveTid = gettid(); + connection->exclusiveTid = rpcGetThreadId(); mConnections.mOutgoing.push_back(connection); } @@ -706,10 +771,7 @@ status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTran mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this)); } - { - std::lock_guard<std::mutex> _l(mMutex); - connection->exclusiveTid = std::nullopt; - } + clearConnectionTid(connection); return status; } @@ -722,6 +784,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene LOG_ALWAYS_FATAL_IF(mEventListener != nullptr); LOG_ALWAYS_FATAL_IF(eventListener == nullptr); LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr); + LOG_ALWAYS_FATAL_IF(mCtx != nullptr); mShutdownTrigger = FdTrigger::make(); if (mShutdownTrigger == nullptr) return false; @@ -735,7 +798,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport) { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); if (mConnections.mIncoming.size() >= mMaxIncomingThreads) { ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)", @@ -753,7 +816,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( sp<RpcConnection> session = sp<RpcConnection>::make(); session->rpcTransport = std::move(rpcTransport); - session->exclusiveTid = gettid(); + session->exclusiveTid = rpcGetThreadId(); mConnections.mIncoming.push_back(session); mConnections.mMaxIncoming = mConnections.mIncoming.size(); @@ -762,7 +825,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( } bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { - std::unique_lock<std::mutex> _l(mMutex); + RpcMutexUniqueLock _l(mMutex); if (auto it = std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection); it != mConnections.mIncoming.end()) { @@ -779,6 +842,15 @@ bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { return false; } +void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) { + RpcMutexUniqueLock _l(mMutex); + connection->exclusiveTid = std::nullopt; + if (mConnections.mWaitingThreads > 0) { + _l.unlock(); + mAvailableConnectionCv.notify_one(); + } +} + std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) { return mCtx->getCertificate(format); } @@ -789,8 +861,8 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co connection->mConnection = nullptr; connection->mReentrant = false; - pid_t tid = gettid(); - std::unique_lock<std::mutex> _l(session->mMutex); + uint64_t tid = rpcGetThreadId(); + RpcMutexUniqueLock _l(session->mMutex); session->mConnections.mWaitingThreads++; while (true) { @@ -876,7 +948,7 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co return OK; } -void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive, +void RpcSession::ExclusiveConnection::findConnection(uint64_t tid, sp<RpcConnection>* exclusive, sp<RpcConnection>* available, std::vector<sp<RpcConnection>>& sockets, size_t socketsIndexHint) { @@ -908,13 +980,28 @@ RpcSession::ExclusiveConnection::~ExclusiveConnection() { // is using this fd, and it retains the right to it. So, we don't give up // exclusive ownership, and no thread is freed. if (!mReentrant && mConnection != nullptr) { - std::unique_lock<std::mutex> _l(mSession->mMutex); - mConnection->exclusiveTid = std::nullopt; - if (mSession->mConnections.mWaitingThreads > 0) { - _l.unlock(); - mSession->mAvailableConnectionCv.notify_one(); + mSession->clearConnectionTid(mConnection); + } +} + +bool RpcSession::hasActiveConnection(const std::vector<sp<RpcConnection>>& connections) { + for (const auto& connection : connections) { + if (connection->exclusiveTid != std::nullopt && !connection->rpcTransport->isWaiting()) { + return true; } } + return false; +} + +bool RpcSession::hasActiveRequests() { + RpcMutexUniqueLock _l(mMutex); + if (hasActiveConnection(mConnections.mIncoming)) { + return true; + } + if (hasActiveConnection(mConnections.mOutgoing)) { + return true; + } + return mConnections.mWaitingThreads != 0; } } // namespace android diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 6d89064a5d..b27f1028d4 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -21,12 +21,14 @@ #include <android-base/hex.h> #include <android-base/macros.h> #include <android-base/scopeguard.h> +#include <android-base/stringprintf.h> #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> #include "Debug.h" #include "RpcWireFormat.h" +#include "Utils.h" #include <random> @@ -34,21 +36,31 @@ namespace android { -using base::ScopeGuard; +using base::StringPrintf; #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { [[clang::no_destroy]] static std::random_device r; - [[clang::no_destroy]] static std::mutex m; + [[clang::no_destroy]] static RpcMutex m; unsigned num; { - std::lock_guard<std::mutex> lock(m); + RpcMutexLockGuard lock(m); num = r(); } if (num % 10 == 0) usleep(num % 1000); } #endif +static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) { + switch (mode) { + case RpcSession::FileDescriptorTransportMode::NONE: + return false; + case RpcSession::FileDescriptorTransportMode::UNIX: + case RpcSession::FileDescriptorTransportMode::TRUSTY: + return true; + } +} + RpcState::RpcState() {} RpcState::~RpcState() {} @@ -77,7 +89,7 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind return INVALID_OPERATION; } - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map @@ -153,7 +165,7 @@ status_t RpcState::onBinderEntering(const sp<RpcSession>& session, uint64_t addr return BAD_VALUE; } - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) { @@ -188,7 +200,7 @@ status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t // extra reference counting packets now. if (binder->remoteBinder()) return OK; - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; auto it = mNodeForAddress.find(address); @@ -215,78 +227,105 @@ status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t return OK; } +status_t RpcState::sendObituaries(const sp<RpcSession>& session) { + RpcMutexUniqueLock _l(mNodeMutex); + + // Gather strong pointers to all of the remote binders for this session so + // we hold the strong references. remoteBinder() returns a raw pointer. + // Send the obituaries and drop the strong pointers outside of the lock so + // the destructors and the onBinderDied calls are not done while locked. + std::vector<sp<IBinder>> remoteBinders; + for (const auto& [_, binderNode] : mNodeForAddress) { + if (auto binder = binderNode.binder.promote()) { + remoteBinders.push_back(std::move(binder)); + } + } + _l.unlock(); + + for (const auto& binder : remoteBinders) { + if (binder->remoteBinder() && + binder->remoteBinder()->getPrivateAccessor().rpcSession() == session) { + binder->remoteBinder()->sendObituary(); + } + } + return OK; +} + size_t RpcState::countBinders() { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); return mNodeForAddress.size(); } void RpcState::dump() { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); dumpLocked(); } void RpcState::clear() { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) { LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(), "New state should be impossible after terminating!"); return; } + mTerminated = true; if (SHOULD_LOG_RPC_DETAIL) { ALOGE("RpcState::clear()"); dumpLocked(); } - // if the destructor of a binder object makes another RPC call, then calling - // decStrong could deadlock. So, we must hold onto these binders until - // mNodeMutex is no longer taken. - std::vector<sp<IBinder>> tempHoldBinder; - - mTerminated = true; + // invariants for (auto& [address, node] : mNodeForAddress) { - sp<IBinder> binder = node.binder.promote(); - LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get()); - - if (node.sentRef != nullptr) { - tempHoldBinder.push_back(node.sentRef); + bool guaranteedHaveBinder = node.timesSent > 0; + if (guaranteedHaveBinder) { + LOG_ALWAYS_FATAL_IF(node.sentRef == nullptr, + "Binder expected to be owned with address: %" PRIu64 " %s", address, + node.toString().c_str()); } } - mNodeForAddress.clear(); + // if the destructor of a binder object makes another RPC call, then calling + // decStrong could deadlock. So, we must hold onto these binders until + // mNodeMutex is no longer taken. + auto temp = std::move(mNodeForAddress); + mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit _l.unlock(); - tempHoldBinder.clear(); // explicit + temp.clear(); // explicit } void RpcState::dumpLocked() { ALOGE("DUMP OF RpcState %p", this); ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size()); for (const auto& [address, node] : mNodeForAddress) { - sp<IBinder> binder = node.binder.promote(); - - const char* desc; - if (binder) { - if (binder->remoteBinder()) { - if (binder->remoteBinder()->isRpcBinder()) { - desc = "(rpc binder proxy)"; - } else { - desc = "(binder proxy)"; - } + ALOGE("- address: %" PRIu64 " %s", address, node.toString().c_str()); + } + ALOGE("END DUMP OF RpcState"); +} + +std::string RpcState::BinderNode::toString() const { + sp<IBinder> strongBinder = this->binder.promote(); + + const char* desc; + if (strongBinder) { + if (strongBinder->remoteBinder()) { + if (strongBinder->remoteBinder()->isRpcBinder()) { + desc = "(rpc binder proxy)"; } else { - desc = "(local binder)"; + desc = "(binder proxy)"; } } else { - desc = "(null)"; + desc = "(local binder)"; } - - ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s", - node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc); + } else { + desc = "(not promotable)"; } - ALOGE("END DUMP OF RpcState"); -} + return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}", + this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc); +} RpcState::CommandData::CommandData(size_t size) : mSize(size) { // The maximum size for regular binder is 1MB for all concurrent @@ -309,9 +348,11 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { mData.reset(new (std::nothrow) uint8_t[size]); } -status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) { +status_t RpcState::rpcSend( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", what, i + 1, niovs, connection->rpcTransport.get(), @@ -320,7 +361,8 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, if (status_t status = connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(), - iovs, niovs, altPoll); + iovs, niovs, altPoll, + ancillaryFds); status != OK) { LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); @@ -331,11 +373,14 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, return OK; } -status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) { +status_t RpcState::rpcRec( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { if (status_t status = connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(), - iovs, niovs, {}); + iovs, niovs, std::nullopt, + ancillaryFds); status != OK) { LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); @@ -355,7 +400,7 @@ status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& c const sp<RpcSession>& session, uint32_t* version) { RpcNewSessionResponse response; iovec iov{&response, sizeof(response)}; - if (status_t status = rpcRec(connection, session, "new session response", &iov, 1); + if (status_t status = rpcRec(connection, session, "new session response", &iov, 1, nullptr); status != OK) { return status; } @@ -369,14 +414,15 @@ status_t RpcState::sendConnectionInit(const sp<RpcSession::RpcConnection>& conne .msg = RPC_CONNECTION_INIT_OKAY, }; iovec iov{&init, sizeof(init)}; - return rpcSend(connection, session, "connection init", &iov, 1); + return rpcSend(connection, session, "connection init", &iov, 1, std::nullopt); } status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session) { RpcOutgoingConnectionInit init; iovec iov{&init, sizeof(init)}; - if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK) + if (status_t status = rpcRec(connection, session, "connection init", &iov, 1, nullptr); + status != OK) return status; static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY)); @@ -448,20 +494,12 @@ status_t RpcState::getSessionId(const sp<RpcSession::RpcConnection>& connection, status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection, const sp<IBinder>& binder, uint32_t code, const Parcel& data, const sp<RpcSession>& session, Parcel* reply, uint32_t flags) { - if (!data.isForRpc()) { - ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code " - "%" PRIu32, - binder.get(), code); - return BAD_TYPE; - } - - if (data.objectsCount() != 0) { - ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p " - "code %" PRIu32, - &data, binder.get(), code); - return BAD_TYPE; + std::string errorMsg; + if (status_t status = validateParcel(session, data, &errorMsg); status != OK) { + ALOGE("Refusing to send RPC on binder %p code %" PRIu32 ": Parcel %p failed validation: %s", + binder.get(), code, &data, errorMsg.c_str()); + return status; } - uint64_t address; if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status; @@ -477,7 +515,7 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti uint64_t asyncNumber = 0; if (address != 0) { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(address); LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), @@ -493,14 +531,21 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti } } - LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) - - sizeof(RpcWireTransaction) < - data.dataSize(), - "Too much data %zu", data.dataSize()); + auto* rpcFields = data.maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + + Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(), + rpcFields->mObjectPositions.size()}; + uint32_t bodySize; + LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(), + &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), + "Too much data %zu", data.dataSize()); RpcWireHeader command{ .command = RPC_COMMAND_TRANSACT, - .bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()), + .bodySize = bodySize, }; RpcWireTransaction transaction{ @@ -508,6 +553,8 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .code = code, .flags = flags, .asyncNumber = asyncNumber, + // bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast<uint32_t>(data.dataSize()), }; constexpr size_t kWaitMaxUs = 1000000; @@ -516,31 +563,33 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti // Oneway calls have no sync point, so if many are sent before, whether this // is a twoway or oneway transaction, they may have filled up the socket. - // So, make sure we drain them before polling. - std::function<status_t()> drainRefs = [&] { - if (waitUs > kWaitLogUs) { - ALOGE("Cannot send command, trying to process pending refcounts. Waiting %zuus. Too " - "many oneway calls?", - waitUs); - } - - if (waitUs > 0) { - usleep(waitUs); - waitUs = std::min(kWaitMaxUs, waitUs * 2); - } else { - waitUs = 1; - } - - return drainCommands(connection, session, CommandType::CONTROL_ONLY); - }; + // So, make sure we drain them before polling iovec iovs[]{ {&command, sizeof(RpcWireHeader)}, {&transaction, sizeof(RpcWireTransaction)}, {const_cast<uint8_t*>(data.data()), data.dataSize()}, + objectTableSpan.toIovec(), }; - if (status_t status = - rpcSend(connection, session, "transaction", iovs, arraysize(iovs), drainRefs); + if (status_t status = rpcSend( + connection, session, "transaction", iovs, arraysize(iovs), + [&] { + if (waitUs > kWaitLogUs) { + ALOGE("Cannot send command, trying to process pending refcounts. Waiting " + "%zuus. Too many oneway calls?", + waitUs); + } + + if (waitUs > 0) { + usleep(waitUs); + waitUs = std::min(kWaitMaxUs, waitUs * 2); + } else { + waitUs = 1; + } + + return drainCommands(connection, session, CommandType::CONTROL_ONLY); + }, + rpcFields->mFds.get()); status != OK) { // TODO(b/167966510): need to undo onBinderLeaving - we know the // refcount isn't successfully transferred. @@ -560,54 +609,91 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti return waitForReply(connection, session, reply); } -static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount) { - (void)p; - delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data)); +static void cleanup_reply_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount) { + delete[] const_cast<uint8_t*>(data); (void)dataSize; LOG_ALWAYS_FATAL_IF(objects != nullptr); - LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount); + (void)objectsCount; } status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply) { + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; RpcWireHeader command; while (true) { iovec iov{&command, sizeof(command)}; - if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1); + if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1, + enableAncillaryFds(session->getFileDescriptorTransportMode()) + ? &ancillaryFds + : nullptr); status != OK) return status; if (command.command == RPC_COMMAND_REPLY) break; - if (status_t status = processCommand(connection, session, command, CommandType::ANY); + if (status_t status = processCommand(connection, session, command, CommandType::ANY, + std::move(ancillaryFds)); status != OK) return status; - } - CommandData data(command.bodySize); - if (!data.valid()) return NO_MEMORY; + // Reset to avoid spurious use-after-move warning from clang-tidy. + ancillaryFds = decltype(ancillaryFds)(); + } - iovec iov{data.data(), command.bodySize}; - if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK) - return status; + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); - if (command.bodySize < sizeof(RpcWireReply)) { + if (command.bodySize < rpcReplyWireSize) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!", sizeof(RpcWireReply), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data()); - if (rpcReply->status != OK) return rpcReply->status; - data.release(); - reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data), - nullptr, 0, cleanup_reply_data); + RpcWireReply rpcReply; + memset(&rpcReply, 0, sizeof(RpcWireReply)); // zero because of potential short read - reply->markForRpc(session); + CommandData data(command.bodySize - rpcReplyWireSize); + if (!data.valid()) return NO_MEMORY; - return OK; + iovec iovs[]{ + {&rpcReply, rpcReplyWireSize}, + {data.data(), data.size()}, + }; + if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr); + status != OK) + return status; + + if (rpcReply.status != OK) return rpcReply.status; + + Span<const uint8_t> parcelSpan = {data.data(), data.size()}; + Span<const uint32_t> objectTableSpan; + if (session->getProtocolVersion().value() >= + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + std::optional<Span<const uint8_t>> objectTableBytes = + parcelSpan.splitOff(rpcReply.parcelDataSize); + if (!objectTableBytes.has_value()) { + ALOGE("Parcel size larger than available bytes: %" PRId32 " vs %zu. Terminating!", + rpcReply.parcelDataSize, parcelSpan.byteSize()); + (void)session->shutdownAndWait(false); + return BAD_VALUE; + } + std::optional<Span<const uint32_t>> maybeSpan = + objectTableBytes->reinterpret<const uint32_t>(); + if (!maybeSpan.has_value()) { + ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32 + " sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!", + command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize, + objectTableBytes->size); + return BAD_VALUE; + } + objectTableSpan = *maybeSpan; + } + + data.release(); + return reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, + objectTableSpan.data, objectTableSpan.size, + std::move(ancillaryFds), cleanup_reply_data); } status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection, @@ -618,7 +704,7 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co }; { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(addr); LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), @@ -643,31 +729,32 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co .bodySize = sizeof(RpcDecStrong), }; iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}}; - return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs)); + return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt); } status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, CommandType type) { LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get()); + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; RpcWireHeader command; iovec iov{&command, sizeof(command)}; - if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1); + if (status_t status = + rpcRec(connection, session, "command header (for server)", &iov, 1, + enableAncillaryFds(session->getFileDescriptorTransportMode()) ? &ancillaryFds + : nullptr); status != OK) return status; - return processCommand(connection, session, command, type); + return processCommand(connection, session, command, type, std::move(ancillaryFds)); } status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, CommandType type) { - uint8_t buf; while (true) { - size_t num_bytes; - status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes); + status_t status = connection->rpcTransport->pollRead(); if (status == WOULD_BLOCK) break; if (status != OK) return status; - if (!num_bytes) break; status = getAndExecuteCommand(connection, session, type); if (status != OK) return status; @@ -675,28 +762,33 @@ status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection return OK; } -status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcWireHeader& command, - CommandType type) { +status_t RpcState::processCommand( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, CommandType type, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { +#ifdef BINDER_WITH_KERNEL_IPC IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull(); IPCThreadState::SpGuard spGuard{ .address = __builtin_frame_address(0), - .context = "processing binder RPC command", + .context = "processing binder RPC command (where RpcServer::setPerSessionRootObject is " + "used to distinguish callers)", }; const IPCThreadState::SpGuard* origGuard; if (kernelBinderState != nullptr) { origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard); } - ScopeGuard guardUnguard = [&]() { + + base::ScopeGuard guardUnguard = [&]() { if (kernelBinderState != nullptr) { kernelBinderState->restoreGetCallingSpGuard(origGuard); } }; +#endif // BINDER_WITH_KERNEL_IPC switch (command.command) { case RPC_COMMAND_TRANSACT: if (type != CommandType::ANY) return BAD_TYPE; - return processTransact(connection, session, command); + return processTransact(connection, session, command, std::move(ancillaryFds)); case RPC_COMMAND_DEC_STRONG: return processDecStrong(connection, session, command); } @@ -710,8 +802,10 @@ status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connectio (void)session->shutdownAndWait(false); return DEAD_OBJECT; } -status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcWireHeader& command) { +status_t RpcState::processTransact( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command); CommandData transactionData(command.bodySize); @@ -719,24 +813,26 @@ status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connecti return NO_MEMORY; } iovec iov{transactionData.data(), transactionData.size()}; - if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK) + if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1, nullptr); + status != OK) return status; - return processTransactInternal(connection, session, std::move(transactionData)); + return processTransactInternal(connection, session, std::move(transactionData), + std::move(ancillaryFds)); } -static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize, +static void do_nothing_to_transact_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { - (void)p; (void)data; (void)dataSize; (void)objects; (void)objectsCount; } -status_t RpcState::processTransactInternal(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - CommandData transactionData) { +status_t RpcState::processTransactInternal( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + CommandData transactionData, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { // for 'recursive' calls to this, we have already read and processed the // binder from the transaction data and taken reference counts into account, // so it is cached here. @@ -780,7 +876,7 @@ processTransactInternalTailCall: (void)session->shutdownAndWait(false); replyStatus = BAD_VALUE; } else if (oneway) { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it->second.binder.promote() != target) { ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr); @@ -791,6 +887,7 @@ processTransactInternalTailCall: it->second.asyncTodo.push(BinderNode::AsyncTodo{ .ref = target, .data = std::move(transactionData), + .ancillaryFds = std::move(ancillaryFds), .asyncNumber = transaction->asyncNumber, }); @@ -824,54 +921,85 @@ processTransactInternalTailCall: reply.markForRpc(session); if (replyStatus == OK) { + Span<const uint8_t> parcelSpan = {transaction->data, + transactionData.size() - + offsetof(RpcWireTransaction, data)}; + Span<const uint32_t> objectTableSpan; + if (session->getProtocolVersion().value() > + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + std::optional<Span<const uint8_t>> objectTableBytes = + parcelSpan.splitOff(transaction->parcelDataSize); + if (!objectTableBytes.has_value()) { + ALOGE("Parcel size (%" PRId32 ") greater than available bytes (%zu). Terminating!", + transaction->parcelDataSize, parcelSpan.byteSize()); + (void)session->shutdownAndWait(false); + return BAD_VALUE; + } + std::optional<Span<const uint32_t>> maybeSpan = + objectTableBytes->reinterpret<const uint32_t>(); + if (!maybeSpan.has_value()) { + ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu " + "sizeofHeader=%zu parcelSize=%" PRId32 + " objectTableBytesSize=%zu. Terminating!", + transactionData.size(), sizeof(RpcWireTransaction), + transaction->parcelDataSize, objectTableBytes->size); + return BAD_VALUE; + } + objectTableSpan = *maybeSpan; + } + Parcel data; // transaction->data is owned by this function. Parcel borrows this data and // only holds onto it for the duration of this function call. Parcel will be // deleted before the 'transactionData' object. - data.ipcSetDataReference(transaction->data, - transactionData.size() - offsetof(RpcWireTransaction, data), - nullptr /*object*/, 0 /*objectCount*/, - do_nothing_to_transact_data); - data.markForRpc(session); - if (target) { - bool origAllowNested = connection->allowNested; - connection->allowNested = !oneway; + replyStatus = + data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, + objectTableSpan.data, objectTableSpan.size, + std::move(ancillaryFds), do_nothing_to_transact_data); + // Reset to avoid spurious use-after-move warning from clang-tidy. + ancillaryFds = std::remove_reference<decltype(ancillaryFds)>::type(); - replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); + if (replyStatus == OK) { + if (target) { + bool origAllowNested = connection->allowNested; + connection->allowNested = !oneway; - connection->allowNested = origAllowNested; - } else { - LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { - replyStatus = reply.writeInt32(session->getMaxIncomingThreads()); - break; - } - case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { - // for client connections, this should always report the value - // originally returned from the server, so this is asserting - // that it exists - replyStatus = reply.writeByteVector(session->mId); - break; - } - default: { - sp<RpcServer> server = session->server(); - if (server) { - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_ROOT: { - sp<IBinder> root = session->mSessionSpecificRootObject - ?: server->getRootObject(); - replyStatus = reply.writeStrongBinder(root); - break; - } - default: { - replyStatus = UNKNOWN_TRANSACTION; + connection->allowNested = origAllowNested; + } else { + LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { + replyStatus = reply.writeInt32(session->getMaxIncomingThreads()); + break; + } + case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { + // for client connections, this should always report the value + // originally returned from the server, so this is asserting + // that it exists + replyStatus = reply.writeByteVector(session->mId); + break; + } + default: { + sp<RpcServer> server = session->server(); + if (server) { + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_ROOT: { + sp<IBinder> root = session->mSessionSpecificRootObject + ?: server->getRootObject(); + replyStatus = reply.writeStrongBinder(root); + break; + } + default: { + replyStatus = UNKNOWN_TRANSACTION; + } } + } else { + ALOGE("Special command sent, but no server object attached."); } - } else { - ALOGE("Special command sent, but no server object attached."); } } } @@ -897,7 +1025,7 @@ processTransactInternalTailCall: // downside: asynchronous transactions may drown out synchronous // transactions. { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); // last refcount dropped after this transaction happened if (it == mNodeForAddress.end()) return OK; @@ -920,6 +1048,7 @@ processTransactInternalTailCall: // reset up arguments transactionData = std::move(todo.data); + ancillaryFds = std::move(todo.ancillaryFds); LOG_ALWAYS_FATAL_IF(target != todo.ref, "async list should be associated with a binder"); @@ -943,49 +1072,69 @@ processTransactInternalTailCall: replyStatus = flushExcessBinderRefs(session, addr, target); } - LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) - - sizeof(RpcWireReply) < - reply.dataSize(), - "Too much data for reply %zu", reply.dataSize()); + std::string errorMsg; + if (status_t status = validateParcel(session, reply, &errorMsg); status != OK) { + ALOGE("Reply Parcel failed validation: %s", errorMsg.c_str()); + // Forward the error to the client of the transaction. + reply.freeData(); + reply.markForRpc(session); + replyStatus = status; + } + + auto* rpcFields = reply.maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); + + Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(), + rpcFields->mObjectPositions.size()}; + + uint32_t bodySize; + LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), + "Too much data for reply %zu", reply.dataSize()); RpcWireHeader cmdReply{ .command = RPC_COMMAND_REPLY, - .bodySize = static_cast<uint32_t>(sizeof(RpcWireReply) + reply.dataSize()), + .bodySize = bodySize, }; RpcWireReply rpcReply{ .status = replyStatus, + // NOTE: Not necessarily written to socket depending on session + // version. + // NOTE: bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast<uint32_t>(reply.dataSize()), + .reserved = {0, 0, 0}, }; - iovec iovs[]{ {&cmdReply, sizeof(RpcWireHeader)}, - {&rpcReply, sizeof(RpcWireReply)}, + {&rpcReply, rpcReplyWireSize}, {const_cast<uint8_t*>(reply.data()), reply.dataSize()}, + objectTableSpan.toIovec(), }; - return rpcSend(connection, session, "reply", iovs, arraysize(iovs)); + return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt, + rpcFields->mFds.get()); } status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const RpcWireHeader& command) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command); - CommandData commandData(command.bodySize); - if (!commandData.valid()) { - return NO_MEMORY; - } - iovec iov{commandData.data(), commandData.size()}; - if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK) - return status; - if (command.bodySize != sizeof(RpcDecStrong)) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcDecStrong. Terminating!", sizeof(RpcDecStrong), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcDecStrong* body = reinterpret_cast<RpcDecStrong*>(commandData.data()); - uint64_t addr = RpcWireAddress::toRaw(body->address); - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcDecStrong body; + iovec iov{&body, sizeof(RpcDecStrong)}; + if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1, nullptr); + status != OK) + return status; + + uint64_t addr = RpcWireAddress::toRaw(body.address); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr); @@ -1002,19 +1151,19 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return BAD_VALUE; } - if (it->second.timesSent < body->amount) { + if (it->second.timesSent < body.amount) { ALOGE("Record of sending binder %zu times, but requested decStrong for %" PRIu64 " of %u", - it->second.timesSent, addr, body->amount); + it->second.timesSent, addr, body.amount); return OK; } LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64, addr); - LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body->amount, + LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body.amount, it->second.timesSent); - it->second.timesSent -= body->amount; + it->second.timesSent -= body.amount; sp<IBinder> tempHold = tryEraseNode(it); _l.unlock(); tempHold = nullptr; // destructor may make binder calls on this session @@ -1022,6 +1171,64 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return OK; } +status_t RpcState::validateParcel(const sp<RpcSession>& session, const Parcel& parcel, + std::string* errorMsg) { + auto* rpcFields = parcel.maybeRpcFields(); + if (rpcFields == nullptr) { + *errorMsg = "Parcel not crafted for RPC call"; + return BAD_TYPE; + } + + if (rpcFields->mSession != session) { + *errorMsg = "Parcel's session doesn't match"; + return BAD_TYPE; + } + + uint32_t protocolVersion = session->getProtocolVersion().value(); + if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE && + !rpcFields->mObjectPositions.empty()) { + *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version " + "(%" PRIu32 ") is too old, must be at least %" PRIu32, + protocolVersion, + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE); + return BAD_VALUE; + } + + if (rpcFields->mFds && !rpcFields->mFds->empty()) { + switch (session->getFileDescriptorTransportMode()) { + case RpcSession::FileDescriptorTransportMode::NONE: + *errorMsg = + "Parcel has file descriptors, but no file descriptor transport is enabled"; + return FDS_NOT_ALLOWED; + case RpcSession::FileDescriptorTransportMode::UNIX: { + constexpr size_t kMaxFdsPerMsg = 253; + if (rpcFields->mFds->size() > kMaxFdsPerMsg) { + *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix " + "domain socket: %zu (max is %zu)", + rpcFields->mFds->size(), kMaxFdsPerMsg); + return BAD_VALUE; + } + break; + } + case RpcSession::FileDescriptorTransportMode::TRUSTY: { + // Keep this in sync with trusty_ipc.h!!! + // We could import that file here on Trusty, but it's not + // 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); + return BAD_VALUE; + } + break; + } + } + } + + return OK; +} + sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) { sp<IBinder> ref; diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index f4a08942b3..ac8658540e 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -19,6 +19,7 @@ #include <binder/IBinder.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> +#include <binder/RpcThreads.h> #include <map> #include <optional> @@ -139,6 +140,11 @@ public: */ [[nodiscard]] status_t flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t address, const sp<IBinder>& binder); + /** + * Called when the RpcSession is shutdown. + * Send obituaries for each known remote binder with this session. + */ + [[nodiscard]] status_t sendObituaries(const sp<RpcSession>& session); size_t countBinders(); void dump(); @@ -178,28 +184,39 @@ private: size_t mSize; }; - [[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, - int niovs, const std::function<status_t()>& altPoll = nullptr); - [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, - int niovs); + [[nodiscard]] status_t rpcSend( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = + nullptr); + [[nodiscard]] status_t rpcRec( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr); [[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply); - [[nodiscard]] status_t processCommand(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - const RpcWireHeader& command, CommandType type); - [[nodiscard]] status_t processTransact(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - const RpcWireHeader& command); - [[nodiscard]] status_t processTransactInternal(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - CommandData transactionData); + [[nodiscard]] status_t processCommand( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, CommandType type, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); + [[nodiscard]] status_t processTransact( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); + [[nodiscard]] status_t processTransactInternal( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + CommandData transactionData, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); [[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const RpcWireHeader& command); + // Whether `parcel` is compatible with `session`. + [[nodiscard]] static status_t validateParcel(const sp<RpcSession>& session, + const Parcel& parcel, std::string* errorMsg); + struct BinderNode { // Two cases: // A - local binder we are serving @@ -233,6 +250,7 @@ private: struct AsyncTodo { sp<IBinder> ref; CommandData data; + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; uint64_t asyncNumber = 0; bool operator<(const AsyncTodo& o) const { @@ -246,6 +264,8 @@ private: // // (no additional data specific to remote binders) + + std::string toString() const; }; // checks if there is any reference left to a node and erases it. If erase @@ -257,7 +277,7 @@ private: // false - session shutdown, halt [[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node); - std::mutex mNodeMutex; + RpcMutex mNodeMutex; bool mTerminated = false; uint32_t mNextId = 0; // binders known by both sides of a session diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index 7cfc78082e..cd067bfee7 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -18,11 +18,14 @@ #include <log/log.h> #include <poll.h> +#include <stddef.h> #include <binder/RpcTransportRaw.h> #include "FdTrigger.h" +#include "OS.h" #include "RpcState.h" +#include "RpcTransportUtils.h" namespace android { @@ -31,135 +34,64 @@ namespace { // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { public: - explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {} - status_t peek(void* buf, size_t size, size_t* out_size) override { - ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK)); + explicit RpcTransportRaw(android::RpcTransportFd socket) : mSocket(std::move(socket)) {} + status_t pollRead(void) override { + uint8_t buf; + ssize_t ret = TEMP_FAILURE_RETRY( + ::recv(mSocket.fd.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT)); if (ret < 0) { int savedErrno = errno; if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { return WOULD_BLOCK; } - LOG_RPC_DETAIL("RpcTransport peek(): %s", strerror(savedErrno)); + LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno)); return -savedErrno; - } - - *out_size = static_cast<size_t>(ret); - return OK; - } - - template <typename SendOrReceive> - status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, int niovs, - SendOrReceive sendOrReceiveFun, const char* funName, - int16_t event, const std::function<status_t()>& altPoll) { - MAYBE_WAIT_IN_FLAKE_MODE; - - if (niovs < 0) { - return BAD_VALUE; - } - - // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we - // may never know we should be shutting down. - if (fdTrigger->isTriggered()) { + } else if (ret == 0) { return DEAD_OBJECT; } - // If iovs has one or more empty vectors at the end and - // we somehow advance past all the preceding vectors and - // pass some or all of the empty ones to sendmsg/recvmsg, - // the call will return processSize == 0. In that case - // we should be returning OK but instead return DEAD_OBJECT. - // To avoid this problem, we make sure here that the last - // vector at iovs[niovs - 1] has a non-zero length. - while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { - niovs--; - } - if (niovs == 0) { - // The vectors are all empty, so we have nothing to send. - return OK; - } - - bool havePolled = false; - while (true) { - msghdr msg{ - .msg_iov = iovs, - // posix uses int, glibc uses size_t. niovs is a - // non-negative int and can be cast to either. - .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), - }; - ssize_t processSize = - TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL)); - - if (processSize < 0) { - int savedErrno = errno; - - // Still return the error on later passes, since it would expose - // a problem with polling - if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) { - LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno)); - return -savedErrno; - } - } else if (processSize == 0) { - return DEAD_OBJECT; - } else { - while (processSize > 0 && niovs > 0) { - auto& iov = iovs[0]; - if (static_cast<size_t>(processSize) < iov.iov_len) { - // Advance the base of the current iovec - iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; - iov.iov_len -= processSize; - break; - } - - // The current iovec was fully written - processSize -= iov.iov_len; - iovs++; - niovs--; - } - if (niovs == 0) { - LOG_ALWAYS_FATAL_IF(processSize > 0, - "Reached the end of iovecs " - "with %zd bytes remaining", - processSize); - return OK; - } - } - - if (altPoll) { - if (status_t status = altPoll(); status != OK) return status; - if (fdTrigger->isTriggered()) { - return DEAD_OBJECT; - } - } else { - if (status_t status = fdTrigger->triggerablePoll(mSocket.get(), event); - status != OK) - return status; - if (!havePolled) havePolled = true; - } - } + return OK; } - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT, + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + bool sentFds = false; + auto send = [&](iovec* iovs, int niovs) -> ssize_t { + ssize_t ret = + sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds); + sentFds |= ret > 0; + return ret; + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT, altPoll); } - status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN, + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { + auto recv = [&](iovec* iovs, int niovs) -> ssize_t { + return receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds); + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll); } + virtual bool isWaiting() { return mSocket.isInPollingState(); } + private: - base::unique_fd mSocket; + android::RpcTransportFd mSocket; }; // RpcTransportCtx with TLS disabled. class RpcTransportCtxRaw : public RpcTransportCtx { public: - std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const { - return std::make_unique<RpcTransportRaw>(std::move(fd)); + std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, FdTrigger*) const { + return std::make_unique<RpcTransportRaw>(std::move(socket)); } std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } }; diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp new file mode 100644 index 0000000000..8b3ddfbd6f --- /dev/null +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcTransportTipcAndroid" + +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcAndroid.h> +#include <log/log.h> +#include <poll.h> +#include <trusty/tipc.h> + +#include "FdTrigger.h" +#include "RpcState.h" +#include "RpcTransportUtils.h" + +using android::base::Error; +using android::base::Result; + +namespace android { + +namespace { + +// RpcTransport for writing Trusty IPC clients in Android. +class RpcTransportTipcAndroid : public RpcTransport { +public: + explicit RpcTransportTipcAndroid(android::RpcTransportFd socket) : mSocket(std::move(socket)) {} + + status_t pollRead() override { + if (mReadBufferPos < mReadBufferSize) { + // We have more data in the read buffer + return OK; + } + + // Trusty IPC device is not a socket, so MSG_PEEK is not available + pollfd pfd{.fd = mSocket.fd.get(), .events = static_cast<int16_t>(POLLIN), .revents = 0}; + ssize_t ret = TEMP_FAILURE_RETRY(::poll(&pfd, 1, 0)); + if (ret < 0) { + int savedErrno = errno; + if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { + return WOULD_BLOCK; + } + + LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno)); + return -savedErrno; + } + + if (pfd.revents & POLLNVAL) { + return BAD_VALUE; + } + if (pfd.revents & POLLERR) { + return DEAD_OBJECT; + } + if (pfd.revents & POLLIN) { + // Copied from FdTrigger.cpp: Even though POLLHUP may also be set, + // treat it as a success condition to ensure data is drained. + return OK; + } + if (pfd.revents & POLLHUP) { + return DEAD_OBJECT; + } + + return WOULD_BLOCK; + } + + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { + // TODO: send ancillaryFds. For now, we just abort if anyone tries + // to send any. + LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(), + "File descriptors are not supported on Trusty yet"); + return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0)); + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn, "tipc_send", + POLLOUT, altPoll); + } + + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) + override { + auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t { + // Fill the read buffer at most once per readFn call, then try to + // return as much of it as possible. If the input iovecs are spread + // across multiple messages that require multiple fillReadBuffer + // calls, we expect the caller to advance the iovecs past the first + // read and call readFn as many times as needed to get all the data + status_t ret = fillReadBuffer(); + if (ret != OK) { + // We need to emulate a Linux read call, which sets errno on + // error and returns -1 + errno = -ret; + return -1; + } + + ssize_t processSize = 0; + for (size_t i = 0; i < niovs && mReadBufferPos < mReadBufferSize; i++) { + auto& iov = iovs[i]; + size_t numBytes = std::min(iov.iov_len, mReadBufferSize - mReadBufferPos); + memcpy(iov.iov_base, mReadBuffer.get() + mReadBufferPos, numBytes); + mReadBufferPos += numBytes; + processSize += numBytes; + } + + return processSize; + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, readFn, "read", POLLIN, + altPoll); + } + + bool isWaiting() override { return mSocket.isInPollingState(); } + +private: + status_t fillReadBuffer() { + if (mReadBufferPos < mReadBufferSize) { + return OK; + } + + if (!mReadBuffer) { + // Guarantee at least kDefaultBufferSize bytes + mReadBufferCapacity = std::max(mReadBufferCapacity, kDefaultBufferSize); + mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]); + if (!mReadBuffer) { + return NO_MEMORY; + } + } + + // Reset the size and position in case we have to exit with an error. + // After we read a message into the buffer, we update the size + // with the actual value. + mReadBufferPos = 0; + mReadBufferSize = 0; + + while (true) { + ssize_t processSize = TEMP_FAILURE_RETRY( + read(mSocket.fd.get(), mReadBuffer.get(), mReadBufferCapacity)); + if (processSize == 0) { + return DEAD_OBJECT; + } else if (processSize < 0) { + int savedErrno = errno; + if (savedErrno == EMSGSIZE) { + // Buffer was too small, double it and retry + if (__builtin_mul_overflow(mReadBufferCapacity, 2, &mReadBufferCapacity)) { + return NO_MEMORY; + } + mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]); + if (!mReadBuffer) { + return NO_MEMORY; + } + continue; + } else { + LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno)); + return -savedErrno; + } + } else { + mReadBufferSize = static_cast<size_t>(processSize); + return OK; + } + } + } + + RpcTransportFd mSocket; + + // For now, we copy all the input data into a temporary buffer because + // we might get multiple interruptableReadFully calls per message, but + // the tipc device only allows one read call. We read every message into + // this temporary buffer, then return pieces of it from our method. + // + // The special transaction GET_MAX_THREADS takes 40 bytes, so the default + // size should start pretty high. + static constexpr size_t kDefaultBufferSize = 64; + std::unique_ptr<uint8_t[]> mReadBuffer; + size_t mReadBufferPos = 0; + size_t mReadBufferSize = 0; + size_t mReadBufferCapacity = 0; +}; + +// RpcTransportCtx for Trusty. +class RpcTransportCtxTipcAndroid : public RpcTransportCtx { +public: + std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd fd, + FdTrigger*) const override { + return std::make_unique<RpcTransportTipcAndroid>(std::move(fd)); + } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } +}; + +} // namespace + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const { + return std::make_unique<RpcTransportCtxTipcAndroid>(); +} + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newClientCtx() const { + return std::make_unique<RpcTransportCtxTipcAndroid>(); +} + +const char* RpcTransportCtxFactoryTipcAndroid::toCString() const { + return "trusty"; +} + +std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcAndroid::make() { + return std::unique_ptr<RpcTransportCtxFactoryTipcAndroid>( + new RpcTransportCtxFactoryTipcAndroid()); +} + +} // namespace android diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index bc68c379ce..3e98ecca9b 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -181,9 +181,10 @@ 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(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger, - const char* fnString, int additionalEvent, - const std::function<status_t()>& altPoll) { + status_t pollForSslError( + const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, + const char* fnString, int additionalEvent, + const std::optional<android::base::function_ref<status_t()>>& altPoll) { switch (sslError) { case SSL_ERROR_WANT_READ: return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); @@ -197,11 +198,12 @@ public: private: bool mHandled = false; - status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger, - const char* fnString, const std::function<status_t()>& altPoll) { + status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger, + const char* fnString, + const std::optional<android::base::function_ref<status_t()>>& altPoll) { status_t ret; if (altPoll) { - ret = altPoll(); + ret = (*altPoll)(); if (fdTrigger->isTriggered()) ret = DEAD_OBJECT; } else { ret = fdTrigger->triggerablePoll(fd, event); @@ -275,23 +277,30 @@ private: class RpcTransportTls : public RpcTransport { public: - RpcTransportTls(android::base::unique_fd socket, Ssl ssl) + RpcTransportTls(RpcTransportFd socket, Ssl ssl) : mSocket(std::move(socket)), mSsl(std::move(ssl)) {} - status_t peek(void* buf, size_t size, size_t* out_size) override; - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override; - status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override; + status_t pollRead(void) override; + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override; + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; + + bool isWaiting() { return mSocket.isInPollingState(); }; private: - android::base::unique_fd mSocket; + android::RpcTransportFd mSocket; Ssl mSsl; }; // Error code is errno. -status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) { - size_t todo = std::min<size_t>(size, std::numeric_limits<int>::max()); - auto [ret, errorQueue] = mSsl.call(SSL_peek, buf, static_cast<int>(todo)); +status_t RpcTransportTls::pollRead(void) { + uint8_t buf; + auto [ret, errorQueue] = mSsl.call(SSL_peek, &buf, sizeof(buf)); if (ret < 0) { int err = mSsl.getError(ret); if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { @@ -304,12 +313,15 @@ status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) { } errorQueue.clear(); LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret); - *out_size = static_cast<size_t>(ret); return OK; } -status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) { +status_t RpcTransportTls::interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + (void)ancillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; @@ -340,7 +352,7 @@ status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* i int sslError = mSsl.getError(writeSize); // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be // triggerablePoll()-ed. Then additionalEvent is no longer necessary. - status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, + status_t pollStatus = errorQueue.pollForSslError(mSocket, sslError, fdTrigger, "SSL_write", POLLIN, altPoll); if (pollStatus != OK) return pollStatus; // Do not advance buffer. Try SSL_write() again. @@ -350,8 +362,12 @@ status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* i return OK; } -status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) { +status_t RpcTransportTls::interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + (void)ancillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; @@ -384,7 +400,7 @@ status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* io return DEAD_OBJECT; } int sslError = mSsl.getError(readSize); - status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, + status_t pollStatus = errorQueue.pollForSslError(mSocket, sslError, fdTrigger, "SSL_read", 0, altPoll); if (pollStatus != OK) return pollStatus; // Do not advance buffer. Try SSL_read() again. @@ -395,8 +411,8 @@ status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* io } // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|. -bool setFdAndDoHandshake(Ssl* ssl, android::base::borrowed_fd fd, FdTrigger* fdTrigger) { - bssl::UniquePtr<BIO> bio = newSocketBio(fd); +bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, FdTrigger* fdTrigger) { + bssl::UniquePtr<BIO> bio = newSocketBio(socket.fd); TEST_AND_RETURN(false, bio != nullptr); auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get()); (void)bio.release(); // SSL_set_bio takes ownership. @@ -416,8 +432,8 @@ bool setFdAndDoHandshake(Ssl* ssl, android::base::borrowed_fd fd, FdTrigger* fdT return false; } int sslError = ssl->getError(ret); - status_t pollStatus = - errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake", 0, {}); + status_t pollStatus = errorQueue.pollForSslError(socket, sslError, fdTrigger, + "SSL_do_handshake", 0, std::nullopt); if (pollStatus != OK) return false; } } @@ -428,7 +444,7 @@ public: typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>> static std::unique_ptr<RpcTransportCtxTls> create( std::shared_ptr<RpcCertificateVerifier> verifier, RpcAuth* auth); - std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, + std::unique_ptr<RpcTransport> newTransport(RpcTransportFd fd, FdTrigger* fdTrigger) const override; std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override; @@ -499,15 +515,15 @@ std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create( return ret; } -std::unique_ptr<RpcTransport> RpcTransportCtxTls::newTransport(android::base::unique_fd fd, +std::unique_ptr<RpcTransport> RpcTransportCtxTls::newTransport(android::RpcTransportFd socket, FdTrigger* fdTrigger) const { bssl::UniquePtr<SSL> ssl(SSL_new(mCtx.get())); TEST_AND_RETURN(nullptr, ssl != nullptr); Ssl wrapped(std::move(ssl)); preHandshake(&wrapped); - TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, fd, fdTrigger)); - return std::make_unique<RpcTransportTls>(std::move(fd), std::move(wrapped)); + TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, socket, fdTrigger)); + return std::make_unique<RpcTransportTls>(std::move(socket), std::move(wrapped)); } class RpcTransportCtxTlsServer : public RpcTransportCtxTls { diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h new file mode 100644 index 0000000000..32f0db805d --- /dev/null +++ b/libs/binder/RpcTransportUtils.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <android-base/unique_fd.h> +#include <poll.h> + +#include "FdTrigger.h" +#include "RpcState.h" + +namespace android { + +template <typename SendOrReceive> +status_t interruptableReadOrWrite( + const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs, + SendOrReceive sendOrReceiveFun, const char* funName, int16_t event, + const std::optional<android::base::function_ref<status_t()>>& altPoll) { + MAYBE_WAIT_IN_FLAKE_MODE; + + if (niovs < 0) { + return BAD_VALUE; + } + + // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we + // may never know we should be shutting down. + if (fdTrigger->isTriggered()) { + return DEAD_OBJECT; + } + + // If iovs has one or more empty vectors at the end and + // we somehow advance past all the preceding vectors and + // pass some or all of the empty ones to sendmsg/recvmsg, + // the call will return processSize == 0. In that case + // we should be returning OK but instead return DEAD_OBJECT. + // To avoid this problem, we make sure here that the last + // vector at iovs[niovs - 1] has a non-zero length. + while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { + niovs--; + } + if (niovs == 0) { + // The vectors are all empty, so we have nothing to send. + return OK; + } + + bool havePolled = false; + while (true) { + ssize_t processSize = sendOrReceiveFun(iovs, niovs); + if (processSize < 0) { + int savedErrno = errno; + + // Still return the error on later passes, since it would expose + // a problem with polling + if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) { + LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno)); + return -savedErrno; + } + } else if (processSize == 0) { + return DEAD_OBJECT; + } else { + while (processSize > 0 && niovs > 0) { + auto& iov = iovs[0]; + if (static_cast<size_t>(processSize) < iov.iov_len) { + // Advance the base of the current iovec + iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; + iov.iov_len -= processSize; + break; + } + + // The current iovec was fully written + processSize -= iov.iov_len; + iovs++; + niovs--; + } + if (niovs == 0) { + LOG_ALWAYS_FATAL_IF(processSize > 0, + "Reached the end of iovecs " + "with %zd bytes remaining", + processSize); + return OK; + } + } + + if (altPoll) { + if (status_t status = (*altPoll)(); status != OK) return status; + if (fdTrigger->isTriggered()) { + return DEAD_OBJECT; + } + } else { + if (status_t status = fdTrigger->triggerablePoll(socket, event); status != OK) + return status; + if (!havePolled) havePolled = true; + } + } +} + +} // namespace android diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp new file mode 100644 index 0000000000..3b53b05991 --- /dev/null +++ b/libs/binder/RpcTrusty.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcTrusty" + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcAndroid.h> +#include <trusty/tipc.h> + +namespace android { + +using android::base::unique_fd; + +sp<RpcSession> RpcTrustyConnectWithSessionInitializer( + const char* device, const char* port, + std::function<void(sp<RpcSession>&)> sessionInitializer) { + auto session = RpcSession::make(RpcTransportCtxFactoryTipcAndroid::make()); + // using the callback to initialize the session + sessionInitializer(session); + auto request = [=] { + int tipcFd = tipc_connect(device, port); + if (tipcFd < 0) { + LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd; + return unique_fd(); + } + return unique_fd(tipcFd); + }; + if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { + LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str(); + return nullptr; + } + return session; +} + +sp<IBinder> RpcTrustyConnect(const char* device, const char* port) { + auto session = RpcTrustyConnectWithSessionInitializer(device, port, [](auto) {}); + return session->getRootObject(); +} + +} // namespace android diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h index 171550e620..ff1b01a213 100644 --- a/libs/binder/RpcWireFormat.h +++ b/libs/binder/RpcWireFormat.h @@ -45,7 +45,8 @@ static_assert(sizeof(RpcWireAddress) == sizeof(uint64_t)); struct RpcConnectionHeader { uint32_t version; // maximum supported by caller uint8_t options; - uint8_t reservered[9]; + uint8_t fileDescriptorTransportMode; + uint8_t reservered[8]; // Follows is sessionIdSize bytes. // if size is 0, this is requesting a new session. uint16_t sessionIdSize; @@ -108,6 +109,10 @@ enum : uint32_t { // serialization is like: // |RpcWireHeader|struct desginated by 'command'| (over and over again) +// +// When file descriptors are included in out-of-band data (e.g. in unix domain +// sockets), they are always paired with the RpcWireHeader bytes of the +// transaction or reply the file descriptors belong to. struct RpcWireHeader { uint32_t command; // RPC_COMMAND_* @@ -131,7 +136,10 @@ struct RpcWireTransaction { uint64_t asyncNumber; - uint32_t reserved[4]; + // The size of the Parcel data directly following RpcWireTransaction. + uint32_t parcelDataSize; + + uint32_t reserved[3]; uint8_t data[]; }; @@ -139,9 +147,23 @@ static_assert(sizeof(RpcWireTransaction) == 40); struct RpcWireReply { int32_t status; // transact return - uint8_t data[]; + + // -- Fields below only transmitted starting at protocol version 1 -- + + // The size of the Parcel data directly following RpcWireReply. + uint32_t parcelDataSize; + + uint32_t reserved[3]; + + // Byte size of RpcWireReply in the wire protocol. + static size_t wireSize(uint32_t protocolVersion) { + if (protocolVersion == 0) { + return sizeof(int32_t); + } + return sizeof(RpcWireReply); + } }; -static_assert(sizeof(RpcWireReply) == 4); +static_assert(sizeof(RpcWireReply) == 20); #pragma clang diagnostic pop diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index 83b97d04c6..dba65878fb 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -139,6 +139,9 @@ status_t Status::readFromParcel(const Parcel& parcel) { mMessage = String8(message.value_or(String16())); // Skip over the remote stack trace data + const size_t remote_start = parcel.dataPosition(); + // Get available size before reading more + const size_t remote_avail = parcel.dataAvail(); int32_t remote_stack_trace_header_size; status = parcel.readInt32(&remote_stack_trace_header_size); if (status != OK) { @@ -146,13 +149,16 @@ status_t Status::readFromParcel(const Parcel& parcel) { return status; } if (remote_stack_trace_header_size < 0 || - static_cast<size_t>(remote_stack_trace_header_size) > parcel.dataAvail()) { + static_cast<size_t>(remote_stack_trace_header_size) > remote_avail) { android_errorWriteLog(0x534e4554, "132650049"); setFromStatusT(UNKNOWN_ERROR); return UNKNOWN_ERROR; } - parcel.setDataPosition(parcel.dataPosition() + remote_stack_trace_header_size); + + if (remote_stack_trace_header_size != 0) { + parcel.setDataPosition(remote_start + remote_stack_trace_header_size); + } if (mException == EX_SERVICE_SPECIFIC) { status = parcel.readInt32(&mErrorCode); diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index ebb0d2731c..1488400007 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -28,9 +28,6 @@ "name": "binderLibTest" }, { - "name": "binderRpcTest" - }, - { "name": "binderStabilityTest" }, { @@ -55,19 +52,19 @@ "name": "memunreachable_binder_test" }, { + "name": "resolv_integration_test" + }, + { "name": "libbinderthreadstateutils_test" }, { "name": "CtsOsTestCases", "options": [ { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "include-filter": "android.os.cts.BinderTest" }, { - "exclude-filter": "android.os.cts.BuildTest#testSdkInt" - }, - { - "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage" + "include-filter": "android.os.cts.ParcelTest" } ] }, @@ -83,5 +80,44 @@ { "name": "rustBinderSerializationTest" } + ], + "presubmit-large": [ + { + "name": "binderRpcTest" + }, + { + "name": "CtsRootRollbackManagerHostTestCases" + }, + { + "name": "StagedRollbackTest" + }, + { + "name": "binderRpcTestNoKernel" + }, + { + "name": "binderRpcTestSingleThreaded" + }, + { + "name": "binderRpcTestSingleThreadedNoKernel" + } + ], + "hwasan-presubmit": [ + { + "name": "binderLibTest" + } + ], + "kernel-presubmit": [ + { + "name": "binderDriverInterfaceTest" + }, + { + "name": "binderLibTest" + }, + { + "name": "binderSafeInterfaceTest" + }, + { + "name": "memunreachable_binder_test" + } ] } diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp index a0ade50efb..5dd1f902f8 100644 --- a/libs/binder/TextOutput.cpp +++ b/libs/binder/TextOutput.cpp @@ -39,11 +39,10 @@ TextOutput::~TextOutput() { static void textOutputPrinter(void* cookie, const char* txt) { - ((TextOutput*)cookie)->print(txt, strlen(txt)); + ((std::ostream*)cookie)->write(txt, strlen(txt)); } -TextOutput& operator<<(TextOutput& to, const TypeCode& val) -{ +std::ostream& operator<<(std::ostream& to, const TypeCode& val) { printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); return to; } @@ -61,8 +60,7 @@ HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) else mAlignment = 1; } -TextOutput& operator<<(TextOutput& to, const HexDump& val) -{ +std::ostream& operator<<(std::ostream& to, const HexDump& val) { printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), val.singleLineCutoff(), val.alignment(), val.carrayStyle(), textOutputPrinter, (void*)&to); diff --git a/libs/binder/Trace.cpp b/libs/binder/Trace.cpp new file mode 100644 index 0000000000..1ebfa1a165 --- /dev/null +++ b/libs/binder/Trace.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/Trace.h> +#include <cutils/trace.h> + +namespace android { +namespace binder { + +void atrace_begin(uint64_t tag, const char* name) { + ::atrace_begin(tag, name); +} + +void atrace_end(uint64_t tag) { + ::atrace_end(tag); +} + +} // namespace binder +} // namespace android diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp index d2a5be1102..0314b0fea7 100644 --- a/libs/binder/Utils.cpp +++ b/libs/binder/Utils.cpp @@ -18,24 +18,10 @@ #include <string.h> -using android::base::ErrnoError; -using android::base::Result; - namespace android { void zeroMemory(uint8_t* data, size_t size) { memset(data, 0, size); } -Result<void> setNonBlocking(android::base::borrowed_fd fd) { - int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); - if (flags == -1) { - return ErrnoError() << "Could not get flags for fd"; - } - if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { - return ErrnoError() << "Could not set non-blocking flag for fd"; - } - return {}; -} - } // namespace android diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index ff2fad8834..e04199c75a 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -14,12 +14,13 @@ * limitations under the License. */ -#include <cstdint> #include <stddef.h> +#include <sys/uio.h> +#include <cstdint> +#include <optional> -#include <android-base/result.h> -#include <android-base/unique_fd.h> #include <log/log.h> +#include <utils/Errors.h> #define TEST_AND_RETURN(value, expr) \ do { \ @@ -34,6 +35,39 @@ namespace android { // avoid optimizations void zeroMemory(uint8_t* data, size_t size); -android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); +// View of contiguous sequence. Similar to std::span. +template <typename T> +struct Span { + T* data = nullptr; + size_t size = 0; + + size_t byteSize() { return size * sizeof(T); } + + iovec toIovec() { return {const_cast<std::remove_const_t<T>*>(data), byteSize()}; } + + // Truncates `this` to a length of `offset` and returns a span with the + // remainder. + // + // `std::nullopt` iff offset > size. + std::optional<Span<T>> splitOff(size_t offset) { + if (offset > size) { + return std::nullopt; + } + Span<T> rest = {data + offset, size - offset}; + size = offset; + return rest; + } + + // Returns nullopt if the byte size of `this` isn't evenly divisible by sizeof(U). + template <typename U> + std::optional<Span<U>> reinterpret() const { + // Only allow casting from bytes for simplicity. + static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>); + if (size % sizeof(U) != 0) { + return std::nullopt; + } + return Span<U>{reinterpret_cast<U*>(data), size / sizeof(U)}; + } +}; } // namespace android diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl index 5880c0a1bd..0fb1615391 100644 --- a/libs/binder/aidl/android/os/IServiceManager.aidl +++ b/libs/binder/aidl/android/os/IServiceManager.aidl @@ -114,6 +114,12 @@ interface IServiceManager { @nullable @utf8InCpp String updatableViaApex(@utf8InCpp String name); /** + * Returns all instances which are updatable via the APEX. Instance names are fully qualified + * like `pack.age.IFoo/default`. + */ + @utf8InCpp String[] getUpdatableNames(@utf8InCpp String apexName); + + /** * If connection info is available for the given instance, returns the ConnectionInfo */ @nullable ConnectionInfo getConnectionInfo(@utf8InCpp String name); diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h index 793795e1d4..eef07aef2d 100644 --- a/libs/binder/binder_module.h +++ b/libs/binder/binder_module.h @@ -100,4 +100,9 @@ struct binder_frozen_status_info { #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32) #endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION +#ifndef BR_TRANSACTION_PENDING_FROZEN +// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it. +#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20) +#endif // BR_TRANSACTION_PENDING_FROZEN + #endif // _BINDER_MODULE_H_ diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index 46223bb00e..d960a0b894 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -59,6 +59,8 @@ public: virtual void* findObject(const void* objectID) const final; virtual void* detachObject(const void* objectID) final; void withLock(const std::function<void()>& doWithLock); + sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, + const void* makeArgs); virtual BBinder* localBinder(); @@ -103,6 +105,12 @@ public: [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd, const sp<IBinder>& 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(); @@ -129,7 +137,7 @@ private: friend ::android::internal::Stability; int16_t mStability; bool mParceled; - uint8_t mReserved0; + bool mRecordingOn; #ifdef __LP64__ int32_t mReserved1; diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 19ad5e6efe..5496d61b70 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -16,6 +16,7 @@ #pragma once +#include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <utils/Mutex.h> @@ -72,6 +73,8 @@ public: virtual void* findObject(const void* objectID) const final; virtual void* detachObject(const void* objectID) final; void withLock(const std::function<void()>& doWithLock); + sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, + const void* makeArgs); virtual BpBinder* remoteBinder(); @@ -87,6 +90,12 @@ public: std::optional<int32_t> getDebugBinderHandle() const; + // Start recording transactions to the unique_fd. + // See RecordedTransaction.h for more details. + status_t startRecordingBinder(const android::base::unique_fd& fd); + // Stop the current recording. + status_t stopRecordingBinder(); + class ObjectManager { public: ObjectManager(); @@ -96,6 +105,8 @@ public: IBinder::object_cleanup_func func); void* find(const void* objectID) const; void* detach(const void* objectID); + sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, + const void* makeArgs); void kill(); @@ -104,9 +115,9 @@ public: ObjectManager& operator=(const ObjectManager&); struct entry_t { - void* object; - void* cleanupCookie; - IBinder::object_cleanup_func func; + void* object = nullptr; + void* cleanupCookie = nullptr; + IBinder::object_cleanup_func func = nullptr; }; std::map<const void*, entry_t> mObjects; diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h new file mode 100644 index 0000000000..8b3fc1cc10 --- /dev/null +++ b/libs/binder/include/binder/Delegate.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> + +#ifndef __BIONIC__ +#ifndef __assert + +// defined differently by liblog +#pragma push_macro("LOG_PRI") +#ifdef LOG_PRI +#undef LOG_PRI +#endif +#include <syslog.h> +#pragma pop_macro("LOG_PRI") + +#define __assert(a, b, c) \ + do { \ + syslog(LOG_ERR, a ": " c); \ + abort(); \ + } while (false) +#endif // __assert +#endif // __BIONIC__ + +namespace android { + +/* + * Used to manage AIDL's *Delegator types. + * This is used to: + * - create a new *Delegator object that delegates to the binder argument. + * - or return an existing *Delegator object that already delegates to the + * binder argument. + * - or return the underlying delegate binder if the binder argument is a + * *Delegator itself. + * + * @param binder - the binder to delegate to or unwrap + * + * @return pointer to the *Delegator object or the unwrapped binder object + */ +template <typename T> +sp<T> delegate(const sp<T>& binder) { + const void* isDelegatorId = &T::descriptor; + const void* hasDelegatorId = &T::descriptor + 1; + // is binder itself a delegator? + if (T::asBinder(binder)->findObject(isDelegatorId)) { + if (T::asBinder(binder)->findObject(hasDelegatorId)) { + __assert(__FILE__, __LINE__, + "This binder has a delegator and is also delegator itself! This is " + "likely an unintended mixing of binders."); + return nullptr; + } + // unwrap the delegator + return static_cast<typename T::DefaultDelegator*>(binder.get())->getImpl(); + } + + struct MakeArgs { + const sp<T>* binder; + const void* id; + } makeArgs; + makeArgs.binder = &binder; + makeArgs.id = isDelegatorId; + + // the binder is not a delegator, so construct one + sp<IBinder> newDelegator = T::asBinder(binder)->lookupOrCreateWeak( + hasDelegatorId, + [](const void* args) -> sp<IBinder> { + auto delegator = sp<typename T::DefaultDelegator>::make( + *static_cast<const MakeArgs*>(args)->binder); + // make sure we know this binder is a delegator by attaching a unique ID + (void)delegator->attachObject(static_cast<const MakeArgs*>(args)->id, + reinterpret_cast<void*>(0x1), nullptr, nullptr); + return delegator; + }, + static_cast<const void*>(&makeArgs)); + return sp<typename T::DefaultDelegator>::cast(newDelegator); +} + +} // namespace android diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 43fc5ff1a9..e75d548bd8 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -56,6 +56,8 @@ public: LAST_CALL_TRANSACTION = 0x00ffffff, PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'), + START_RECORDING_TRANSACTION = B_PACK_CHARS('_', 'S', 'R', 'D'), + STOP_RECORDING_TRANSACTION = B_PACK_CHARS('_', 'E', 'R', 'D'), DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'), SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'), INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), @@ -284,6 +286,9 @@ public: virtual BBinder* localBinder(); virtual BpBinder* remoteBinder(); + typedef sp<IBinder> (*object_make_func)(const void* makeArgs); + sp<IBinder> lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs); protected: virtual ~IBinder(); diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 706783093c..9f7e2c87b8 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -72,9 +72,9 @@ class BnInterface : public INTERFACE, public BBinder public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; @@ -85,9 +85,9 @@ class BpInterface : public INTERFACE, public BpRefBase { public: explicit BpInterface(const sp<IBinder>& remote); + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index bf02099ffb..d261c2143c 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -28,6 +28,10 @@ typedef int uid_t; // --------------------------------------------------------------------------- namespace android { +/** + * Kernel binder thread state. All operations here refer to kernel binder. This + * object is allocated per-thread. + */ class IPCThreadState { public: @@ -135,12 +139,15 @@ public: int64_t clearCallingIdentity(); // Restores PID/UID (not SID) void restoreCallingIdentity(int64_t token); + bool hasExplicitIdentity(); + // For main functions - dangerous for libraries to use status_t setupPolling(int* fd); status_t handlePolledCommands(); void flushCommands(); bool flushIfNeeded(); + // For main functions - dangerous for libraries to use void joinThreadPool(bool isMain = true); // Stop the local process. @@ -213,9 +220,9 @@ private: void clearCaller(); static void threadDestructor(void *st); - static void freeBuffer(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsSize); + static void freeBuffer(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsSize); + static void logExtendedError(); const sp<ProcessState> mProcess; Vector<BBinder*> mPendingStrongDerefs; @@ -237,6 +244,7 @@ private: bool mPropagateWorkSource; bool mIsLooper; bool mIsFlushing; + bool mHasExplicitIdentity; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; CallRestriction mCallRestriction; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 413c97f349..c78f870153 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -67,7 +67,8 @@ public: * a system property, or in the case of services in the VINTF manifest, it can be checked * with isDeclared). */ - virtual sp<IBinder> getService( const String16& name) const = 0; + [[deprecated("this polls for 5s, prefer waitForService or checkService")]] + virtual sp<IBinder> getService(const String16& name) const = 0; /** * Retrieve an existing service, non-blocking. @@ -115,6 +116,12 @@ public: virtual std::optional<String16> updatableViaApex(const String16& name) = 0; /** + * Returns all instances which are updatable via the APEX. Instance names are fully qualified + * like `pack.age.IFoo/default`. + */ + virtual Vector<String16> getUpdatableNames(const String16& apexName) = 0; + + /** * If this instance has declared remote connection information, returns * the ConnectionInfo. */ @@ -191,7 +198,10 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) { const sp<IServiceManager> sm = defaultServiceManager(); if (sm != nullptr) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" *outService = interface_cast<INTERFACE>(sm->getService(name)); +#pragma clang diagnostic pop // getService deprecation if ((*outService) != nullptr) return NO_ERROR; } return NAME_NOT_FOUND; diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 15dd28f08e..c7177bd8eb 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -26,9 +26,10 @@ namespace android { // --------------------------------------------------------------------------- -class MemoryHeapBase : public virtual BnMemoryHeap +class MemoryHeapBase : public BnMemoryHeap { public: + static constexpr auto MEMFD_ALLOW_SEALING_FLAG = 0x00000800; enum { READ_ONLY = IMemoryHeap::READ_ONLY, // memory won't be mapped locally, but will be mapped in the remote @@ -48,7 +49,7 @@ public: // Clients of shared files can seal at anytime via syscall, leading to // TOC/TOU issues if additional seals prevent access from the creating // process. Alternatively, seccomp fcntl(). - MEMFD_ALLOW_SEALING = 0x00000800 + MEMFD_ALLOW_SEALING = FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG }; /* diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index e2b2c5128d..162cd406dc 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -20,6 +20,7 @@ #include <map> // for legacy reasons #include <string> #include <type_traits> +#include <variant> #include <vector> #include <android-base/unique_fd.h> @@ -74,8 +75,14 @@ public: size_t dataAvail() const; size_t dataPosition() const; size_t dataCapacity() const; + size_t dataBufferSize() const; status_t setDataSize(size_t size); + + // this must only be used to set a data position that was previously returned from + // dataPosition(). If writes are made, the exact same types of writes must be made (e.g. + // auto i = p.dataPosition(); p.writeInt32(0); p.setDataPosition(i); p.writeInt32(1);). + // Writing over objects, such as file descriptors and binders, is not supported. void setDataPosition(size_t pos) const; status_t setDataCapacity(size_t size); @@ -144,6 +151,9 @@ public: // Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed. binder::Status enforceNoDataAvail() const; + // This Api is used by fuzzers to skip dataAvail checks. + void setEnforceNoDataAvail(bool enforceNoDataAvail); + void freeData(); size_t objectsCount() const; @@ -589,27 +599,36 @@ public: // uid. uid_t readCallingWorkSourceUid() const; - void print(TextOutput& to, uint32_t flags = 0) const; + void print(std::ostream& to, uint32_t flags = 0) const; private: - typedef void (*release_func)(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsSize); + // `objects` and `objectsSize` always 0 for RPC Parcels. + typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsSize); uintptr_t ipcData() const; size_t ipcDataSize() const; uintptr_t ipcObjects() const; size_t ipcObjectsCount() const; - void ipcSetDataReference(const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount, - release_func relFunc); + void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount, release_func relFunc); + // Takes ownership even when an error is returned. + status_t rpcSetDataReference( + const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, + const uint32_t* objectTable, size_t objectTableSize, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds, + release_func relFunc); status_t finishWrite(size_t len); void releaseObjects(); void acquireObjects(); status_t growData(size_t len); + // Clear the Parcel and set the capacity to `desired`. + // Doesn't reset the RPC session association. status_t restartWrite(size_t desired); + // Set the capacity to `desired`, truncating the Parcel if necessary. status_t continueWrite(size_t desired); + status_t truncateRpcObjects(size_t newObjectsSize); status_t writePointer(uintptr_t val); status_t readPointer(uintptr_t *pArg) const; uintptr_t readPointer() const; @@ -1169,10 +1188,20 @@ private: c->clear(); // must clear before resizing/reserving otherwise move ctors may be called. if constexpr (is_pointer_equivalent_array_v<T>) { // could consider POD without gaps and alignment of 4. - auto data = reinterpret_cast<const T*>( - readInplace(static_cast<size_t>(size) * sizeof(T))); + size_t dataLen; + if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) { + return -EOVERFLOW; + } + auto data = reinterpret_cast<const T*>(readInplace(dataLen)); if (data == nullptr) return BAD_VALUE; - c->insert(c->begin(), data, data + size); // insert should do a reserve(). + // std::vector::insert and similar methods will require type-dependent + // byte alignment when inserting from a const iterator such as `data`, + // e.g. 8 byte alignment for int64_t, and so will not work if `data` + // is 4 byte aligned (which is all Parcel guarantees). Copying + // the contents into the vector directly, where possible, circumvents + // this. + c->resize(size); + memcpy(c->data(), data, dataLen); } else if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char16_t>) { c->reserve(size); // avoids default initialization @@ -1247,28 +1276,68 @@ private: uint8_t* mData; size_t mDataSize; size_t mDataCapacity; - mutable size_t mDataPos; - binder_size_t* mObjects; - size_t mObjectsSize; - size_t mObjectsCapacity; - mutable size_t mNextObjectHint; - mutable bool mObjectsSorted; + mutable size_t mDataPos; - mutable bool mRequestHeaderPresent; + // Fields only needed when parcelling for "kernel Binder". + struct KernelFields { + binder_size_t* mObjects = nullptr; + size_t mObjectsSize = 0; + size_t mObjectsCapacity = 0; + mutable size_t mNextObjectHint = 0; - mutable size_t mWorkSourceRequestHeaderPosition; + mutable size_t mWorkSourceRequestHeaderPosition = 0; + mutable bool mRequestHeaderPresent = false; + + mutable bool mObjectsSorted = false; + mutable bool mFdsKnown = true; + mutable bool mHasFds = false; + }; + // Fields only needed when parcelling for RPC Binder. + struct RpcFields { + RpcFields(const sp<RpcSession>& session); + + // Should always be non-null. + const sp<RpcSession> mSession; + + enum ObjectType : int32_t { + TYPE_BINDER_NULL = 0, + TYPE_BINDER = 1, + // FD to be passed via native transport (Trusty IPC or UNIX domain socket). + TYPE_NATIVE_FILE_DESCRIPTOR = 2, + }; + + // Sorted. + std::vector<uint32_t> mObjectPositions; + + // File descriptors referenced by the parcel data. Should be indexed + // using the offsets in the parcel data. Don't assume the list is in the + // same order as `mObjectPositions`. + // + // Boxed to save space. Lazy allocated. + std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds; + }; + std::variant<KernelFields, RpcFields> mVariantFields; + + // Pointer to KernelFields in mVariantFields if present. + KernelFields* maybeKernelFields() { return std::get_if<KernelFields>(&mVariantFields); } + const KernelFields* maybeKernelFields() const { + return std::get_if<KernelFields>(&mVariantFields); + } + // Pointer to RpcFields in mVariantFields if present. + RpcFields* maybeRpcFields() { return std::get_if<RpcFields>(&mVariantFields); } + const RpcFields* maybeRpcFields() const { return std::get_if<RpcFields>(&mVariantFields); } - mutable bool mFdsKnown; - mutable bool mHasFds; bool mAllowFds; // if this parcelable is involved in a secure transaction, force the // data to be overridden with zero when deallocated mutable bool mDeallocZero; + // Set this to false to skip dataAvail checks. + bool mEnforceNoDataAvail; + release_func mOwner; - sp<RpcSession> mSession; size_t mReserved; class Blob { @@ -1532,8 +1601,7 @@ status_t Parcel::readNullableStrongBinder(sp<T>* val) const { // --------------------------------------------------------------------------- -inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) -{ +inline std::ostream& operator<<(std::ostream& to, const Parcel& parcel) { parcel.print(to); return to; } diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h index 9896fd7a51..08d8e43106 100644 --- a/libs/binder/include/binder/ParcelFileDescriptor.h +++ b/libs/binder/include/binder/ParcelFileDescriptor.h @@ -42,6 +42,7 @@ public: android::status_t writeToParcel(android::Parcel* parcel) const override; android::status_t readFromParcel(const android::Parcel* parcel) override; + inline std::string toString() const { return "ParcelFileDescriptor:" + std::to_string(get()); } inline bool operator!=(const ParcelFileDescriptor& rhs) const { return mFd.get() != rhs.mFd.get(); } diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h index 88790a803b..40fd30ae3f 100644 --- a/libs/binder/include/binder/ParcelableHolder.h +++ b/libs/binder/include/binder/ParcelableHolder.h @@ -111,6 +111,11 @@ public: Stability getStability() const override { return mStability; } + inline std::string toString() const { + return "ParcelableHolder:" + + (mParcelableName ? std::string(String8(mParcelableName.value()).c_str()) + : "<parceled>"); + } inline bool operator!=(const ParcelableHolder& rhs) const { return this != &rhs; } diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 675585ecef..ce578e3f5c 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -29,11 +29,17 @@ namespace android { class IPCThreadState; +/** + * Kernel binder process state. All operations here refer to kernel binder. This + * object is allocated per process. + */ class ProcessState : public virtual RefBase { public: static sp<ProcessState> self(); static sp<ProcessState> selfOrNull(); + static bool isVndservicemanagerEnabled(); + /* initWithDriver() can be used to configure libbinder to use * a different binder driver dev node. It must be called *before* * any call to ProcessState::self(). The default is /dev/vndbinder @@ -46,6 +52,7 @@ public: sp<IBinder> getContextObject(const sp<IBinder>& caller); + // For main functions - dangerous for libraries to use void startThreadPool(); bool becomeContextManager(); @@ -53,10 +60,17 @@ public: sp<IBinder> getStrongProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); + // 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 + // thread. Typically this is called before joinThreadPool. + // + // TODO: remove this API, and automatically set it intelligently. void giveThreadPoolName(); String8 getDriverName(); @@ -84,14 +98,20 @@ public: void setCallRestriction(CallRestriction restriction); /** - * Get the max number of threads that the kernel can start. - * - * Note: this is the lower bound. Additional threads may be started. + * Get the max number of threads that have joined the thread pool. + * This includes kernel started threads, user joined threads and polling + * threads if used. + */ + size_t getThreadPoolMaxTotalThreadCount() const; + + /** + * Check to see if the thread pool has started. */ - size_t getThreadPoolMaxThreadCount() const; + bool isThreadPoolStarted() const; enum class DriverFeature { ONEWAY_SPAM_DETECTION, + EXTENDED_ERROR, }; // Determine whether a feature is supported by the binder driver. static bool isDriverFeatureEnabled(const DriverFeature feature); @@ -125,15 +145,19 @@ private: void* mVMStart; // Protects thread count and wait variables below. - pthread_mutex_t mThreadCountLock; + mutable pthread_mutex_t mThreadCountLock; // Broadcast whenever mWaitingForThreads > 0 pthread_cond_t mThreadCountDecrement; // Number of binder threads current executing a command. size_t mExecutingThreadsCount; // Number of threads calling IPCThreadState::blockUntilThreadAvailable() size_t mWaitingForThreads; - // Maximum number for binder threads allowed for this process. + // Maximum number of lazy threads to be started in the threadpool by the kernel. size_t mMaxThreads; + // Current number of threads inside the thread pool. + size_t mCurrentThreads; + // Current number of pooled threads inside the thread pool. + size_t mKernelStartedThreads; // Time when thread pool was emptied int64_t mStarvationStartTimeMs; diff --git a/libs/binder/include/binder/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h new file mode 100644 index 0000000000..eb765fe8ec --- /dev/null +++ b/libs/binder/include/binder/RecordedTransaction.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/Parcel.h> +#include <mutex> + +namespace android { + +namespace binder::debug { + +// Warning: Transactions are sequentially recorded to the file descriptor in a +// non-stable format. A detailed description of the recording format can be found in +// RecordedTransaction.cpp. + +class RecordedTransaction { +public: + // Filled with the first transaction from fd. + static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd); + // Filled with the arguments. + static std::optional<RecordedTransaction> fromDetails(const String16& interfaceName, + uint32_t code, uint32_t flags, + timespec timestamp, const Parcel& data, + const Parcel& reply, status_t err); + RecordedTransaction(RecordedTransaction&& t) noexcept; + + [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const; + + const std::string& getInterfaceName() const; + uint32_t getCode() const; + uint32_t getFlags() const; + int32_t getReturnedStatus() const; + timespec getTimestamp() const; + uint32_t getVersion() const; + const Parcel& getDataParcel() const; + const Parcel& getReplyParcel() const; + +private: + RecordedTransaction() = default; + + android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType, + size_t byteCount, const uint8_t* data) const; + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wpadded" + struct TransactionHeader { + uint32_t code = 0; + uint32_t flags = 0; + int32_t statusReturned = 0; + uint32_t version = 0; // !0 iff Rpc + int64_t timestampSeconds = 0; + int32_t timestampNanoseconds = 0; + int32_t reserved = 0; + }; +#pragma clang diagnostic pop + static_assert(sizeof(TransactionHeader) == 32); + static_assert(sizeof(TransactionHeader) % 8 == 0); + + struct MovableData { // movable + TransactionHeader mHeader; + std::string mInterfaceName; + }; + MovableData mData; + Parcel mSent; + Parcel mReply; +}; + +} // namespace binder::debug + +} // namespace android diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 6b31812d17..25193a3428 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -18,6 +18,7 @@ #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcSession.h> +#include <binder/RpcThreads.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -28,6 +29,7 @@ namespace android { class FdTrigger; +class RpcServerTrusty; class RpcSocketAddress; /** @@ -48,6 +50,17 @@ public: std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr); /** + * Creates an RPC server that bootstraps sessions using an existing + * Unix domain socket pair. + * + * Callers should create a pair of SOCK_STREAM Unix domain sockets, pass + * one to RpcServer::setupUnixDomainSocketBootstrapServer and the other + * to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client + * session can be created from the client end of the pair. + */ + [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(base::unique_fd serverFd); + + /** * This represents a session for responses, e.g.: * * process A serves binder a @@ -58,9 +71,19 @@ public: [[nodiscard]] status_t setupUnixDomainServer(const char* path); /** - * Creates an RPC server at the current port. + * Sets up an RPC server with a raw socket file descriptor. + * The socket should be created and bound to a socket address already, e.g. + * the socket can be created in init.rc. + * + * This method is used in the libbinder_rpc_unstable API + * RunInitUnixDomainRpcServer(). + */ + [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd); + + /** + * Creates an RPC server binding to the given CID at the given port. */ - [[nodiscard]] status_t setupVsockServer(unsigned int port); + [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port); /** * Creates an RPC server at the current port using IPv4. @@ -114,6 +137,15 @@ public: void setProtocolVersion(uint32_t version); /** + * Set the supported transports for sending and receiving file descriptors. + * + * Clients will propose a mode when connecting. If the mode is not in the + * provided list, the connection will be rejected. + */ + void setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes); + + /** * The root object can be retrieved by any client, without any * authentication. TODO(b/183988761) * @@ -125,12 +157,30 @@ public: */ void setRootObjectWeak(const wp<IBinder>& binder); /** - * Allows a root object to be created for each session + * Allows a root object to be created for each session. + * + * Takes one argument: a callable that is invoked once per new session. + * The callable takes two arguments: a type-erased pointer to an OS- and + * transport-specific address structure, e.g., sockaddr_vm for vsock, and + * an integer representing the size in bytes of that structure. The + * callable should validate the size, then cast the type-erased pointer + * to a pointer to the actual type of the address, e.g., const void* to + * const sockaddr_vm*. */ - void setPerSessionRootObject(std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& object); + void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object); sp<IBinder> getRootObject(); /** + * Set optional filter of incoming connections based on the peer's address. + * + * Takes one argument: a callable that is invoked on each accept()-ed + * connection and returns false if the connection should be dropped. + * See the description of setPerSessionRootObject() for details about + * the callable's arguments. + */ + void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter); + + /** * See RpcTransportCtx::getCertificate */ std::vector<uint8_t> getCertificate(RpcCertificateFormat); @@ -168,34 +218,56 @@ public: std::vector<sp<RpcSession>> listSessions(); size_t numUninitializedSessions(); + /** + * Whether any requests are currently being processed. + */ + bool hasActiveRequests(); + ~RpcServer(); private: + friend RpcServerTrusty; friend sp<RpcServer>; explicit RpcServer(std::unique_ptr<RpcTransportCtx> ctx); void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen); + status_t setupExternalServer( + base::unique_fd serverFd, + std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn); + + static constexpr size_t kRpcAddressSize = 128; + static void establishConnection( + sp<RpcServer>&& server, RpcTransportFd clientFd, + std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen, + std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn); + static status_t acceptSocketConnection(const RpcServer& server, RpcTransportFd* out); + static status_t recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out); + [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address); const std::unique_ptr<RpcTransportCtx> mCtx; size_t mMaxThreads = 1; std::optional<uint32_t> mProtocolVersion; - base::unique_fd mServer; // socket we are accepting sessions on + // A mode is supported if the N'th bit is on, where N is the mode enum's value. + std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set( + static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE)); + RpcTransportFd mServer; // socket we are accepting sessions on - std::mutex mLock; // for below - std::unique_ptr<std::thread> mJoinThread; + RpcMutex mLock; // for below + std::unique_ptr<RpcMaybeThread> mJoinThread; bool mJoinThreadRunning = false; - std::map<std::thread::id, std::thread> mConnectingThreads; + std::map<RpcMaybeThread::id, RpcMaybeThread> mConnectingThreads; + sp<IBinder> mRootObject; wp<IBinder> mRootObjectWeak; - std::function<sp<IBinder>(const sockaddr*, socklen_t)> mRootObjectFactory; + std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory; + std::function<bool(const void*, size_t)> mConnectionFilter; std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions; std::unique_ptr<FdTrigger> mShutdownTrigger; - std::condition_variable mShutdownCv; + RpcConditionVariable mShutdownCv; + std::function<status_t(const RpcServer& server, RpcTransportFd* out)> mAcceptFn; }; } // namespace android diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index a5794428de..40faf2c3b1 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -15,21 +15,23 @@ */ #pragma once +#include <android-base/threads.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> +#include <binder/RpcThreads.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <map> #include <optional> -#include <thread> #include <vector> namespace android { class Parcel; class RpcServer; +class RpcServerTrusty; class RpcSocketAddress; class RpcState; class RpcTransport; @@ -37,7 +39,13 @@ class FdTrigger; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000; -constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 0; +constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL; + +// Starting with this version: +// +// * RpcWireReply is larger (4 bytes -> 20). +// * RpcWireTransaction and RpcWireReplyV1 include the parcel data size. +constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE = 1; /** * This represents a session (group of connections) between a client @@ -88,6 +96,20 @@ public: [[nodiscard]] bool setProtocolVersion(uint32_t version); std::optional<uint32_t> getProtocolVersion(); + enum class FileDescriptorTransportMode : uint8_t { + NONE = 0, + // Send file descriptors via unix domain socket ancillary data. + UNIX = 1, + // Send file descriptors as Trusty IPC handles. + TRUSTY = 2, + }; + + /** + * Set the transport for sending and receiving file descriptors. + */ + void setFileDescriptorTransportMode(FileDescriptorTransportMode mode); + FileDescriptorTransportMode getFileDescriptorTransportMode(); + /** * This should be called once per thread, matching 'join' in the remote * process. @@ -95,6 +117,11 @@ public: [[nodiscard]] status_t setupUnixDomainClient(const char* path); /** + * Connects to an RPC server over a nameless Unix domain socket pair. + */ + [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(base::unique_fd bootstrap); + + /** * Connects to an RPC server at the CVD & port. */ [[nodiscard]] status_t setupVsockClient(unsigned int cvd, unsigned int port); @@ -169,6 +196,11 @@ public: */ [[nodiscard]] status_t sendDecStrong(const BpBinder* binder); + /** + * Whether any requests are currently being processed. + */ + bool hasActiveRequests(); + ~RpcSession(); /** @@ -183,9 +215,14 @@ public: private: friend sp<RpcSession>; friend RpcServer; + friend RpcServerTrusty; friend RpcState; explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx); + // internal version of setProtocolVersion that + // optionally skips the mStartedSetup check + [[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted); + // for 'target', see RpcState::sendDecStrongToTarget [[nodiscard]] status_t sendDecStrongToTarget(uint64_t address, size_t target); @@ -199,10 +236,11 @@ private: public: void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session); + void waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session); private: - std::condition_variable mCv; + RpcConditionVariable mCv; + std::atomic<size_t> mShutdownCount = 0; }; friend WaitForShutdownListener; @@ -211,7 +249,7 @@ private: // whether this or another thread is currently using this fd to make // or receive transactions. - std::optional<pid_t> exclusiveTid; + std::optional<uint64_t> exclusiveTid; bool allowNested = false; }; @@ -225,7 +263,7 @@ private: // // transfer ownership of thread (usually done while a lock is taken on the // structure which originally owns the thread) - void preJoinThreadOwnership(std::thread thread); + void preJoinThreadOwnership(RpcMaybeThread thread); // pass FD to thread and read initial connection information struct PreJoinSetupResult { // Server connection object associated with this @@ -244,7 +282,7 @@ private: [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address, const std::vector<uint8_t>& sessionId, bool incoming); - [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd, + [[nodiscard]] status_t initAndAddConnection(RpcTransportFd fd, const std::vector<uint8_t>& sessionId, bool incoming); [[nodiscard]] status_t addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport); @@ -257,9 +295,15 @@ private: sp<RpcConnection> assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport); [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection); + void clearConnectionTid(const sp<RpcConnection>& connection); [[nodiscard]] status_t initShutdownTrigger(); + /** + * Checks whether any connection is active (Not polling on fd) + */ + bool hasActiveConnection(const std::vector<sp<RpcConnection>>& connections); + enum class ConnectionUse { CLIENT, CLIENT_ASYNC, @@ -276,7 +320,7 @@ private: const sp<RpcConnection>& get() { return mConnection; } private: - static void findConnection(pid_t tid, sp<RpcConnection>* exclusive, + static void findConnection(uint64_t tid, sp<RpcConnection>* exclusive, sp<RpcConnection>* available, std::vector<sp<RpcConnection>>& sockets, size_t socketsIndexHint); @@ -306,7 +350,7 @@ private: // For a more complicated case, the client might itself open up a thread to // serve calls to the server at all times (e.g. if it hosts a callback) - wp<RpcServer> mForServer; // maybe null, for client sessions + wp<RpcServer> mForServer; // maybe null, for client sessions sp<WaitForShutdownListener> mShutdownListener; // used for client sessions wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client @@ -320,22 +364,27 @@ private: std::unique_ptr<RpcState> mRpcBinderState; - std::mutex mMutex; // for all below + RpcMutex mMutex; // for all below + bool mStartedSetup = false; size_t mMaxIncomingThreads = 0; size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads; std::optional<uint32_t> mProtocolVersion; + FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE; + + RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads - std::condition_variable mAvailableConnectionCv; // for mWaitingThreads + std::unique_ptr<RpcTransport> mBootstrapTransport; struct ThreadState { size_t mWaitingThreads = 0; // hint index into clients, ++ when sending an async transaction size_t mOutgoingOffset = 0; std::vector<sp<RpcConnection>> mOutgoing; + // max size of mIncoming. Once any thread starts down, no more can be started. size_t mMaxIncoming = 0; std::vector<sp<RpcConnection>> mIncoming; - std::map<std::thread::id, std::thread> mThreads; + std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads; } mConnections; }; diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h new file mode 100644 index 0000000000..8abf04eaf0 --- /dev/null +++ b/libs/binder/include/binder/RpcThreads.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <pthread.h> + +#include <android-base/threads.h> + +#include <functional> +#include <memory> +#include <thread> + +namespace android { + +#ifdef BINDER_RPC_SINGLE_THREADED +class RpcMutex { +public: + void lock() {} + void unlock() {} +}; + +class RpcMutexUniqueLock { +public: + RpcMutexUniqueLock(RpcMutex&) {} + void unlock() {} +}; + +class RpcMutexLockGuard { +public: + RpcMutexLockGuard(RpcMutex&) {} +}; + +class RpcConditionVariable { +public: + void notify_one() {} + void notify_all() {} + + void wait(RpcMutexUniqueLock&) {} + + template <typename Predicate> + void wait(RpcMutexUniqueLock&, Predicate stop_waiting) { + LOG_ALWAYS_FATAL_IF(!stop_waiting(), "RpcConditionVariable::wait condition not met"); + } + + template <typename Duration> + std::cv_status wait_for(RpcMutexUniqueLock&, const Duration&) { + return std::cv_status::no_timeout; + } + + template <typename Duration, typename Predicate> + bool wait_for(RpcMutexUniqueLock&, const Duration&, Predicate stop_waiting) { + return stop_waiting(); + } +}; + +class RpcMaybeThread { +public: + RpcMaybeThread() = default; + + template <typename Function, typename... Args> + RpcMaybeThread(Function&& f, Args&&... args) { + // std::function requires a copy-constructible closure, + // so we need to wrap both the function and its arguments + // in a shared pointer that std::function can copy internally + struct Vars { + std::decay_t<Function> f; + std::tuple<std::decay_t<Args>...> args; + + explicit Vars(Function&& f, Args&&... args) + : f(std::move(f)), args(std::move(args)...) {} + }; + auto vars = std::make_shared<Vars>(std::forward<Function>(f), std::forward<Args>(args)...); + mFunc = [vars]() { std::apply(std::move(vars->f), std::move(vars->args)); }; + } + + void join() { + if (mFunc) { + // Move mFunc into a temporary so we can clear mFunc before + // executing the callback. This avoids infinite recursion if + // the callee then calls join() again directly or indirectly. + decltype(mFunc) func = nullptr; + mFunc.swap(func); + func(); + } + } + void detach() { join(); } + + class id { + public: + bool operator==(const id&) const { return true; } + bool operator!=(const id&) const { return false; } + bool operator<(const id&) const { return false; } + bool operator<=(const id&) const { return true; } + bool operator>(const id&) const { return false; } + bool operator>=(const id&) const { return true; } + }; + + id get_id() const { return id(); } + +private: + std::function<void(void)> mFunc; +}; + +namespace rpc_this_thread { +static inline RpcMaybeThread::id get_id() { + return RpcMaybeThread::id(); +} +} // namespace rpc_this_thread + +static inline uint64_t rpcGetThreadId() { + return 0; +} + +static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) { + t.join(); +} +#else // BINDER_RPC_SINGLE_THREADED +using RpcMutex = std::mutex; +using RpcMutexUniqueLock = std::unique_lock<std::mutex>; +using RpcMutexLockGuard = std::lock_guard<std::mutex>; +using RpcConditionVariable = std::condition_variable; +using RpcMaybeThread = std::thread; +namespace rpc_this_thread = std::this_thread; + +static inline uint64_t rpcGetThreadId() { + return base::GetThreadId(); +} + +static inline void rpcJoinIfSingleThreaded(RpcMaybeThread&) {} +#endif // BINDER_RPC_SINGLE_THREADED + +} // namespace android diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index 751c4f905d..fd52a3a1a9 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -20,18 +20,24 @@ #include <functional> #include <memory> +#include <optional> #include <string> +#include <variant> +#include <vector> +#include <android-base/function_ref.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> #include <binder/RpcCertificateFormat.h> +#include <binder/RpcThreads.h> #include <sys/uio.h> namespace android { class FdTrigger; +struct RpcTransportFd; // Represents a socket connection. // No thread-safety is guaranteed for these APIs. @@ -39,8 +45,15 @@ class RpcTransport { public: virtual ~RpcTransport() = default; - // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled. - [[nodiscard]] virtual status_t peek(void *buf, size_t size, size_t *out_size) = 0; + /** + * Poll the transport to check whether there is any data ready to read. + * + * Return: + * OK - There is data available on this transport + * WOULDBLOCK - No data is available + * error - any other error + */ + [[nodiscard]] virtual status_t pollRead(void) = 0; /** * Read (or write), but allow to be interrupted by a trigger. @@ -52,16 +65,32 @@ public: * to read/write data. If this returns an error, that error is returned from * this function. * + * ancillaryFds - FDs to be sent via UNIX domain dockets or Trusty IPC. When + * reading, if `ancillaryFds` is null, any received FDs will be silently + * dropped and closed (by the OS). Appended values will always be unique_fd, + * the variant type is used to avoid extra copies elsewhere. + * * Return: * OK - succeeded in completely processing 'size' * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::function<status_t()> &altPoll) = 0; + const std::optional<android::base::function_ref<status_t()>> &altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; [[nodiscard]] virtual status_t interruptableReadFully( FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::function<status_t()> &altPoll) = 0; + const std::optional<android::base::function_ref<status_t()>> &altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; + + /** + * Check whether any threads are blocked while polling the transport + * for read operations + * Return: + * True - Specifies that there is active polling on transport. + * False - No active polling on transport + */ + [[nodiscard]] virtual bool isWaiting() = 0; protected: RpcTransport() = default; @@ -78,7 +107,7 @@ public: // Implementation details: for TLS, this function may incur I/O. |fdTrigger| may be used // to interrupt I/O. This function blocks until handshake is finished. [[nodiscard]] virtual std::unique_ptr<RpcTransport> newTransport( - android::base::unique_fd fd, FdTrigger *fdTrigger) const = 0; + android::RpcTransportFd fd, FdTrigger *fdTrigger) const = 0; // Return the preconfigured certificate of this context. // @@ -111,4 +140,36 @@ protected: RpcTransportCtxFactory() = default; }; +struct RpcTransportFd { +private: + mutable bool isPolling{false}; + + void setPollingState(bool state) const { isPolling = state; } + +public: + base::unique_fd fd; + + RpcTransportFd() = default; + explicit RpcTransportFd(base::unique_fd &&descriptor) + : isPolling(false), fd(std::move(descriptor)) {} + + RpcTransportFd(RpcTransportFd &&transportFd) noexcept + : isPolling(transportFd.isPolling), fd(std::move(transportFd.fd)) {} + + RpcTransportFd &operator=(RpcTransportFd &&transportFd) noexcept { + fd = std::move(transportFd.fd); + isPolling = transportFd.isPolling; + return *this; + } + + RpcTransportFd &operator=(base::unique_fd &&descriptor) noexcept { + fd = std::move(descriptor); + isPolling = false; + return *this; + } + + bool isInPollingState() const { return isPolling; } + friend class FdTrigger; +}; + } // namespace android diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h index bf9c92bfc5..eb98042c42 100644 --- a/libs/binder/include/binder/TextOutput.h +++ b/libs/binder/include/binder/TextOutput.h @@ -94,7 +94,7 @@ private: uint32_t mCode; }; -TextOutput& operator<<(TextOutput& to, const TypeCode& val); +std::ostream& operator<<(std::ostream& to, const TypeCode& val); class HexDump { @@ -123,7 +123,7 @@ private: bool mCArrayStyle; }; -TextOutput& operator<<(TextOutput& to, const HexDump& val); +std::ostream& operator<<(std::ostream& to, const HexDump& val); inline TextOutput& operator<<(TextOutput& to, decltype(std::endl<char, std::char_traits<char>>) diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h new file mode 100644 index 0000000000..99378428ad --- /dev/null +++ b/libs/binder/include/binder/Trace.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cutils/trace.h> +#include <stdint.h> + +namespace android { +namespace binder { + +// 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); + +class ScopedTrace { +public: + inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); } + + inline ~ScopedTrace() { atrace_end(mTag); } + +private: + uint64_t mTag; +}; + +} // namespace binder +} // namespace android diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 5baa4d7ad4..42d226b805 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -17,38 +17,121 @@ #pragma once #include <sys/socket.h> +#include <stdint.h> extern "C" { struct AIBinder; +struct ARpcServer; +struct ARpcSession; -// Starts an RPC server on a given port and a given root IBinder object. -// This function sets up the server and joins before returning. -bool RunRpcServer(AIBinder* service, unsigned int port); +enum class ARpcSession_FileDescriptorTransportMode { + None, + Unix, + Trusty, +}; // Starts an RPC server on a given port and a given root IBinder object. -// This function sets up the server, calls readyCallback with a given param, and -// then joins before returning. -bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param), - void* param); - -// Starts an RPC server on a given port and a given root IBinder factory. -// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of -// assigning single root IBinder object to all connections, factory is called -// whenever a client connects, making it possible to assign unique IBinder -// object to each client. -bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), - void* factoryContext, unsigned int port); - -AIBinder* RpcClient(unsigned int cid, unsigned int port); - -// Connect to an RPC server with preconnected file descriptors. +// The server will only accept connections from the given CID. +// Set `cid` to VMADDR_CID_ANY to accept connections from any client. +// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface. +// Returns an opaque handle to the running server instance, or null if the server +// could not be started. +[[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` +// and a given root IBinder object. +// The socket should be created in init.rc with the same `name`. +// 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); + +// Starts an RPC server that bootstraps sessions using an existing Unix domain +// socket pair, with a given root IBinder object. +// Callers should create a pair of SOCK_STREAM Unix domain sockets, pass one to +// this function and the other to UnixDomainBootstrapClient(). Multiple client +// session can be created from the client end of the pair. +// Does not take ownership of `service`. +// Returns an opaque handle to the running server instance, or null if the server +// could not be started. +[[nodiscard]] ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd); + +// Starts an RPC server on a given IP address+port and a given IBinder object. +// Returns an opaque handle to the running server instance, or null if the server +// could not be started. +// Does not take ownership of `service`. +// Returns an opaque handle to the running service instance, or null if the server +// could not be started. +[[nodiscard]] ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, + unsigned int port); + +// Sets the list of supported file descriptor transport modes of this RPC server. +void ARpcServer_setSupportedFileDescriptorTransportModes( + ARpcServer* handle, + const ARpcSession_FileDescriptorTransportMode modes[], + size_t modes_len); + +// Runs ARpcServer_join() in a background thread. Immediately returns. +void ARpcServer_start(ARpcServer* server); + +// Joins the thread of a running RpcServer instance. At any given point, there +// can only be one thread calling ARpcServer_join(). +// If a client needs to actively terminate join, call ARpcServer_shutdown() in +// a separate thread. +void ARpcServer_join(ARpcServer* server); + +// Shuts down any running ARpcServer_join(). +[[nodiscard]] bool ARpcServer_shutdown(ARpcServer* server); + +// Frees the ARpcServer handle and drops the reference count on the underlying +// RpcServer instance. The handle must not be reused afterwards. +// This automatically calls ARpcServer_shutdown(). +void ARpcServer_free(ARpcServer* server); + +// Allocates a new RpcSession object and returns an opaque handle to it. +[[nodiscard]] ARpcSession* ARpcSession_new(); + +// Connects to an RPC server over vsock at a given CID on a given port. +// Returns the root Binder object of the server. +AIBinder* ARpcSession_setupVsockClient(ARpcSession* session, unsigned int cid, + unsigned int port); + +// Connects to an RPC server over a Unix Domain Socket of the given name. +// The final Unix Domain Socket path name is /dev/socket/`name`. +// Returns the root Binder object of the server. +AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* session, const char* name); + +// Connects to an RPC server over the given bootstrap Unix domain socket. +// Does NOT take ownership of `bootstrapFd`. +AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* session, + int bootstrapFd); + +// Connects to an RPC server over an INET socket at a given IP address on a given port. +// Returns the root Binder object of the server. +AIBinder* ARpcSession_setupInet(ARpcSession* session, const char* address, unsigned int port); + +// Connects to an RPC server with preconnected file descriptors. // // requestFd should connect to the server and return a valid file descriptor, or // -1 if connection fails. // // param will be passed to requestFd. Callers can use param to pass contexts to // the requestFd function. -AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param); +AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session, + int (*requestFd)(void* param), + void* param); + +// Sets the file descriptor transport mode for this session. +void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session, + ARpcSession_FileDescriptorTransportMode mode); + +// Sets the maximum number of incoming threads. +void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads); + +// Sets the maximum number of outgoing threads. +void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads); +// Decrements the refcount of the underlying RpcSession object. +void ARpcSession_free(ARpcSession* session); } diff --git a/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h new file mode 100644 index 0000000000..4a4172a3a7 --- /dev/null +++ b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Wraps the transport layer of RPC. Implementation uses Trusty IPC. + +#pragma once + +#include <memory> + +#include <binder/RpcTransport.h> + +namespace android { + +// RpcTransportCtxFactory for writing Trusty IPC clients in Android. +class RpcTransportCtxFactoryTipcAndroid : public RpcTransportCtxFactory { +public: + static std::unique_ptr<RpcTransportCtxFactory> make(); + + std::unique_ptr<RpcTransportCtx> newServerCtx() const override; + std::unique_ptr<RpcTransportCtx> newClientCtx() const override; + const char* toCString() const override; + +private: + RpcTransportCtxFactoryTipcAndroid() = default; +}; + +} // namespace android diff --git a/libs/binder/include_trusty/binder/RpcTrusty.h b/libs/binder/include_trusty/binder/RpcTrusty.h new file mode 100644 index 0000000000..b034b9b65b --- /dev/null +++ b/libs/binder/include_trusty/binder/RpcTrusty.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> + +namespace android { + +sp<IBinder> RpcTrustyConnect(const char* device, const char* port); + +sp<RpcSession> RpcTrustyConnectWithSessionInitializer( + const char* device, const char* port, + std::function<void(sp<RpcSession>&)> sessionInitializer); + +} // namespace android diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index bf2b25b265..daff8c1e16 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -14,68 +14,189 @@ * limitations under the License. */ +#include <binder_rpc_unstable.hpp> + #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <android/binder_libbinder.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> +#include <cutils/sockets.h> #include <linux/vm_sockets.h> using android::OK; using android::RpcServer; using android::RpcSession; +using android::sp; using android::status_t; using android::statusToString; using android::base::unique_fd; +// Opaque handle for RpcServer. +struct ARpcServer {}; + +// Opaque handle for RpcSession. +struct ARpcSession {}; + +template <typename A, typename T> +static A* createObjectHandle(sp<T>& server) { + auto ref = server.get(); + ref->incStrong(ref); + return reinterpret_cast<A*>(ref); +} + +template <typename T, typename A> +static void freeObjectHandle(A* handle) { + LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null"); + auto ref = reinterpret_cast<T*>(handle); + ref->decStrong(ref); +} + +template <typename T, typename A> +static sp<T> handleToStrongPointer(A* handle) { + LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null"); + auto ref = reinterpret_cast<T*>(handle); + return sp<T>::fromExisting(ref); +} + +RpcSession::FileDescriptorTransportMode toTransportMode( + ARpcSession_FileDescriptorTransportMode mode) { + switch (mode) { + case ARpcSession_FileDescriptorTransportMode::None: + return RpcSession::FileDescriptorTransportMode::NONE; + case ARpcSession_FileDescriptorTransportMode::Unix: + return RpcSession::FileDescriptorTransportMode::UNIX; + case ARpcSession_FileDescriptorTransportMode::Trusty: + return RpcSession::FileDescriptorTransportMode::TRUSTY; + default: + return RpcSession::FileDescriptorTransportMode::NONE; + } +} + extern "C" { -bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), - void* factoryContext, unsigned int port) { +ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) { auto server = RpcServer::make(); - if (status_t status = server->setupVsockServer(port); status != OK) { + + unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface + if (cid == VMADDR_CID_LOCAL) { + bindCid = VMADDR_CID_LOCAL; // bind to the local interface + cid = VMADDR_CID_ANY; // no need for a connection filter + } + + 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(); - return false; + return nullptr; } - server->setPerSessionRootObject([=](const sockaddr* addr, socklen_t addrlen) { - LOG_ALWAYS_FATAL_IF(addr->sa_family != AF_VSOCK, "address is not a vsock"); - LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated"); - const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr); - return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext)); - }); - - server->join(); + if (cid != VMADDR_CID_ANY) { + server->setConnectionFilter([=](const void* addr, size_t addrlen) { + LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated"); + const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr); + LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock"); + if (cid != vaddr->svm_cid) { + LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid; + return false; + } + return true; + }); + } + server->setRootObject(AIBinder_toPlatformBinder(service)); + return createObjectHandle<ARpcServer>(server); +} - // Shutdown any open sessions since server failed. - (void)server->shutdown(); - return true; +ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) { + auto server = RpcServer::make(); + auto fd = unique_fd(android_get_control_socket(name)); + if (!fd.ok()) { + LOG(ERROR) << "Failed to get fd for the socket:" << name; + 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(); + return nullptr; + } + server->setRootObject(AIBinder_toPlatformBinder(service)); + return createObjectHandle<ARpcServer>(server); } -bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param), - void* param) { +ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd) { auto server = RpcServer::make(); - if (status_t status = server->setupVsockServer(port); status != OK) { - LOG(ERROR) << "Failed to set up vsock server with port " << port + auto fd = unique_fd(bootstrapFd); + if (!fd.ok()) { + LOG(ERROR) << "Invalid bootstrap fd " << 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(); - return false; + return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); + return createObjectHandle<ARpcServer>(server); +} - if (readyCallback) readyCallback(param); - server->join(); +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(); + return nullptr; + } + server->setRootObject(AIBinder_toPlatformBinder(service)); + return createObjectHandle<ARpcServer>(server); +} - // Shutdown any open sessions since server failed. - (void)server->shutdown(); - return true; +void ARpcServer_setSupportedFileDescriptorTransportModes( + ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[], + size_t modes_len) { + auto server = handleToStrongPointer<RpcServer>(handle); + std::vector<RpcSession::FileDescriptorTransportMode> modevec; + for (size_t i = 0; i < modes_len; i++) { + modevec.push_back(toTransportMode(modes[i])); + } + server->setSupportedFileDescriptorTransportModes(modevec); +} + +void ARpcServer_start(ARpcServer* handle) { + handleToStrongPointer<RpcServer>(handle)->start(); +} + +void ARpcServer_join(ARpcServer* handle) { + handleToStrongPointer<RpcServer>(handle)->join(); } -bool RunRpcServer(AIBinder* service, unsigned int port) { - return RunRpcServerCallback(service, port, nullptr, nullptr); +bool ARpcServer_shutdown(ARpcServer* handle) { + return handleToStrongPointer<RpcServer>(handle)->shutdown(); } -AIBinder* RpcClient(unsigned int cid, unsigned int port) { +void ARpcServer_free(ARpcServer* handle) { + // Ignore the result of ARpcServer_shutdown - either it had been called + // earlier, or the RpcServer destructor will panic. + (void)ARpcServer_shutdown(handle); + freeObjectHandle<RpcServer>(handle); +} + +ARpcSession* ARpcSession_new() { auto session = RpcSession::make(); + return createObjectHandle<ARpcSession>(session); +} + +void ARpcSession_free(ARpcSession* handle) { + freeObjectHandle<RpcSession>(handle); +} + +AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) { + auto session = handleToStrongPointer<RpcSession>(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(); @@ -84,8 +205,47 @@ AIBinder* RpcClient(unsigned int cid, unsigned int port) { return AIBinder_fromPlatformBinder(session->getRootObject()); } -AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) { - auto session = RpcSession::make(); +AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* name) { + std::string pathname(name); + pathname = ANDROID_SOCKET_DIR "/" + pathname; + auto session = handleToStrongPointer<RpcSession>(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(); + return nullptr; + } + return AIBinder_fromPlatformBinder(session->getRootObject()); +} + +AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bootstrapFd) { + auto session = handleToStrongPointer<RpcSession>(handle); + auto fd = unique_fd(dup(bootstrapFd)); + if (!fd.ok()) { + LOG(ERROR) << "Invalid bootstrap fd " << 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(); + return nullptr; + } + return AIBinder_fromPlatformBinder(session->getRootObject()); +} + +AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) { + auto session = handleToStrongPointer<RpcSession>(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(); + return nullptr; + } + return AIBinder_fromPlatformBinder(session->getRootObject()); +} + +AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param), + void* param) { + auto session = handleToStrongPointer<RpcSession>(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(); @@ -93,4 +253,20 @@ AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) { } return AIBinder_fromPlatformBinder(session->getRootObject()); } + +void ARpcSession_setFileDescriptorTransportMode(ARpcSession* handle, + ARpcSession_FileDescriptorTransportMode mode) { + auto session = handleToStrongPointer<RpcSession>(handle); + session->setFileDescriptorTransportMode(toTransportMode(mode)); +} + +void ARpcSession_setMaxIncomingThreads(ARpcSession* handle, size_t threads) { + auto session = handleToStrongPointer<RpcSession>(handle); + session->setMaxIncomingThreads(threads); +} + +void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) { + auto session = handleToStrongPointer<RpcSession>(handle); + session->setMaxOutgoingThreads(threads); +} } diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt index e8565695a3..63679c28d0 100644 --- a/libs/binder/libbinder_rpc_unstable.map.txt +++ b/libs/binder/libbinder_rpc_unstable.map.txt @@ -1,8 +1,14 @@ LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only global: - RunRpcServer; - RunRpcServerCallback; - RpcClient; + ARpcServer_free; + ARpcServer_join; + ARpcServer_newInet; + ARpcServer_newInitUnixDomain; + ARpcServer_newVsock; + ARpcServer_shutdown; + ARpcServer_start; + VsockRpcClient; + UnixDomainRpcClient; RpcPreconnectedClient; local: *; diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 79c8c8f88b..58ed4188e2 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -57,6 +57,7 @@ cc_library { srcs: [ "ibinder.cpp", "ibinder_jni.cpp", + "libbinder.cpp", "parcel.cpp", "parcel_jni.cpp", "process.cpp", @@ -65,10 +66,14 @@ cc_library { "service_manager.cpp", ], - shared_libs: [ + static_libs: [ "libandroid_runtime_lazy", "libbase", + ], + + shared_libs: [ "libbinder", + "liblog", "libutils", ], @@ -113,6 +118,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "-clang-analyzer-core.CallAndMessage", @@ -180,4 +186,8 @@ ndk_library { name: "libbinder_ndk", symbol_file: "libbinder_ndk.map.txt", first_version: "29", + export_header_libs: [ + "libbinder_ndk_headers", + "libbinder_ndk_helper_headers", + ], } diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 28e3ff43ff..d0de7b96b5 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -14,21 +14,19 @@ * limitations under the License. */ +#include <android-base/logging.h> #include <android/binder_ibinder.h> #include <android/binder_ibinder_platform.h> -#include <android/binder_libbinder.h> -#include "ibinder_internal.h" - #include <android/binder_stability.h> #include <android/binder_status.h> -#include "parcel_internal.h" -#include "status_internal.h" - -#include <android-base/logging.h> #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> #include <private/android_filesystem_config.h> +#include "ibinder_internal.h" +#include "parcel_internal.h" +#include "status_internal.h" + using DeathRecipient = ::android::IBinder::DeathRecipient; using ::android::IBinder; @@ -36,6 +34,7 @@ using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; using ::android::wp; @@ -63,6 +62,9 @@ struct Value { wp<ABpBinder> binder; }; void clean(const void* id, void* obj, void* cookie) { + // be weary of leaks! + // LOG(INFO) << "Deleting an ABpBinder"; + CHECK(id == kId) << id << " " << obj << " " << cookie; delete static_cast<Value*>(obj); @@ -73,12 +75,48 @@ void clean(const void* id, void* obj, void* cookie) { AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {} AIBinder::~AIBinder() {} -std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz, - const String16& newDescriptor, bool set) { +// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char +static std::string SanitizeString(const String16& str) { + std::string sanitized{String8(str)}; + for (auto& c : sanitized) { + if (!isprint(c)) { + c = '?'; + } + } + return sanitized; +} + +bool AIBinder::associateClass(const AIBinder_Class* clazz) { + if (clazz == nullptr) return false; + + // If mClazz is non-null, this must have been called and cached + // already. So, we can safely call this first. Due to the implementation + // of getInterfaceDescriptor (at time of writing), two simultaneous calls + // may lead to extra binder transactions, but this is expected to be + // exceedingly rare. Once we have a binder, when we get it again later, + // we won't make another binder transaction here. + const String16& descriptor = getBinder()->getInterfaceDescriptor(); + const String16& newDescriptor = clazz->getInterfaceDescriptor(); + std::lock_guard<std::mutex> lock(mClazzMutex); if (mClazz == clazz) return true; - if (mClazz != nullptr) { + // If this is an ABpBinder, the first class object becomes the canonical one. The implication + // of this is that no API can require a proxy information to get information on how to behave. + // from the class itself - which should only store the interface descriptor. The functionality + // should be implemented by adding AIBinder_* APIs to set values on binders themselves, by + // setting things on AIBinder_Class which get transferred along with the binder, so that they + // can be read along with the BpBinder, or by modifying APIs directly (e.g. an option in + // onTransact). + // + // While this check is required to support linkernamespaces, one downside of it is that + // you may parcel code to communicate between things in the same process. However, comms + // between linkernamespaces like this already happen for cross-language calls like Java<->C++ + // or Rust<->Java, and there are good stability guarantees here. This interacts with + // binder Stability checks exactly like any other in-process call. The stability is known + // to the IBinder object, so that it doesn't matter if a class object comes from + // a different stability level. + if (mClazz != nullptr && !asABpBinder()) { const String16& currentDescriptor = mClazz->getInterfaceDescriptor(); if (newDescriptor == currentDescriptor) { LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor @@ -95,37 +133,10 @@ std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz return false; } - if (set) { - // if this is a local object, it's not one known to libbinder_ndk - mClazz = clazz; - return true; - } - - return {}; -} - -// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char -static std::string SanitizeString(const String16& str) { - std::string sanitized{String8(str)}; - for (auto& c : sanitized) { - if (!isprint(c)) { - c = '?'; - } - } - return sanitized; -} - -bool AIBinder::associateClass(const AIBinder_Class* clazz) { - if (clazz == nullptr) return false; - - const String16& newDescriptor = clazz->getInterfaceDescriptor(); - - auto result = associateClassInternal(clazz, newDescriptor, false); - if (result.has_value()) return *result; - - CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor - - const String16& descriptor = getBinder()->getInterfaceDescriptor(); + // This will always be an O(n) comparison, but it's expected to be extremely rare. + // 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 (getBinder()->isBinderAlive()) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor @@ -133,12 +144,20 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { } else { // b/155793159 LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor - << "' to dead binder."; + << "' to dead binder with cached descriptor '" << SanitizeString(descriptor) + << "'."; } return false; } - return associateClassInternal(clazz, newDescriptor, true).value(); + // A local binder being set for the first time OR + // ignoring a proxy binder which is set multiple time, by considering the first + // associated class as the canonical one. + if (mClazz == nullptr) { + mClazz = clazz; + } + + return true; } ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) @@ -237,26 +256,11 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce } ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) - : AIBinder(nullptr /*clazz*/), BpRefBase(binder) { + : AIBinder(nullptr /*clazz*/), mRemote(binder) { CHECK(binder != nullptr); } ABpBinder::~ABpBinder() {} -void ABpBinder::onLastStrongRef(const void* id) { - // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for - // the ABpBinder to be deleted. Even though we have no more references on the ABpBinder - // (BpRefBase), the remote object may still exist (for instance, if we - // receive it from another process, before the ABpBinder is attached). - - ABpBinderTag::Value* value = - static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId)); - CHECK_NE(nullptr, value) << "ABpBinder must always be attached"; - - remote()->withLock([&]() { value->binder = nullptr; }); - - BpRefBase::onLastStrongRef(id); -} - sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) { if (binder == nullptr) { return nullptr; @@ -337,6 +341,10 @@ bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) { return lhs->binder < rhs->binder; } +// WARNING: When multiple classes exist with the same interface descriptor in different +// linkernamespaces, the first one to be associated with mClazz becomes the canonical one +// and the only requirement on this is that the interface descriptors match. If this +// is an ABpBinder, no other state can be referenced from mClazz. AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) @@ -458,7 +466,8 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& 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."; + << ": removed reference to death recipient but unlink failed: " + << statusToString(status); } return PruneStatusT(status); } @@ -539,7 +548,8 @@ 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 and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -550,7 +560,8 @@ 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 and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -625,7 +636,8 @@ 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."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder + << ") and in (" << in << ")."; return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); @@ -640,6 +652,10 @@ binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { (*in)->get()->markForBinder(binder->getBinder()); status_t status = android::OK; + + // note - this is the only read of a value in clazz, and it comes with a warning + // on the API itself. Do not copy this design. Instead, attach data in a new + // version of the prepareTransaction function. if (clazz->writeHeader) { status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor()); } @@ -671,7 +687,9 @@ 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."; + LOG(ERROR) << __func__ + << ": Only user-defined transactions can be made from the NDK, but requested: " + << code; return STATUS_UNKNOWN_TRANSACTION; } @@ -682,7 +700,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa } if (binder == nullptr || *in == nullptr || out == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in (" + << in << "), and out (" << out << ")."; return STATUS_UNEXPECTED_NULL; } @@ -785,17 +804,6 @@ const char* AIBinder_getCallingSid() { return ::android::IPCThreadState::self()->getCallingSid(); } -android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder) { - if (binder == nullptr) return nullptr; - return binder->getBinder(); -} - -AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder) { - sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder); - AIBinder_incStrong(ndkBinder.get()); - return ndkBinder.get(); -} - void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) { binder->asABBinder()->setMinSchedulerPolicy(policy, priority); } diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 730e51b3e3..67bb092f0f 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -53,12 +53,14 @@ struct AIBinder : public virtual ::android::RefBase { } private: - std::optional<bool> associateClassInternal(const AIBinder_Class* clazz, - const ::android::String16& newDescriptor, bool set); - // AIBinder instance is instance of this class for a local object. In order to transact on a // remote object, this also must be set for simplicity (although right now, only the // interfaceDescriptor from it is used). + // + // WARNING: When multiple classes exist with the same interface descriptor in different + // linkernamespaces, the first one to be associated with mClazz becomes the canonical one + // and the only requirement on this is that the interface descriptors match. If this + // is an ABpBinder, no other state can be referenced from mClazz. const AIBinder_Class* mClazz; std::mutex mClazzMutex; }; @@ -91,7 +93,7 @@ struct ABBinder : public AIBinder, public ::android::BBinder { // This binder object may be remote or local (even though it is 'Bp'). The implication if it is // local is that it is an IBinder object created outside of the domain of libbinder_ndk. -struct ABpBinder : public AIBinder, public ::android::BpRefBase { +struct ABpBinder : public AIBinder { // Looks up to see if this object has or is an existing ABBinder or ABpBinder object, otherwise // it creates an ABpBinder object. static ::android::sp<AIBinder> lookupOrCreateFromBinder( @@ -99,14 +101,13 @@ struct ABpBinder : public AIBinder, public ::android::BpRefBase { virtual ~ABpBinder(); - void onLastStrongRef(const void* id) override; - - ::android::sp<::android::IBinder> getBinder() override { return remote(); } + ::android::sp<::android::IBinder> getBinder() override { return mRemote; } ABpBinder* asABpBinder() override { return this; } private: friend android::sp<ABpBinder>; explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); + ::android::sp<::android::IBinder> mRemote; }; 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 7ea9be797b..d6937c2c52 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -30,11 +30,11 @@ #include <android/binder_internal_logging.h> #include <android/binder_parcel.h> #include <android/binder_status.h> - #include <assert.h> - #include <unistd.h> + #include <cstddef> +#include <iostream> #include <string> namespace ndk { @@ -270,14 +270,19 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nul std::string getDescription() const { #ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ if (__builtin_available(android 30, *)) { -#else - if (__ANDROID_API__ >= 30) { #endif + +#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 30 const char* cStr = AStatus_getDescription(get()); std::string ret = cStr; AStatus_deleteDescription(cStr); return ret; +#endif + +#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ } +#endif + binder_exception_t exception = getExceptionCode(); std::string desc = std::to_string(exception); if (exception == EX_SERVICE_SPECIFIC) { @@ -315,6 +320,11 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nul } }; +static inline std::ostream& operator<<(std::ostream& os, const ScopedAStatus& status) { + return os << status.getDescription(); + return os; +} + /** * Convenience wrapper. See AIBinder_DeathRecipient. */ @@ -349,7 +359,7 @@ class ScopedAIBinder_Weak /** * See AIBinder_Weak_promote. */ - SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); } + SpAIBinder promote() const { return SpAIBinder(AIBinder_Weak_promote(get())); } }; namespace internal { 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 c8e78fc55d..9949de2aac 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -43,10 +43,12 @@ namespace ndk { /** - * analog using std::shared_ptr for internally held refcount + * Binder analog to using std::shared_ptr for an internally held refcount. * * ref must be called at least one time during the lifetime of this object. The recommended way to * construct this object is with SharedRefBase::make. + * + * If you need a "this" shared reference analogous to shared_from_this, use this->ref(). */ class SharedRefBase { public: @@ -194,6 +196,10 @@ class BnCInterface : public INTERFACE { bool isRemote() override final { return false; } + static std::string makeServiceName(std::string_view instance) { + return INTERFACE::descriptor + ("/" + std::string(instance)); + } + protected: /** * This function should only be called by asBinder. Otherwise, there is a possibility of @@ -289,7 +295,10 @@ void ICInterface::ICInterfaceData::onDestroy(void* userData) { binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) { std::shared_ptr<ICInterface> interface = getInterface(binder); - return interface->dump(fd, args, numArgs); + if (interface != nullptr) { + return interface->dump(fd, args, numArgs); + } + return STATUS_DEAD_OBJECT; } #ifdef HAS_BINDER_SHELL_COMMAND @@ -297,7 +306,10 @@ binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binde int err, const char** argv, uint32_t argc) { std::shared_ptr<ICInterface> interface = getInterface(binder); - return interface->handleShellCommand(in, out, err, argv, argc); + if (interface != nullptr) { + return interface->handleShellCommand(in, out, err, argv, argc); + } + return STATUS_DEAD_OBJECT; } #endif diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h index 0bf1e3dba9..95eee26eed 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -1639,7 +1639,6 @@ static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value) return AParcel_writeParcelable(parcel, value); } else { static_assert(dependent_false_v<T>, "unrecognized type"); - return STATUS_OK; } } @@ -1707,7 +1706,6 @@ static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value) return AParcel_readParcelable(parcel, value); } else { static_assert(dependent_false_v<T>, "unrecognized type"); - return STATUS_OK; } } diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index f45aa7631b..caee4711a4 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -41,65 +41,34 @@ enum { if (_status != STATUS_OK) return _status; \ } while (false) +// AParcelableHolder has been introduced in 31. +#if __ANDROID_API__ >= 31 class AParcelableHolder { public: AParcelableHolder() = delete; explicit AParcelableHolder(parcelable_stability_t stability) : mParcel(AParcel_create()), mStability(stability) {} -#if __ANDROID_API__ >= 31 AParcelableHolder(const AParcelableHolder& other) : mParcel(AParcel_create()), mStability(other.mStability) { - // AParcelableHolder has been introduced in 31. -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, - AParcel_getDataSize(other.mParcel.get())); - } + AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(other.mParcel.get())); } -#endif AParcelableHolder(AParcelableHolder&& other) = default; virtual ~AParcelableHolder() = default; binder_status_t writeToParcel(AParcel* parcel) const { RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability))); -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - int32_t size = AParcel_getDataSize(this->mParcel.get()); - RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size)); - } else { - return STATUS_INVALID_OPERATION; - } -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - int32_t size = AParcel_getDataSize(this->mParcel.get()); - RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size)); - } else { - return STATUS_INVALID_OPERATION; - } + int32_t size = AParcel_getDataSize(this->mParcel.get()); + RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size)); + size = AParcel_getDataSize(this->mParcel.get()); + RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size)); return STATUS_OK; } binder_status_t readFromParcel(const AParcel* parcel) { -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_reset(mParcel.get()); - } else { - return STATUS_INVALID_OPERATION; - } + AParcel_reset(mParcel.get()); parcelable_stability_t wireStability; RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability)); @@ -120,15 +89,7 @@ class AParcelableHolder { return STATUS_BAD_VALUE; } -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize); - } else { - status = STATUS_INVALID_OPERATION; - } + status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize); if (status != STATUS_OK) { return status; } @@ -140,15 +101,7 @@ class AParcelableHolder { if (this->mStability > T::_aidl_stability) { return STATUS_BAD_VALUE; } -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_reset(mParcel.get()); - } else { - return STATUS_INVALID_OPERATION; - } + AParcel_reset(mParcel.get()); AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor)); p.writeToParcel(mParcel.get()); return STATUS_OK; @@ -158,17 +111,9 @@ class AParcelableHolder { binder_status_t getParcelable(std::optional<T>* ret) const { const std::string parcelableDesc(T::descriptor); AParcel_setDataPosition(mParcel.get(), 0); -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - if (AParcel_getDataSize(mParcel.get()) == 0) { - *ret = std::nullopt; - return STATUS_OK; - } - } else { - return STATUS_INVALID_OPERATION; + if (AParcel_getDataSize(mParcel.get()) == 0) { + *ret = std::nullopt; + return STATUS_OK; } std::string parcelableDescInParcel; binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel); @@ -185,15 +130,7 @@ class AParcelableHolder { return STATUS_OK; } - void reset() { -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_reset(mParcel.get()); - } - } + void reset() { AParcel_reset(mParcel.get()); } inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; } inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; } @@ -201,11 +138,23 @@ class AParcelableHolder { inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; } inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; } inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; } + inline AParcelableHolder& operator=(const AParcelableHolder& rhs) { + this->reset(); + if (this->mStability != rhs.mStability) { + syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!", + this->mStability, rhs.mStability); + abort(); + } + AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(rhs.mParcel.get())); + return *this; + } private: mutable ndk::ScopedAParcel mParcel; parcelable_stability_t mStability; }; +#endif // __ANDROID_API__ >= 31 #undef RETURN_ON_FAILURE } // namespace ndk diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h index ef71a81328..b0c7f6d4a9 100644 --- a/libs/binder/ndk/include_cpp/android/binder_to_string.h +++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h @@ -49,12 +49,17 @@ #include <android/binder_interface_utils.h> #include <android/binder_parcelable_utils.h> #define HAS_NDK_INTERFACE -#else +#endif + +// TODO: some things include libbinder without having access to libbase. This is +// due to frameworks/native/include, which symlinks to libbinder headers, so even +// though we don't use it here, we detect a different header, so that we are more +// confident libbase will be included +#if __has_include(<binder/RpcSession.h>) #include <binder/IBinder.h> #include <binder/IInterface.h> -#include <binder/ParcelFileDescriptor.h> -#include <binder/ParcelableHolder.h> -#endif //_has_include +#define HAS_CPP_INTERFACE +#endif namespace android { namespace internal { @@ -104,10 +109,12 @@ class IsPointerLike { IsInstantiationOf<_U, sp>::value || // for IBinder and interface types in the C++ // backend #endif - IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the - // C++/NDK backends - IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the - // NDK backends + IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the + // C++/NDK backends + IsInstantiationOf<_U, std::unique_ptr>::value || // for @nullable(heap=true) + // in C++/NDK backends + IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the + // NDK backends std::true_type> _test(int); @@ -132,46 +139,52 @@ class IsIterable { }; template <typename _T> -class ToEmptyString { - template <typename _U> - static std::enable_if_t< -#ifdef HAS_NDK_INTERFACE - std::is_base_of_v<::ndk::ICInterface, _U> || - std::is_same_v<::ndk::AParcelableHolder, _U> -#else - std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> || - std::is_same_v<os::ParcelFileDescriptor, _U> || - std::is_same_v<os::ParcelableHolder, _U> -#endif - , - std::true_type> - _test(int); - template <typename _U> - static std::false_type _test(...); - - public: - enum { value = decltype(_test<_T>(0))::value }; +struct TypeDependentFalse { + enum { value = false }; }; } // namespace details template <typename _T> std::string ToString(const _T& t) { - if constexpr (details::ToEmptyString<_T>::value) { - return ""; - } else if constexpr (std::is_same_v<bool, _T>) { + if constexpr (std::is_same_v<bool, _T>) { return t ? "true" : "false"; } else if constexpr (std::is_same_v<char16_t, _T>) { + // TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the + // warnings. There's no replacement in the standard library yet. + _Pragma("clang diagnostic push") + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\""); return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(t); + _Pragma("clang diagnostic pop"); } else if constexpr (std::is_arithmetic_v<_T>) { return std::to_string(t); } else if constexpr (std::is_same_v<std::string, _T>) { return t; #ifdef HAS_NDK_INTERFACE } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) { - return (t.get() == nullptr) ? "(null)" : ""; + std::stringstream ss; + ss << "binder:" << std::hex << t.get(); + return ss.str(); } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) { - return (t.get() == -1) ? "(null)" : ""; + return "fd:" + std::to_string(t.get()); + } else if constexpr (std::is_base_of_v<::ndk::ICInterface, _T>) { + // TODO(b/266248339): this format is to make it easy to handle resolv_integration_test + // freezing the output format. We would like to print more info. + return "<interface>"; +#if __ANDROID_API__ >= 31 + } else if constexpr (std::is_same_v<::ndk::AParcelableHolder, _T>) { + return "AParcelableHolder"; +#endif +#endif // HAS_NDK_INTERFACE +#ifdef HAS_CPP_INTERFACE + } else if constexpr (std::is_base_of_v<IInterface, _T>) { + std::stringstream ss; + ss << "interface:" << std::hex << &t; + return ss.str(); + } else if constexpr (std::is_same_v<IBinder, _T>) { + std::stringstream ss; + ss << "binder:" << std::hex << &t; + return ss.str(); #endif #ifdef HAS_STRING16 } else if constexpr (std::is_same_v<String16, _T>) { @@ -205,11 +218,27 @@ std::string ToString(const _T& t) { out << "]"; return out.str(); } else { - return "{no toString() implemented}"; + static_assert(details::TypeDependentFalse<_T>::value, "no toString implemented, huh?"); } } } // namespace internal } // namespace android +#ifdef HAS_STRONG_POINTER +#undef HAS_STRONG_POINTER +#endif + +#ifdef HAS_STRING16 +#undef HAS_STRING16 +#endif + +#ifdef HAS_NDK_INTERFACE +#undef HAS_NDK_INTERFACE +#endif + +#ifdef HAS_CPP_INTERFACE +#undef HAS_CPP_INTERFACE +#endif + /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 41638976cd..db2d2c1b09 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -229,6 +229,11 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __I * * Available since API level 33. * + * WARNING: this API interacts badly with linkernamespaces. For correct behavior, you must + * use it on all instances of a class in the same process which share the same interface + * descriptor. In general, it is recommended you do not use this API, because it is disabling + * type safety. + * * \param clazz class to disable interface header on. */ void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(33); @@ -589,6 +594,9 @@ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_ * * See also AIBinder_linkToDeath/AIBinder_unlinkToDeath. * + * WARNING: Make sure the lifetime of this cookie is long enough. If it is dynamically + * allocated, it should be deleted with AIBinder_DeathRecipient_setOnUnlinked. + * * Available since API level 33. * * \param cookie the cookie passed to AIBinder_linkToDeath. @@ -600,6 +608,9 @@ typedef void (*AIBinder_DeathRecipient_onBinderUnlinked)(void* cookie) __INTRODU * * Available since API level 29. * + * WARNING: Make sure the lifetime of this cookie is long enough. If it is dynamically + * allocated, it should be deleted with AIBinder_DeathRecipient_setOnUnlinked. + * * \param onBinderDied the callback to call when this death recipient is invoked. * * \return the newly constructed object (or null if onBinderDied is null). @@ -613,7 +624,8 @@ __attribute__((warn_unused_result)) AIBinder_DeathRecipient* AIBinder_DeathRecip * * 1. If the binder died, shortly after the call to onBinderDied. * 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or - * AIBinder_DeathRecipient_delete. + * AIBinder_DeathRecipient_delete, after any pending onBinderDied calls + * finish. * 3. During or shortly after the AIBinder_linkToDeath call if it returns an error. * * It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h index 6880d86e1a..8e288b307e 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h @@ -56,6 +56,12 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en * If the binder is null, null is returned. If this binder object was originally an IBinder object, * the original java object will be returned. * + * WARNING: this function returns global and local references. This can be + * figured out using GetObjectRefType. Though, when this function is called + * from within a Java context, the local ref will automatically be cleaned + * up. If this is called outside of a Java frame, + * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup. + * * Available since API level 29. * * \param env Java environment. Must not be null. diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index 84575811f0..f68612c3ba 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -59,6 +59,11 @@ void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29); /** * Sets the position within the parcel. * + * This must be called with a position that has been previously returned from + * AParcel_getDataPosition. If writes are made after setting the data position, they must + * be made in the exact same sequence used before resetting data position. Writing over + * objects such as binders or file descriptors is not supported. + * * Available since API level 29. * * \param parcel The parcel of which to set the position. diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h index f0c00e87e6..74a7157314 100644 --- a/libs/binder/ndk/include_platform/android/binder_libbinder.h +++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h @@ -16,10 +16,12 @@ #pragma once -#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) +#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)) || defined(__TRUSTY__) #include <android/binder_ibinder.h> +#include <android/binder_parcel.h> #include <binder/IBinder.h> +#include <binder/Parcel.h> /** * Get libbinder version of binder from AIBinder. @@ -47,4 +49,26 @@ android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder); */ AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder); +/** + * View libbinder version of parcel from AParcel (mutable). + * + * The lifetime of the returned parcel is the lifetime of the input AParcel. + * Do not ues this reference after dropping the AParcel. + * + * \param parcel non-null parcel with ownership retained by client + * \return platform parcel object + */ +android::Parcel* AParcel_viewPlatformParcel(AParcel* parcel); + +/** + * View libbinder version of parcel from AParcel (const version). + * + * The lifetime of the returned parcel is the lifetime of the input AParcel. + * Do not ues this reference after dropping the AParcel. + * + * \param parcel non-null parcel with ownership retained by client + * \return platform parcel object + */ +const android::Parcel* AParcel_viewPlatformParcel(const AParcel* parcel); + #endif diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index dfa8ea28e7..43159d8ba2 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -22,6 +22,16 @@ __BEGIN_DECLS +enum AServiceManager_AddServiceFlag : uint32_t { + /** + * This allows processes with AID_ISOLATED to get the binder of the service added. + * + * Services with methods that perform file IO, web socket creation or ways to egress data must + * not be added with this flag for privacy concerns. + */ + ADD_SERVICE_ALLOW_ISOLATED = 1, +}; + /** * This registers the service with the default service manager under this instance name. This does * not take ownership of binder. @@ -38,6 +48,23 @@ __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServic AIBinder* binder, const char* instance) __INTRODUCED_IN(29); /** + * This registers the service with the default service manager under this instance name. This does + * not take ownership of binder. + * + * WARNING: when using this API across an APEX boundary, do not use with unstable + * AIDL services. TODO(b/139325195) + * + * \param binder object to register globally with the service manager. + * \param instance identifier of the service. This will be used to lookup the service. + * \param flag an AServiceManager_AddServiceFlag enum to denote how the service should be added. + * + * \return EX_NONE on success. + */ +__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithFlag( + AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flag) + __INTRODUCED_IN(34); + +/** * Gets a binder object with this specific instance name. Will return nullptr immediately if the * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this * function is responsible for calling AIBinder_decStrong). @@ -68,6 +95,7 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const * * \param instance identifier of the service used to lookup the service. */ +[[deprecated("this polls 5s, use AServiceManager_waitForService or AServiceManager_checkService")]] __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance) __INTRODUCED_IN(29); @@ -108,6 +136,67 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(con __INTRODUCED_IN(31); /** + * Function to call when a service is registered. The instance is passed as well as + * ownership of the binder named 'registered'. + * + * WARNING: a lock is held when this method is called in order to prevent races with + * AServiceManager_NotificationRegistration_delete. Do not make synchronous binder calls when + * implementing this method to avoid deadlocks. + * + * \param instance instance name of service registered + * \param registered ownership-passed instance of service registered + * \param cookie data passed during registration for notifications + */ +typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered, + void* cookie); + +/** + * Represents a registration to servicemanager which can be cleared anytime. + */ +struct AServiceManager_NotificationRegistration; + +/** + * Get notifications when a service is registered. If the service is already registered, + * you will immediately get a notification. + * + * WARNING: it is strongly recommended to use AServiceManager_waitForService API instead. + * That API will wait synchronously, which is what you usually want in cases, including + * using some feature or during boot up. There is a history of bugs where waiting for + * notifications like this races with service startup. Also, when this API is used, a service + * bug will result in silent failure (rather than a debuggable deadlock). Furthermore, there + * is a history of this API being used to know when a service is up as a proxy for whethre + * that service should be started. This should only be used if you are intending to get + * ahold of the service as a client. For lazy services, whether a service is registered + * should not be used as a proxy for when it should be registered, which is only known + * by the real client. + * + * WARNING: if you use this API, you must also ensure that you check missing services are + * started and crash otherwise. If service failures are ignored, the system rots. + * + * \param instance name of service to wait for notifications about + * \param onRegister callback for when service is registered + * \param cookie data associated with this callback + * + * \return the token for this registration. Deleting this token will unregister. + */ +__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* +AServiceManager_registerForServiceNotifications(const char* instance, + AServiceManager_onRegister onRegister, void* cookie) + __INTRODUCED_IN(34); + +/** + * Unregister for notifications and delete the object. + * + * After this method is called, the callback is guaranteed to no longer be invoked. This will block + * until any in-progress onRegister callbacks have completed. It is therefore safe to immediately + * destroy the void* cookie that was registered when this method returns. + * + * \param notification object to dismiss + */ +void AServiceManager_NotificationRegistration_delete( + AServiceManager_NotificationRegistration* notification) __INTRODUCED_IN(34); + +/** * Check if a service is declared (e.g. VINTF manifest). * * \param instance identifier of the service. @@ -143,6 +232,17 @@ void AServiceManager_forEachDeclaredInstance(const char* interface, void* contex bool AServiceManager_isUpdatableViaApex(const char* instance) __INTRODUCED_IN(31); /** + * Returns the APEX name if a service is declared as updatable via an APEX module. + * + * \param instance identifier of the service + * \param context to pass to callback + * \param callback taking the APEX name (e.g. 'com.android.foo') and context + */ +void AServiceManager_getUpdatableApexName(const char* instance, void* context, + void (*callback)(const char*, void*)) + __INTRODUCED_IN(__ANDROID_API_U__); + +/** * Prevent lazy services without client from shutting down their process * * This should only be used if it is every eventually set to false. If a diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index f408fadee0..ffcad559f6 100644 --- a/libs/binder/ndk/include_platform/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -28,17 +28,33 @@ __BEGIN_DECLS * * When using this, it is expected that ABinderProcess_setupPolling and * ABinderProcess_handlePolledCommands are not used. + * + * 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. */ void ABinderProcess_startThreadPool(); /** * 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. + * + * 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. */ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); /** + * Check if the threadpool has already been started. + * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually, + * you should use this in a library to abort if the threadpool is not started. + * Programs should configure binder threadpools once at the beginning. + */ +bool ABinderProcess_isThreadPoolStarted(); +/** * This adds the current thread to the threadpool. This may cause the threadpool to exceed the * maximum size. + * + * 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. */ void ABinderProcess_joinThreadPool(); diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index d0cd11f6fc..c1f62e58e9 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -50,6 +50,15 @@ static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { * requirements associated with that higher stability level. For instance, a * VINTF stability binder is required to be in the VINTF manifest. This API * can be called to use that same interface within the vendor partition. + * + * WARNING: you must hold on to a binder instance after this is set, while you + * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must + * save this binder and then + * use it. For instance: + * + * auto binder = ...->asBinder(); + * AIBinder_forceDowngradeToVendorStability(binder.get()); + * doSomething(binder); */ void AIBinder_forceDowngradeToVendorStability(AIBinder* binder); @@ -79,6 +88,15 @@ static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { * requirements associated with that higher stability level. For instance, a * VINTF stability binder is required to be in the VINTF manifest. This API * can be called to use that same interface within the system partition. + * + * WARNING: you must hold on to a binder instance after this is set, while you + * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must + * save this binder and then + * use it. For instance: + * + * auto binder = ...->asBinder(); + * AIBinder_forceDowngradeToSystemStability(binder.get()); + * doSomething(binder); */ void AIBinder_forceDowngradeToSystemStability(AIBinder* binder); @@ -98,9 +116,9 @@ static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) { * This interface has system<->vendor stability */ // b/227835797 - can't use __INTRODUCED_IN(30) because old targets load this code -#if __ANDROID_MIN_SDK_VERSION__ < 30 +#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30 __attribute__((weak)) -#endif // __ANDROID_MIN_SDK_VERSION__ < 30 +#endif // defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30 void AIBinder_markVintfStability(AIBinder* binder); __END_DECLS diff --git a/libs/binder/ndk/libbinder.cpp b/libs/binder/ndk/libbinder.cpp new file mode 100644 index 0000000000..f94d81d8fb --- /dev/null +++ b/libs/binder/ndk/libbinder.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_libbinder.h> + +#include "ibinder_internal.h" +#include "parcel_internal.h" + +using ::android::IBinder; +using ::android::Parcel; +using ::android::sp; + +sp<IBinder> AIBinder_toPlatformBinder(AIBinder* binder) { + if (binder == nullptr) return nullptr; + return binder->getBinder(); +} + +AIBinder* AIBinder_fromPlatformBinder(const sp<IBinder>& binder) { + sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ndkBinder.get()); + return ndkBinder.get(); +} + +Parcel* AParcel_viewPlatformParcel(AParcel* parcel) { + return parcel->get(); +} + +const Parcel* AParcel_viewPlatformParcel(const AParcel* parcel) { + return parcel->get(); +} diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 3824a1b44c..1078fb2b16 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -89,12 +89,12 @@ LIBBINDER_NDK { # introduced=29 AStatus_getStatus; AStatus_isOk; AStatus_newOk; - ABinderProcess_joinThreadPool; # apex llndk - ABinderProcess_setThreadPoolMaxThreadCount; # apex llndk - ABinderProcess_startThreadPool; # apex llndk - AServiceManager_addService; # apex llndk - AServiceManager_checkService; # apex llndk - AServiceManager_getService; # apex llndk + ABinderProcess_joinThreadPool; # systemapi llndk + ABinderProcess_setThreadPoolMaxThreadCount; # systemapi llndk + ABinderProcess_startThreadPool; # systemapi llndk + AServiceManager_addService; # systemapi llndk + AServiceManager_checkService; # systemapi llndk + AServiceManager_getService; # systemapi llndk }; LIBBINDER_NDK30 { # introduced=30 @@ -105,30 +105,30 @@ LIBBINDER_NDK30 { # introduced=30 AStatus_deleteDescription; AParcel_fromJavaParcel; - AIBinder_markSystemStability; # apex + AIBinder_markSystemStability; # systemapi AIBinder_markVendorStability; # llndk - AIBinder_markVintfStability; # apex llndk - AIBinder_Class_setHandleShellCommand; # apex llndk + AIBinder_markVintfStability; # systemapi llndk + AIBinder_Class_setHandleShellCommand; # systemapi llndk }; LIBBINDER_NDK31 { # introduced=31 global: - ABinderProcess_handlePolledCommands; # apex - ABinderProcess_setupPolling; # apex - AIBinder_getCallingSid; # apex - AIBinder_setRequestingSid; # apex + ABinderProcess_handlePolledCommands; # systemapi + ABinderProcess_setupPolling; # systemapi + AIBinder_getCallingSid; # systemapi + AIBinder_setRequestingSid; # systemapi AParcel_markSensitive; # systemapi llndk - AServiceManager_forEachDeclaredInstance; # apex llndk - AServiceManager_forceLazyServicesPersist; # apex llndk - AServiceManager_isDeclared; # apex llndk - AServiceManager_isUpdatableViaApex; # apex + AServiceManager_forEachDeclaredInstance; # systemapi llndk + AServiceManager_forceLazyServicesPersist; # systemapi llndk + AServiceManager_isDeclared; # systemapi llndk + AServiceManager_isUpdatableViaApex; # systemapi AServiceManager_reRegister; # llndk - AServiceManager_registerLazyService; # apex llndk + AServiceManager_registerLazyService; # systemapi llndk AServiceManager_setActiveServicesCallback; # llndk AServiceManager_tryUnregister; # llndk - AServiceManager_waitForService; # apex llndk + AServiceManager_waitForService; # systemapi llndk - AIBinder_forceDowngradeToSystemStability; # apex + AIBinder_forceDowngradeToSystemStability; # systemapi AIBinder_forceDowngradeToVendorStability; # llndk AIBinder_Class_getDescriptor; @@ -146,18 +146,28 @@ LIBBINDER_NDK33 { # introduced=33 AIBinder_Class_disableInterfaceTokenHeader; AIBinder_DeathRecipient_setOnUnlinked; AIBinder_isHandlingTransaction; - AIBinder_setInheritRt; # llndk - AIBinder_setMinSchedulerPolicy; # llndk + AIBinder_setInheritRt; # systemapi llndk + AIBinder_setMinSchedulerPolicy; # systemapi llndk AParcel_marshal; AParcel_unmarshal; }; +LIBBINDER_NDK34 { # introduced=UpsideDownCake + global: + ABinderProcess_isThreadPoolStarted; # systemapi llndk + AServiceManager_getUpdatableApexName; # systemapi + AServiceManager_registerForServiceNotifications; # systemapi llndk + AServiceManager_NotificationRegistration_delete; # systemapi llndk + AServiceManager_addServiceWithFlag; # systemapi llndk +}; + LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; extern "C++" { AIBinder_fromPlatformBinder*; AIBinder_toPlatformBinder*; + AParcel_viewPlatformParcel*; }; local: *; diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index c320e8d6df..b5a2e2ff0b 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -129,7 +129,13 @@ binder_status_t ReadArray(const AParcel* parcel, void* arrayData, } T* array; - if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; + if (!allocator(arrayData, length, &array)) { + if (length < 0) { + return STATUS_UNEXPECTED_NULL; + } else { + return STATUS_NO_MEMORY; + } + } if (length <= 0) return STATUS_OK; if (array == nullptr) return STATUS_NO_MEMORY; @@ -157,7 +163,13 @@ binder_status_t ReadArray<char16_t>(const AParcel* parcel, void* arrayData, } char16_t* array; - if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; + if (!allocator(arrayData, length, &array)) { + if (length < 0) { + return STATUS_UNEXPECTED_NULL; + } else { + return STATUS_NO_MEMORY; + } + } if (length <= 0) return STATUS_OK; if (array == nullptr) return STATUS_NO_MEMORY; @@ -204,7 +216,13 @@ binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ArrayAllocator return status; } - if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; + if (!allocator(arrayData, length)) { + if (length < 0) { + return STATUS_UNEXPECTED_NULL; + } else { + return STATUS_NO_MEMORY; + } + } if (length <= 0) return STATUS_OK; @@ -677,11 +695,17 @@ binder_status_t AParcel_marshal(const AParcel* parcel, uint8_t* buffer, size_t s if (parcel->get()->objectsCount()) { return STATUS_INVALID_OPERATION; } - int32_t dataSize = AParcel_getDataSize(parcel); + // b/264739302 - getDataSize will return dataPos if it is greater than dataSize + // which will cause crashes in memcpy at later point. Instead compare with + // actual length of internal buffer + int32_t dataSize = parcel->get()->dataBufferSize(); if (len > static_cast<size_t>(dataSize) || start > static_cast<size_t>(dataSize) - len) { return STATUS_BAD_VALUE; } const uint8_t* internalBuffer = parcel->get()->data(); + if (internalBuffer == nullptr) { + return STATUS_UNEXPECTED_NULL; + } memcpy(buffer, internalBuffer + start, len); return STATUS_OK; } diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp index ac582a412e..bc6610e3b9 100644 --- a/libs/binder/ndk/process.cpp +++ b/libs/binder/ndk/process.cpp @@ -31,6 +31,9 @@ void ABinderProcess_startThreadPool() { bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) { return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0; } +bool ABinderProcess_isThreadPoolStarted() { + return ProcessState::self()->isThreadPoolStarted(); +} void ABinderProcess_joinThreadPool() { IPCThreadState::self()->joinThreadPool(); } diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 7649a26ff6..84da459454 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -28,6 +28,7 @@ using ::android::IBinder; using ::android::IServiceManager; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; @@ -40,6 +41,20 @@ binder_exception_t AServiceManager_addService(AIBinder* binder, const char* inst status_t exception = sm->addService(String16(instance), binder->getBinder()); return PruneException(exception); } + +binder_exception_t AServiceManager_addServiceWithFlag(AIBinder* binder, const char* instance, + const AServiceManager_AddServiceFlag flag) { + if (binder == nullptr || instance == nullptr) { + return EX_ILLEGAL_ARGUMENT; + } + + sp<IServiceManager> sm = defaultServiceManager(); + + bool allowIsolated = flag & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED; + status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated); + return PruneException(exception); +} + AIBinder* AServiceManager_checkService(const char* instance) { if (instance == nullptr) { return nullptr; @@ -86,6 +101,67 @@ AIBinder* AServiceManager_waitForService(const char* instance) { AIBinder_incStrong(ret.get()); return ret.get(); } +typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered, + void* cookie); + +struct AServiceManager_NotificationRegistration + : public IServiceManager::LocalRegistrationCallback { + std::mutex m; + const char* instance = nullptr; + void* cookie = nullptr; + AServiceManager_onRegister onRegister = nullptr; + + virtual void onServiceRegistration(const String16& smInstance, const sp<IBinder>& binder) { + std::lock_guard<std::mutex> l(m); + if (onRegister == nullptr) return; + + CHECK_EQ(String8(smInstance), instance); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + + onRegister(instance, ret.get(), cookie); + } + + void clear() { + std::lock_guard<std::mutex> l(m); + instance = nullptr; + cookie = nullptr; + onRegister = nullptr; + } +}; + +__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; + // cookie can be nullptr + + auto cb = sp<AServiceManager_NotificationRegistration>::make(); + cb->instance = instance; + cb->onRegister = onRegister; + cb->cookie = cookie; + + sp<IServiceManager> 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); + return nullptr; + } + + cb->incStrong(nullptr); + return cb.get(); +} + +void AServiceManager_NotificationRegistration_delete( + AServiceManager_NotificationRegistration* notification) { + CHECK_NE(notification, nullptr); + notification->clear(); + notification->decStrong(nullptr); +} + bool AServiceManager_isDeclared(const char* instance) { if (instance == nullptr) { return false; @@ -113,6 +189,18 @@ bool AServiceManager_isUpdatableViaApex(const char* instance) { sp<IServiceManager> sm = defaultServiceManager(); return sm->updatableViaApex(String16(instance)) != std::nullopt; } +void AServiceManager_getUpdatableApexName(const char* instance, void* context, + void (*callback)(const char*, void*)) { + CHECK_NE(instance, nullptr); + // context may be nullptr + CHECK_NE(callback, nullptr); + + sp<IServiceManager> sm = defaultServiceManager(); + std::optional<String16> updatableViaApex = sm->updatableViaApex(String16(instance)); + if (updatableViaApex.has_value()) { + callback(String8(updatableViaApex.value()).c_str(), context); + } +} void AServiceManager_forceLazyServicesPersist(bool persist) { auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); serviceRegistrar.forcePersist(persist); diff --git a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp index f3cd21822d..43b2cb8577 100644 --- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp +++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp @@ -106,7 +106,7 @@ TEST(DoubleBinder, CallIntoNdk) { std::string outString; ScopedAStatus status = server->RepeatString("foo", &outString); EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get())) - << serviceName << " " << status.getDescription(); + << serviceName << " " << status; EXPECT_EQ("foo", outString) << serviceName; } } diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp index 2afe5d2058..76acff50bf 100644 --- a/libs/binder/ndk/tests/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -72,6 +72,11 @@ binder_status_t IFoo_Class_onTransact(AIBinder* binder, transaction_code_t code, AIBinder_Class* IFoo::kClass = AIBinder_Class_define(kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); +// Defines the same class. Ordinarly, you would never want to do this, but it's done here +// to simulate what would happen when multiple linker namespaces interact. +AIBinder_Class* IFoo::kClassDupe = AIBinder_Class_define( + kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); + class BpFoo : public IFoo { public: explicit BpFoo(AIBinder* binder) : mBinder(binder) {} diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h index 7408d0c5a9..0a562f085d 100644 --- a/libs/binder/ndk/tests/include/iface/iface.h +++ b/libs/binder/ndk/tests/include/iface/iface.h @@ -30,8 +30,13 @@ class IFoo : public virtual ::android::RefBase { static const char* kIFooDescriptor; static AIBinder_Class* kClass; + static AIBinder_Class* kClassDupe; // binder representing this interface with one reference count + // NOTE - this will create a new binder if it already exists. If you use + // getService for instance, you must pull outBinder. Don't use this without + // verifying isRemote or pointer equality. This is not a very good testing API - don't + // copy it - consider the AIDL-generated APIs instead. AIBinder* getBinder(); // Takes ownership of IFoo diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 1b136dcb8e..5b2532ab4e 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -33,13 +33,15 @@ #include <binder/IResultReceiver.h> #include <binder/IServiceManager.h> #include <binder/IShellCallback.h> - #include <sys/prctl.h> + #include <chrono> #include <condition_variable> #include <iostream> #include <mutex> +#include <optional> #include <thread> + #include "android/binder_ibinder.h" using namespace android; @@ -53,6 +55,18 @@ constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestS constexpr unsigned int kShutdownWaitTime = 10; constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715; +class MyTestFoo : public IFoo { + binder_status_t doubleNumber(int32_t in, int32_t* out) override { + *out = 2 * in; + LOG(INFO) << "doubleNumber (" << in << ") => " << *out; + return STATUS_OK; + } + binder_status_t die() override { + ADD_FAILURE() << "die called on local instance"; + return STATUS_OK; + } +}; + class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) { *out = in; @@ -252,12 +266,52 @@ TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder_decStrong(binder); } +struct ServiceData { + std::string instance; + ndk::SpAIBinder binder; + + static void fillOnRegister(const char* instance, AIBinder* binder, void* cookie) { + ServiceData* d = reinterpret_cast<ServiceData*>(cookie); + d->instance = instance; + d->binder = ndk::SpAIBinder(binder); + } +}; + +TEST(NdkBinder, RegisterForServiceNotificationsNonExisting) { + ServiceData data; + auto* notif = AServiceManager_registerForServiceNotifications( + "DOES_NOT_EXIST", ServiceData::fillOnRegister, (void*)&data); + ASSERT_NE(notif, nullptr); + + sleep(1); // give us a chance to fail + AServiceManager_NotificationRegistration_delete(notif); + + // checking after deleting to avoid needing a mutex over the data - otherwise + // in an environment w/ multiple threads, you would need to guard access + EXPECT_EQ(data.instance, ""); + EXPECT_EQ(data.binder, nullptr); +} + +TEST(NdkBinder, RegisterForServiceNotificationsExisting) { + ServiceData data; + auto* notif = AServiceManager_registerForServiceNotifications( + kExistingNonNdkService, ServiceData::fillOnRegister, (void*)&data); + ASSERT_NE(notif, nullptr); + + sleep(1); // give us a chance to fail + AServiceManager_NotificationRegistration_delete(notif); + + // checking after deleting to avoid needing a mutex over the data - otherwise + // in an environment w/ multiple threads, you would need to guard access + EXPECT_EQ(data.instance, kExistingNonNdkService); + EXPECT_EQ(data.binder, ndk::SpAIBinder(AServiceManager_checkService(kExistingNonNdkService))); +} + TEST(NdkBinder, UnimplementedDump) { - sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); + ndk::SpAIBinder binder; + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR()); ASSERT_NE(foo, nullptr); - AIBinder* binder = foo->getBinder(); - EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0)); - AIBinder_decStrong(binder); + EXPECT_EQ(OK, AIBinder_dump(binder.get(), STDOUT_FILENO, nullptr, 0)); } TEST(NdkBinder, UnimplementedShell) { @@ -281,6 +335,24 @@ TEST(NdkBinder, DoubleNumber) { EXPECT_EQ(2, out); } +TEST(NdkBinder, ReassociateBpBinderWithSameDescriptor) { + ndk::SpAIBinder binder; + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR()); + + EXPECT_TRUE(AIBinder_isRemote(binder.get())); + + EXPECT_TRUE(AIBinder_associateClass(binder.get(), IFoo::kClassDupe)); +} + +TEST(NdkBinder, CantHaveTwoLocalBinderClassesWithSameDescriptor) { + sp<IFoo> foo = sp<MyTestFoo>::make(); + ndk::SpAIBinder binder(foo->getBinder()); + + EXPECT_FALSE(AIBinder_isRemote(binder.get())); + + EXPECT_FALSE(AIBinder_associateClass(binder.get(), IFoo::kClassDupe)); +} + TEST(NdkBinder, GetTestServiceStressTest) { // libbinder has some complicated logic to make sure only one instance of // ABpBinder is associated with each binder. @@ -337,6 +409,16 @@ TEST(NdkBinder, IsUpdatable) { EXPECT_EQ(isUpdatable, false); } +TEST(NdkBinder, GetUpdatableViaApex) { + std::optional<std::string> updatableViaApex; + AServiceManager_getUpdatableApexName( + "android.hardware.light.ILights/default", &updatableViaApex, + [](const char* apexName, void* context) { + *static_cast<std::optional<std::string>*>(context) = apexName; + }); + EXPECT_EQ(updatableViaApex, std::nullopt) << *updatableViaApex; +} + // This is too slow TEST(NdkBinder, CheckLazyServiceShutDown) { ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); @@ -492,18 +574,6 @@ TEST(NdkBinder, LinkToDeath) { AIBinder_decStrong(binder); } -class MyTestFoo : public IFoo { - binder_status_t doubleNumber(int32_t in, int32_t* out) override { - *out = 2 * in; - LOG(INFO) << "doubleNumber (" << in << ") => " << *out; - return STATUS_OK; - } - binder_status_t die() override { - ADD_FAILURE() << "die called on local instance"; - return STATUS_OK; - } -}; - TEST(NdkBinder, SetInheritRt) { // functional test in binderLibTest sp<IFoo> foo = sp<MyTestFoo>::make(); @@ -544,7 +614,8 @@ TEST(NdkBinder, GetServiceInProcess) { sp<IFoo> foo = new MyTestFoo; EXPECT_EQ(EX_NONE, foo->addService(kInstanceName)); - sp<IFoo> getFoo = IFoo::getService(kInstanceName); + ndk::SpAIBinder binder; + sp<IFoo> getFoo = IFoo::getService(kInstanceName, binder.getR()); EXPECT_EQ(foo.get(), getFoo.get()); int32_t out; @@ -661,6 +732,35 @@ TEST(NdkBinder, ConvertToPlatformBinder) { } } +TEST(NdkBinder, ConvertToPlatformParcel) { + ndk::ScopedAParcel parcel = ndk::ScopedAParcel(AParcel_create()); + EXPECT_EQ(OK, AParcel_writeInt32(parcel.get(), 42)); + + android::Parcel* pparcel = AParcel_viewPlatformParcel(parcel.get()); + pparcel->setDataPosition(0); + EXPECT_EQ(42, pparcel->readInt32()); +} + +TEST(NdkBinder, GetAndVerifyScopedAIBinder_Weak) { + for (const ndk::SpAIBinder& binder : + {// remote + ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)), + // local + ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) { + // get a const ScopedAIBinder_Weak and verify promote + EXPECT_NE(binder.get(), nullptr); + const ndk::ScopedAIBinder_Weak wkAIBinder = + ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get())); + EXPECT_EQ(wkAIBinder.promote().get(), binder.get()); + // get another ScopedAIBinder_Weak and verify + ndk::ScopedAIBinder_Weak wkAIBinder2 = + ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get())); + EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder.get(), wkAIBinder2.get())); + EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder2.get(), wkAIBinder.get())); + EXPECT_EQ(wkAIBinder2.promote(), wkAIBinder.promote()); + } +} + class MyResultReceiver : public BnResultReceiver { public: Mutex mMutex; diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 355b3b4b89..d36ebac109 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -15,12 +15,13 @@ rust_library { "libutils", ], rustlibs: [ - "liblibc", "libbinder_ndk_sys", "libdowncast_rs", + "liblibc", ], host_supported: true, vendor_available: true, + product_available: true, target: { darwin: { enabled: false, @@ -29,6 +30,7 @@ rust_library { apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.rkpd", "com.android.uwb", "com.android.virt", ], @@ -71,6 +73,7 @@ rust_library { ], host_supported: true, vendor_available: true, + product_available: true, target: { darwin: { enabled: false, @@ -79,12 +82,14 @@ rust_library { apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.rkpd", "com.android.uwb", "com.android.virt", ], min_sdk_version: "Tiramisu", lints: "none", clippy_lints: "none", + visibility: [":__subpackages__"], } rust_bindgen { @@ -126,6 +131,7 @@ rust_bindgen { ], host_supported: true, vendor_available: true, + product_available: true, // Currently necessary for host builds // TODO(b/31559095): bionic on host should define this @@ -137,23 +143,7 @@ rust_bindgen { apex_available: [ "//apex_available:platform", "com.android.compos", - "com.android.uwb", - "com.android.virt", - ], - min_sdk_version: "Tiramisu", -} - -// TODO(b/184872979): remove once the Rust API is created. -rust_bindgen { - name: "libbinder_rpc_unstable_bindgen", - wrapper_src: ":libbinder_rpc_unstable_header", - crate_name: "binder_rpc_unstable_bindgen", - source_stem: "bindings", - shared_libs: [ - "libutils", - ], - apex_available: [ - "com.android.compos", + "com.android.rkpd", "com.android.uwb", "com.android.virt", ], @@ -170,9 +160,9 @@ rust_test { "libbinder_ndk", ], rustlibs: [ - "liblibc", "libbinder_ndk_sys", "libdowncast_rs", + "liblibc", ], } @@ -185,13 +175,3 @@ rust_test { clippy_lints: "none", lints: "none", } - -rust_test { - name: "libbinder_rpc_unstable_bindgen_test", - srcs: [":libbinder_rpc_unstable_bindgen"], - crate_name: "binder_rpc_unstable_bindgen", - test_suites: ["general-tests"], - auto_gen_config: true, - clippy_lints: "none", - lints: "none", -} diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 9dcef4260e..2d2bf7c582 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -28,22 +28,22 @@ //! //! [`Tokio`]: crate::Tokio -use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong}; use binder::binder_impl::BinderAsyncRuntime; +use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong}; use std::future::Future; /// Retrieve an existing service for a particular interface, sleeping for a few /// seconds if it doesn't yet exist. -pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { +pub async fn get_interface<T: FromIBinder + ?Sized + 'static>( + name: &str, +) -> Result<Strong<T>, StatusCode> { if binder::is_handling_transaction() { // See comment in the BinderAsyncPool impl. return binder::get_interface::<T>(name); } let name = name.to_string(); - let res = tokio::task::spawn_blocking(move || { - binder::get_interface::<T>(&name) - }).await; + let res = tokio::task::spawn_blocking(move || binder::get_interface::<T>(&name)).await; // The `is_panic` branch is not actually reachable in Android as we compile // with `panic = abort`. @@ -58,16 +58,16 @@ pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Res /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. -pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { +pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>( + name: &str, +) -> Result<Strong<T>, StatusCode> { if binder::is_handling_transaction() { // See comment in the BinderAsyncPool impl. return binder::wait_for_interface::<T>(name); } let name = name.to_string(); - let res = tokio::task::spawn_blocking(move || { - binder::wait_for_interface::<T>(&name) - }).await; + let res = tokio::task::spawn_blocking(move || binder::wait_for_interface::<T>(&name)).await; // The `is_panic` branch is not actually reachable in Android as we compile // with `panic = abort`. diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp new file mode 100644 index 0000000000..0067a20484 --- /dev/null +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -0,0 +1,113 @@ +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"], +} + +rust_library { + name: "librpcbinder_rs", + crate_name: "rpcbinder", + srcs: ["src/lib.rs"], + shared_libs: [ + "libutils", + ], + rustlibs: [ + "libbinder_ndk_sys", + "libbinder_rpc_unstable_bindgen_sys", + "libbinder_rs", + "libdowncast_rs", + "libforeign_types", + "liblibc", + "liblog_rust", + ], + visibility: [ + "//device/google/cuttlefish/shared/minidroid/sample", + "//packages/modules/Virtualization:__subpackages__", + "//system/software_defined_vehicle:__subpackages__", + ], + apex_available: [ + "//apex_available:platform", + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", +} + +// Build a separate rust_library rather than depending directly on libbinder_rpc_unstable_bindgen, +// to work around the fact that rust_bindgen targets only produce rlibs and not dylibs, which would +// result in duplicate conflicting versions of libbinder_ndk_sys. This will hopefully be fixed in +// the build system, at which point we can delete this target and go back to using +// libbinder_rpc_unstable_bindgen directly. +rust_library { + name: "libbinder_rpc_unstable_bindgen_sys", + crate_name: "binder_rpc_unstable_bindgen", + srcs: [ + ":libbinder_rpc_unstable_bindgen", + ], + visibility: [":__subpackages__"], + rustlibs: [ + "libbinder_ndk_sys", + ], + shared_libs: [ + "libbinder_rpc_unstable", + "libutils", + ], + apex_available: [ + "//apex_available:platform", + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", + lints: "none", + clippy_lints: "none", +} + +// TODO(b/184872979): remove once the RPC Binder API is stabilised. +rust_bindgen { + name: "libbinder_rpc_unstable_bindgen", + wrapper_src: ":libbinder_rpc_unstable_header", + crate_name: "binder_rpc_unstable_bindgen", + visibility: [":__subpackages__"], + source_stem: "bindings", + bindgen_flags: [ + "--size_t-is-usize", + "--blocklist-type", + "AIBinder", + "--raw-line", + "use binder_ndk_sys::AIBinder;", + "--rustified-enum", + "ARpcSession_FileDescriptorTransportMode", + ], + rustlibs: [ + "libbinder_ndk_sys", + ], + shared_libs: [ + "libbinder_rpc_unstable", + "libutils", + ], + apex_available: [ + "//apex_available:platform", + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", +} + +rust_test { + name: "libbinder_rpc_unstable_bindgen_test", + srcs: [":libbinder_rpc_unstable_bindgen"], + crate_name: "binder_rpc_unstable_bindgen", + rustlibs: [ + "libbinder_ndk_sys", + ], + test_suites: ["general-tests"], + auto_gen_config: true, + clippy_lints: "none", + lints: "none", +} diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs new file mode 100644 index 0000000000..a9573850f1 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -0,0 +1,23 @@ +/* + * 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. + */ + +//! API for RPC Binder services. + +mod server; +mod session; + +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 new file mode 100644 index 0000000000..c87876ac15 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -0,0 +1,173 @@ +/* + * 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. + */ + +use crate::session::FileDescriptorTransportMode; +use binder::{unstable_api::AsNative, SpIBinder}; +use binder_rpc_unstable_bindgen::ARpcServer; +use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; +use std::ffi::CString; +use std::io::{Error, ErrorKind}; +use std::os::unix::io::{IntoRawFd, OwnedFd}; + +foreign_type! { + type CType = binder_rpc_unstable_bindgen::ARpcServer; + fn drop = binder_rpc_unstable_bindgen::ARpcServer_free; + + /// A type that represents a foreign instance of RpcServer. + #[derive(Debug)] + pub struct RpcServer; + /// A borrowed RpcServer. + pub struct RpcServerRef; +} + +/// SAFETY - The opaque handle can be cloned freely. +unsafe impl Send for RpcServer {} +/// SAFETY - The underlying C++ RpcServer class is thread-safe. +unsafe impl Sync for RpcServer {} + +impl RpcServer { + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// vsock port. Only connections from the given CID are accepted. + /// + // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. + // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. + pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { + 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. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( + service, cid, port, + )) + } + } + + /// 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( + mut service: SpIBinder, + socket_name: &str, + ) -> Result<RpcServer, Error> { + 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. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain( + service, + socket_name.as_ptr(), + )) + } + } + + /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket + /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix + /// domain sockets, pass one to the server and the other to the client. Multiple client session + /// can be created from the client end of the pair. + pub fn new_unix_domain_bootstrap( + mut service: SpIBinder, + bootstrap_fd: OwnedFd, + ) -> Result<RpcServer, Error> { + 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 bootstrap FD. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap( + service, + bootstrap_fd.into_raw_fd(), + )) + } + } + + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// IP address and port. + pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> { + let address = match CString::new(address) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", address, 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. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet( + service, + address.as_ptr(), + port, + )) + } + } + + unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { + if ptr.is_null() { + return Err(Error::new(ErrorKind::Other, "Failed to start server")); + } + Ok(RpcServer::from_ptr(ptr)) + } +} + +impl RpcServerRef { + /// Sets the list of file descriptor transport modes supported by this server. + pub fn set_supported_file_descriptor_transport_modes( + &self, + modes: &[FileDescriptorTransportMode], + ) { + // 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( + self.as_ptr(), + modes.as_ptr(), + modes.len(), + ) + } + } + + /// Starts a new background thread and calls join(). Returns immediately. + pub fn start(&self) { + 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) { + 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> { + if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { + Ok(()) + } else { + Err(Error::from(ErrorKind::UnexpectedEof)) + } + } +} diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs new file mode 100644 index 0000000000..0b517cf613 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -0,0 +1,216 @@ +/* + * 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. + */ + +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}, +}; + +pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode; + +foreign_type! { + type CType = binder_rpc_unstable_bindgen::ARpcSession; + fn drop = binder_rpc_unstable_bindgen::ARpcSession_free; + + /// A type that represents a foreign instance of RpcSession. + #[derive(Debug)] + pub struct RpcSession; + /// A borrowed RpcSession. + pub struct RpcSessionRef; +} + +/// SAFETY - The opaque handle can be cloned freely. +unsafe impl Send for RpcSession {} +/// 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. + unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) } + } +} + +impl Default for RpcSession { + fn default() -> Self { + Self::new() + } +} + +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. + unsafe { + binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode( + self.as_ptr(), + mode, + ) + }; + } + + /// 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. + unsafe { + binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads) + }; + } + + /// Sets the maximum number of outgoing threads. + pub fn set_max_outgoing_threads(&self, threads: usize) { + // SAFETY - Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads) + }; + } + + /// Connects to an RPC Binder server over vsock for a particular interface. + pub fn setup_vsock_client<T: FromIBinder + ?Sized>( + &self, + cid: u32, + port: u32, + ) -> Result<Strong<T>, StatusCode> { + // SAFETY: AIBinder returned by ARpcSession_setupVsockClient has correct + // reference count, and the ownership can safely be taken by new_spibinder. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupVsockClient( + self.as_ptr(), + cid, + port, + )) + }; + Self::get_interface(service) + } + + /// Connects to an RPC Binder server over a names Unix Domain Socket for + /// a particular interface. + pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>( + &self, + socket_name: &str, + ) -> Result<Strong<T>, StatusCode> { + 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(StatusCode::NAME_NOT_FOUND); + } + }; + + // SAFETY: AIBinder returned by ARpcSession_setupUnixDomainClient has correct + // reference count, and the ownership can safely be taken by new_spibinder. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainClient( + self.as_ptr(), + socket_name.as_ptr(), + )) + }; + Self::get_interface(service) + } + + /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket + /// for a particular interface. + pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>( + &self, + bootstrap_fd: BorrowedFd, + ) -> Result<Strong<T>, StatusCode> { + // 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. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainBootstrapClient( + self.as_ptr(), + bootstrap_fd.as_raw_fd(), + )) + }; + Self::get_interface(service) + } + + /// Connects to an RPC Binder server over inet socket at the given address and port. + pub fn setup_inet_client<T: FromIBinder + ?Sized>( + &self, + address: &str, + port: u32, + ) -> Result<Strong<T>, StatusCode> { + let address = match CString::new(address) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", address, e); + return Err(StatusCode::BAD_VALUE); + } + }; + + // SAFETY: AIBinder returned by ARpcSession_setupInet has correct reference + // count, and the ownership can safely be taken by new_spibinder. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupInet( + self.as_ptr(), + address.as_ptr(), + port, + )) + }; + Self::get_interface(service) + } + + /// 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<T: FromIBinder + ?Sized>( + &self, + mut request_fd: impl FnMut() -> Option<RawFd>, + ) -> Result<Strong<T>, StatusCode> { + // Double reference the factory because trait objects aren't FFI safe. + let mut request_fd_ref: RequestFd = &mut request_fd; + let param = &mut request_fd_ref as *mut RequestFd as *mut c_void; + + // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the + // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership + // of param, only passing it to request_fd_wrapper. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupPreconnectedClient( + self.as_ptr(), + Some(request_fd_wrapper), + param, + )) + }; + Self::get_interface(service) + } + + fn get_interface<T: FromIBinder + ?Sized>( + service: Option<SpIBinder>, + ) -> Result<Strong<T>, StatusCode> { + if let Some(service) = service { + FromIBinder::try_from(service) + } else { + Err(StatusCode::NAME_NOT_FOUND) + } + } +} + +type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>; + +unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { + // 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(); + request_fd().unwrap_or(-1) +} diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 5d6206de0b..976f54d36f 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -17,7 +17,7 @@ //! Trait definitions for binder objects use crate::error::{status_t, Result, StatusCode}; -use crate::parcel::{Parcel, BorrowedParcel}; +use crate::parcel::{BorrowedParcel, Parcel}; use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; @@ -133,7 +133,7 @@ impl TryFrom<i32> for Stability { match stability { 0 => Ok(Local), 1 => Ok(Vintf), - _ => Err(StatusCode::BAD_VALUE) + _ => Err(StatusCode::BAD_VALUE), } } } @@ -158,7 +158,12 @@ pub trait Remotable: Send + Sync { /// Handle and reply to a request to invoke a transaction on this object. /// /// `reply` may be [`None`] if the sender does not expect a reply. - fn on_transact(&self, code: TransactionCode, data: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>) -> Result<()>; + fn on_transact( + &self, + code: TransactionCode, + data: &BorrowedParcel<'_>, + reply: &mut BorrowedParcel<'_>, + ) -> Result<()>; /// Handle a request to invoke the dump transaction on this /// object. @@ -454,28 +459,19 @@ impl<I: FromIBinder + ?Sized> Weak<I> { /// Construct a new weak reference from a strong reference fn new(binder: &Strong<I>) -> Self { let weak_binder = binder.as_binder().downgrade(); - Weak { - weak_binder, - interface_type: PhantomData, - } + Weak { weak_binder, interface_type: PhantomData } } /// Upgrade this weak reference to a strong reference if the binder object /// is still alive pub fn upgrade(&self) -> Result<Strong<I>> { - self.weak_binder - .promote() - .ok_or(StatusCode::DEAD_OBJECT) - .and_then(FromIBinder::try_from) + self.weak_binder.promote().ok_or(StatusCode::DEAD_OBJECT).and_then(FromIBinder::try_from) } } impl<I: FromIBinder + ?Sized> Clone for Weak<I> { fn clone(&self) -> Self { - Self { - weak_binder: self.weak_binder.clone(), - interface_type: PhantomData, - } + Self { weak_binder: self.weak_binder.clone(), interface_type: PhantomData } } } @@ -614,7 +610,12 @@ pub trait InterfaceClassMethods { /// 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. - unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t; + unsafe extern "C" fn on_dump( + binder: *mut sys::AIBinder, + fd: i32, + args: *mut *const c_char, + num_args: u32, + ) -> status_t; } /// Interface for transforming a generic SpIBinder into a specific remote @@ -1090,7 +1091,7 @@ macro_rules! declare_binder_enum { } } => { $( #[$attr] )* - #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] + #[derive(Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] #[allow(missing_docs)] pub struct $enum(pub $backing); impl $enum { @@ -1103,6 +1104,15 @@ macro_rules! declare_binder_enum { } } + impl std::fmt::Debug for $enum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + $($value => f.write_str(stringify!($name)),)* + _ => f.write_fmt(format_args!("{}", self.0)) + } + } + } + impl $crate::binder_impl::Serialize for $enum { fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> { parcel.write(&self.0) diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs index 579f9f90b8..d90acafaeb 100644 --- a/libs/binder/rust/src/binder_async.rs +++ b/libs/binder/rust/src/binder_async.rs @@ -41,7 +41,10 @@ pub trait BinderAsyncPool { /// boxed `Future` trait object, and including `after_spawn` in the trait function /// allows the caller to avoid double-boxing if they want to do anything to the value /// returned from the spawned thread. - fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>> + fn spawn<'a, F1, F2, Fut, A, B, E>( + spawn_me: F1, + after_spawn: F2, + ) -> BoxFuture<'a, Result<B, E>> where F1: FnOnce() -> A, F2: FnOnce(A) -> Fut, diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs index 2598ebc804..f6b09ed8fe 100644 --- a/libs/binder/rust/src/error.rs +++ b/libs/binder/rust/src/error.rs @@ -18,7 +18,7 @@ use crate::binder::AsNative; use crate::sys; use std::error; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::result; @@ -104,6 +104,10 @@ unsafe impl Sync for Status {} // A thread-local `AStatus` would not be valid. unsafe impl Send for Status {} +fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> { + CString::new(message.as_ref()).ok() +} + impl Status { /// Create a status object representing a successful transaction. pub fn ok() -> Self { @@ -146,6 +150,11 @@ impl Status { Self(ptr) } + /// Creates a status object from a service specific error. + pub fn new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status { + Self::new_service_specific_error(err, message.and_then(to_cstring).as_deref()) + } + /// Create a status object from an exception code pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status { if let Some(message) = message { @@ -158,6 +167,14 @@ impl Status { } } + /// Creates a status object from an exception code and message. + pub fn new_exception_str<T: AsRef<str>>( + exception: ExceptionCode, + message: Option<T>, + ) -> Status { + Self::new_exception(exception, message.and_then(to_cstring).as_deref()) + } + /// Create a status object from a raw `AStatus` pointer. /// /// # Safety @@ -371,3 +388,41 @@ unsafe impl AsNative<sys::AStatus> for Status { self.0 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn make_service_specific_error() { + let status = Status::new_service_specific_error_str(-42, Some("message")); + + assert!(!status.is_ok()); + 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 make_exception() { + let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("message")); + + assert!(!status.is_ok()); + 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 make_exception_null() { + let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("one\0two")); + + assert!(!status.is_ok()); + 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): ''".to_string()); + } +} diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index dbfb1c20a5..0c8b48f1f5 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -94,23 +94,22 @@ //! ``` #[macro_use] -mod proxy; - -#[macro_use] mod binder; mod binder_async; mod error; mod native; mod parcel; +mod proxy; mod state; use binder_ndk_sys as sys; -pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; +pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use error::{ExceptionCode, Status, StatusCode}; pub use native::{ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, + LazyServiceGuard, }; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; pub use proxy::{ @@ -147,4 +146,5 @@ pub mod unstable_api { pub use crate::binder::AsNative; 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 b7c7ae4b54..5557168055 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -30,6 +30,7 @@ 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. /// @@ -97,10 +98,7 @@ impl<T: Remotable> Binder<T> { // ends. sys::AIBinder_new(class.into(), rust_object as *mut c_void) }; - let mut binder = Binder { - ibinder, - rust_object, - }; + let mut binder = Binder { ibinder, rust_object }; binder.mark_stability(stability); binder } @@ -211,8 +209,8 @@ impl<T: Remotable> Binder<T> { } /// Mark this binder object with local stability, which is vendor if we are - /// building for the VNDK and system otherwise. - #[cfg(any(vendor_ndk, android_vndk))] + /// building for android_vendor and system otherwise. + #[cfg(android_vendor)] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -222,8 +220,8 @@ impl<T: Remotable> Binder<T> { } /// Mark this binder object with local stability, which is vendor if we are - /// building for the VNDK and system otherwise. - #[cfg(not(any(vendor_ndk, android_vndk)))] + /// building for android_vendor and system otherwise. + #[cfg(not(android_vendor))] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -297,7 +295,7 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { /// 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) { - Box::from_raw(object as *mut T); + drop(Box::from_raw(object as *mut T)); } /// Called whenever a new, local `AIBinder` object is needed of a specific @@ -335,11 +333,18 @@ impl<T: Remotable> InterfaceClassMethods for Binder<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)); - if args.is_null() { + if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; } - let args = slice::from_raw_parts(args, num_args as usize); - let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect(); + + let args = if args.is_null() || num_args == 0 { + vec![] + } else { + slice::from_raw_parts(args, num_args as usize) + .iter() + .map(|s| CStr::from_ptr(*s)) + .collect() + }; let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); @@ -413,10 +418,7 @@ impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> { // We are transferring the ownership of the AIBinder into the new Binder // object. let mut ibinder = ManuallyDrop::new(ibinder); - Ok(Binder { - ibinder: ibinder.as_native_mut(), - rust_object: userdata as *mut B, - }) + Ok(Binder { ibinder: ibinder.as_native_mut(), rust_object: userdata as *mut B }) } } @@ -486,6 +488,8 @@ pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result< /// If persist is true then shut down will be blocked until this function is called again with /// persist false. If this is to be the initial state, call this function before calling /// register_lazy_service. +/// +/// 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. @@ -493,6 +497,57 @@ pub fn force_lazy_services_persist(persist: bool) { } } +/// An RAII object to ensure a process which registers lazy services is not killed. During the +/// lifetime of any of these objects the service manager will not not kill the process even if none +/// of its lazy services are in use. +#[must_use] +#[derive(Debug)] +pub struct LazyServiceGuard { + // Prevent construction outside this module. + _private: (), +} + +// Count of how many LazyServiceGuard objects are in existence. +static GUARD_COUNT: Mutex<u64> = Mutex::new(0); + +impl LazyServiceGuard { + /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this + /// process. + pub fn new() -> Self { + let mut count = GUARD_COUNT.lock().unwrap(); + *count += 1; + if *count == 1 { + // It's important that we make this call with the mutex held, to make sure + // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly + // sequenced. (That also means we can't just use an AtomicU64.) + force_lazy_services_persist(true); + } + Self { _private: () } + } +} + +impl Drop for LazyServiceGuard { + fn drop(&mut self) { + let mut count = GUARD_COUNT.lock().unwrap(); + *count -= 1; + if *count == 0 { + force_lazy_services_persist(false); + } + } +} + +impl Clone for LazyServiceGuard { + fn clone(&self) -> Self { + Self::new() + } +} + +impl Default for LazyServiceGuard { + fn default() -> Self { + Self::new() + } +} + /// Tests often create a base BBinder instance; so allowing the unit /// type to be remotable translates nicely to Binder::new(()). impl Remotable for () { diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 256fa8bea6..e4c568eab2 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -22,10 +22,10 @@ use crate::proxy::SpIBinder; use crate::sys; use std::convert::TryInto; +use std::fmt; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ptr::{self, NonNull}; -use std::fmt; mod file_descriptor; mod parcelable; @@ -33,8 +33,8 @@ mod parcelable_holder; pub use self::file_descriptor::ParcelFileDescriptor; pub use self::parcelable::{ - Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, - Parcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG, + Deserialize, DeserializeArray, DeserializeOption, Parcelable, Serialize, SerializeArray, + SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG, }; pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata}; @@ -78,9 +78,7 @@ impl Parcel { // a valid pointer. If it fails, the process will crash. sys::AParcel_create() }; - Self { - ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") - } + Self { ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") } } /// Create an owned reference to a parcel object from a raw pointer. @@ -116,10 +114,7 @@ impl Parcel { // lifetime of the returned `BorrowedParcel` is tied to `self`, so the // borrow checker will ensure that the `AParcel` can only be accessed // via the `BorrowParcel` until it goes out of scope. - BorrowedParcel { - ptr: self.ptr, - _lifetime: PhantomData, - } + BorrowedParcel { ptr: self.ptr, _lifetime: PhantomData } } /// Get an immutable borrowed view into the contents of this `Parcel`. @@ -127,9 +122,7 @@ impl Parcel { // Safety: Parcel and BorrowedParcel are both represented in the same // way as a NonNull<sys::AParcel> due to their use of repr(transparent), // so casting references as done here is valid. - unsafe { - &*(self as *const Parcel as *const BorrowedParcel<'_>) - } + unsafe { &*(self as *const Parcel as *const BorrowedParcel<'_>) } } } @@ -165,10 +158,7 @@ impl<'a> BorrowedParcel<'a> { /// since this is a mutable borrow, it must have exclusive access to the /// AParcel for the duration of the borrow. pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<BorrowedParcel<'a>> { - Some(Self { - ptr: NonNull::new(ptr)?, - _lifetime: PhantomData, - }) + Some(Self { ptr: NonNull::new(ptr)?, _lifetime: PhantomData }) } /// Get a sub-reference to this reference to the parcel. @@ -177,10 +167,7 @@ impl<'a> BorrowedParcel<'a> { // lifetime of the returned `BorrowedParcel` is tied to `self`, so the // borrow checker will ensure that the `AParcel` can only be accessed // via the `BorrowParcel` until it goes out of scope. - BorrowedParcel { - ptr: self.ptr, - _lifetime: PhantomData, - } + BorrowedParcel { ptr: self.ptr, _lifetime: PhantomData } } } @@ -269,7 +256,7 @@ impl<'a> BorrowedParcel<'a> { /// ``` pub fn sized_write<F>(&mut self, f: F) -> Result<()> where - for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>, { let start = self.get_data_position(); self.write(&0i32)?; @@ -324,17 +311,17 @@ impl<'a> BorrowedParcel<'a> { /// /// This appends `size` bytes of data from `other` starting at offset /// `start` to the current parcel, or returns an error if not possible. - pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { + pub fn append_from( + &mut self, + other: &impl AsNative<sys::AParcel>, + start: i32, + size: i32, + ) -> Result<()> { 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, - ) + sys::AParcel_appendFrom(other.as_native(), self.as_native_mut(), start, size) }; status_result(status) } @@ -406,7 +393,7 @@ impl Parcel { /// ``` pub fn sized_write<F>(&mut self, f: F) -> Result<()> where - for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>, { self.borrowed().sized_write(f) } @@ -438,7 +425,12 @@ impl Parcel { /// /// This appends `size` bytes of data from `other` starting at offset /// `start` to the current parcel, or returns an error if not possible. - pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { + pub fn append_from( + &mut self, + other: &impl AsNative<sys::AParcel>, + start: i32, + size: i32, + ) -> Result<()> { self.borrowed().append_from(other, start, size) } @@ -492,7 +484,7 @@ impl<'a> BorrowedParcel<'a> { /// pub fn sized_read<F>(&self, f: F) -> Result<()> where - for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>, { let start = self.get_data_position(); let parcelable_size: i32 = self.read()?; @@ -500,17 +492,13 @@ impl<'a> BorrowedParcel<'a> { return Err(StatusCode::BAD_VALUE); } - let end = start.checked_add(parcelable_size) - .ok_or(StatusCode::BAD_VALUE)?; + let end = start.checked_add(parcelable_size).ok_or(StatusCode::BAD_VALUE)?; if end > self.get_data_size() { return Err(StatusCode::NOT_ENOUGH_DATA); } let subparcel = ReadableSubParcel { - parcel: BorrowedParcel { - ptr: self.ptr, - _lifetime: PhantomData, - }, + parcel: BorrowedParcel { ptr: self.ptr, _lifetime: PhantomData }, end_position: end, }; f(subparcel)?; @@ -578,9 +566,6 @@ pub struct ReadableSubParcel<'a> { impl<'a> ReadableSubParcel<'a> { /// Read a type that implements [`Deserialize`] from the sub-parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { - // The caller should have checked this, - // but it can't hurt to double-check - assert!(self.has_more_data()); D::deserialize(&self.parcel) } @@ -633,7 +618,7 @@ impl Parcel { /// pub fn sized_read<F>(&self, f: F) -> Result<()> where - for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>, { self.borrowed_ref().sized_read(f) } @@ -716,15 +701,13 @@ impl Drop for Parcel { impl fmt::Debug for Parcel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Parcel") - .finish() + f.debug_struct("Parcel").finish() } } impl<'a> fmt::Debug for BorrowedParcel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BorrowedParcel") - .finish() + f.debug_struct("BorrowedParcel").finish() } } @@ -813,10 +796,7 @@ fn test_read_data() { assert!(parcel.set_data_position(start).is_ok()); } - assert_eq!( - parcel.read::<f32>().unwrap(), - 1143139100000000000000000000.0 - ); + assert_eq!(parcel.read::<f32>().unwrap(), 1143139100000000000000000000.0); assert_eq!(parcel.read::<f32>().unwrap(), 40.043392); unsafe { @@ -842,10 +822,7 @@ fn test_utf8_utf16_conversions() { unsafe { assert!(parcel.set_data_position(start).is_ok()); } - assert_eq!( - parcel.read::<Option<String>>().unwrap().unwrap(), - "Hello, Binder!", - ); + assert_eq!(parcel.read::<Option<String>>().unwrap().unwrap(), "Hello, Binder!",); unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -864,13 +841,7 @@ fn test_utf8_utf16_conversions() { assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok()); assert!(parcel - .write( - &[ - String::from("str4"), - String::from("str5"), - String::from("str6"), - ][..] - ) + .write(&[String::from("str4"), String::from("str5"), String::from("str6"),][..]) .is_ok()); let s1 = "Hello, Binder!"; @@ -882,14 +853,8 @@ fn test_utf8_utf16_conversions() { assert!(parcel.set_data_position(start).is_ok()); } - assert_eq!( - parcel.read::<Vec<String>>().unwrap(), - ["str1", "str2", "str3"] - ); - assert_eq!( - parcel.read::<Vec<String>>().unwrap(), - ["str4", "str5", "str6"] - ); + assert_eq!(parcel.read::<Vec<String>>().unwrap(), ["str1", "str2", "str3"]); + assert_eq!(parcel.read::<Vec<String>>().unwrap(), ["str4", "str5", "str6"]); assert_eq!(parcel.read::<Vec<String>>().unwrap(), [s1, s2, s3]); } @@ -900,9 +865,9 @@ fn test_sized_write() { let arr = [1i32, 2i32, 3i32]; - parcel.sized_write(|subparcel| { - subparcel.write(&arr[..]) - }).expect("Could not perform sized write"); + parcel + .sized_write(|subparcel| subparcel.write(&arr[..])) + .expect("Could not perform sized write"); // i32 sub-parcel length + i32 array length + 3 i32 elements let expected_len = 20i32; @@ -913,15 +878,9 @@ fn test_sized_write() { parcel.set_data_position(start).unwrap(); } - assert_eq!( - expected_len, - parcel.read().unwrap(), - ); + assert_eq!(expected_len, parcel.read().unwrap(),); - assert_eq!( - parcel.read::<Vec<i32>>().unwrap(), - &arr, - ); + assert_eq!(parcel.read::<Vec<i32>>().unwrap(), &arr,); } #[test] diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index 4d3d59afb0..de6d64934a 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -15,7 +15,7 @@ */ use super::{ - Deserialize, DeserializeArray, DeserializeOption, BorrowedParcel, Serialize, SerializeArray, + BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, }; use crate::binder::AsNative; @@ -115,10 +115,7 @@ impl DeserializeOption for ParcelFileDescriptor { // 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, - ))?; + status_result(sys::AParcel_readParcelFileDescriptor(parcel.as_native(), &mut fd))?; } if fd < 0 { Ok(None) @@ -136,9 +133,7 @@ impl DeserializeOption for ParcelFileDescriptor { impl Deserialize for ParcelFileDescriptor { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { - Deserialize::deserialize(parcel) - .transpose() - .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + 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 0c7e48df0c..6f4c37530f 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -22,8 +22,8 @@ use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; -use std::os::raw::{c_char, c_ulong}; -use std::mem::{self, MaybeUninit, ManuallyDrop}; +use std::mem::{self, ManuallyDrop, MaybeUninit}; +use std::os::raw::c_char; use std::ptr; use std::slice; @@ -103,23 +103,16 @@ pub trait SerializeArray: Serialize + Sized { unsafe extern "C" fn serialize_element<T: Serialize>( parcel: *mut sys::AParcel, array: *const c_void, - index: c_ulong, + index: usize, ) -> status_t { - // c_ulong and usize are the same, but we need the explicitly sized version - // so the function signature matches what bindgen generates. - let index = index as usize; - - let slice: &[T] = slice::from_raw_parts(array.cast(), index+1); + let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1); let mut parcel = match BorrowedParcel::from_raw(parcel) { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; - slice[index].serialize(&mut parcel) - .err() - .unwrap_or(StatusCode::OK) - as status_t + slice[index].serialize(&mut parcel).err().unwrap_or(StatusCode::OK) as status_t } /// Helper trait for types that can be deserialized as arrays. @@ -161,12 +154,8 @@ pub trait DeserializeArray: Deserialize { unsafe extern "C" fn deserialize_element<T: Deserialize>( parcel: *const sys::AParcel, array: *mut c_void, - index: c_ulong, + index: usize, ) -> status_t { - // c_ulong and usize are the same, but we need the explicitly sized version - // so the function signature matches what bindgen generates. - let index = index as usize; - let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>); let vec = match vec { Some(v) => v, @@ -265,10 +254,7 @@ unsafe extern "C" fn allocate_vec_with_buffer<T>( /// /// The opaque data pointer passed to the array read function must be a mutable /// pointer to an `Option<Vec<MaybeUninit<T>>>`. -unsafe extern "C" fn allocate_vec<T>( - data: *mut c_void, - len: i32, -) -> bool { +unsafe extern "C" fn allocate_vec<T>(data: *mut c_void, len: i32) -> bool { let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>); if len < 0 { *vec = None; @@ -286,7 +272,6 @@ unsafe extern "C" fn allocate_vec<T>( true } - macro_rules! parcelable_primitives { { $( @@ -303,11 +288,7 @@ unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> { // has the same alignment and size as T, so the pointer to the vector // allocation will be compatible. let mut vec = ManuallyDrop::new(vec); - Vec::from_raw_parts( - vec.as_mut_ptr().cast(), - vec.len(), - vec.capacity(), - ) + Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) } macro_rules! impl_parcelable { @@ -522,11 +503,7 @@ impl SerializeOption for str { // `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, - )) + status_result(sys::AParcel_writeString(parcel.as_native_mut(), ptr::null(), -1)) }, Some(s) => unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -540,10 +517,7 @@ impl SerializeOption for str { status_result(sys::AParcel_writeString( parcel.as_native_mut(), s.as_ptr() as *const c_char, - s.as_bytes() - .len() - .try_into() - .or(Err(StatusCode::BAD_VALUE))?, + s.as_bytes().len().try_into().or(Err(StatusCode::BAD_VALUE))?, )) }, } @@ -602,9 +576,7 @@ impl DeserializeArray for Option<String> {} impl Deserialize for String { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { - Deserialize::deserialize(parcel) - .transpose() - .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } } @@ -704,10 +676,7 @@ impl Serialize for Status { // 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(), - )) + status_result(sys::AParcel_writeStatusHeader(parcel.as_native_mut(), self.as_native())) } } } @@ -881,8 +850,7 @@ macro_rules! impl_deserialize_for_parcelable { Ok(()) } else { use $crate::Parcelable; - this.get_or_insert_with(Self::default) - .read_from_parcel(parcel) + this.get_or_insert_with(Self::default).read_from_parcel(parcel) } } } @@ -915,8 +883,8 @@ impl<T: DeserializeOption> DeserializeOption for Box<T> { #[cfg(test)] mod tests { - use crate::parcel::Parcel; use super::*; + use crate::parcel::Parcel; #[test] fn test_custom_parcelable() { @@ -934,11 +902,11 @@ mod tests { impl Deserialize for Custom { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Ok(Custom( - parcel.read()?, - parcel.read()?, - parcel.read()?, - parcel.read::<Option<Vec<String>>>()?.unwrap(), - )) + parcel.read()?, + parcel.read()?, + parcel.read()?, + parcel.read::<Option<Vec<String>>>()?.unwrap(), + )) } } @@ -1157,12 +1125,7 @@ mod tests { assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); - let f32s = [ - std::f32::NAN, - std::f32::INFINITY, - 1.23456789, - std::f32::EPSILON, - ]; + let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON]; unsafe { assert!(parcel.set_data_position(start).is_ok()); @@ -1178,12 +1141,7 @@ mod tests { assert!(vec[0].is_nan()); assert_eq!(vec[1..], f32s[1..]); - let f64s = [ - std::f64::NAN, - std::f64::INFINITY, - 1.234567890123456789, - std::f64::EPSILON, - ]; + let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON]; 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 432da5dfd7..c829d37dca 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -48,10 +48,7 @@ impl<T> AnyParcelable for T where T: DowncastSync + Parcelable + std::fmt::Debug #[derive(Debug, Clone)] enum ParcelableHolderData { Empty, - Parcelable { - parcelable: Arc<dyn AnyParcelable>, - name: String, - }, + Parcelable { parcelable: Arc<dyn AnyParcelable>, name: String }, Parcel(Parcel), } @@ -77,10 +74,7 @@ pub struct ParcelableHolder { impl ParcelableHolder { /// Construct a new `ParcelableHolder` with the given stability. pub fn new(stability: Stability) -> Self { - Self { - data: Mutex::new(ParcelableHolderData::Empty), - stability, - } + Self { data: Mutex::new(ParcelableHolderData::Empty), stability } } /// Reset the contents of this `ParcelableHolder`. @@ -101,10 +95,8 @@ impl ParcelableHolder { return Err(StatusCode::BAD_VALUE); } - *self.data.get_mut().unwrap() = ParcelableHolderData::Parcelable { - parcelable: p, - name: T::get_descriptor().into(), - }; + *self.data.get_mut().unwrap() = + ParcelableHolderData::Parcelable { parcelable: p, name: T::get_descriptor().into() }; Ok(()) } @@ -130,10 +122,7 @@ impl ParcelableHolder { let mut data = self.data.lock().unwrap(); match *data { ParcelableHolderData::Empty => Ok(None), - ParcelableHolderData::Parcelable { - ref parcelable, - ref name, - } => { + ParcelableHolderData::Parcelable { ref parcelable, ref name } => { if name != parcelable_desc { return Err(StatusCode::BAD_VALUE); } @@ -199,10 +188,7 @@ impl Parcelable for ParcelableHolder { let mut data = self.data.lock().unwrap(); match *data { ParcelableHolderData::Empty => parcel.write(&0i32), - ParcelableHolderData::Parcelable { - ref parcelable, - ref name, - } => { + ParcelableHolderData::Parcelable { ref parcelable, ref name } => { let length_start = parcel.get_data_position(); parcel.write(&0i32)?; @@ -251,9 +237,7 @@ impl Parcelable for ParcelableHolder { // TODO: C++ ParcelableHolder accepts sizes up to SIZE_MAX here, but we // only go up to i32::MAX because that's what our API uses everywhere let data_start = parcel.get_data_position(); - let data_end = data_start - .checked_add(data_size) - .ok_or(StatusCode::BAD_VALUE)?; + let data_end = data_start.checked_add(data_size).ok_or(StatusCode::BAD_VALUE)?; let mut new_parcel = Parcel::new(); new_parcel.append_from(parcel, data_start, data_size)?; diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 4df557bee0..254efaed9a 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -22,7 +22,8 @@ use crate::binder::{ }; use crate::error::{status_result, Result, StatusCode}; use crate::parcel::{ - Parcel, BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, + BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, + SerializeArray, SerializeOption, }; use crate::sys; @@ -132,6 +133,14 @@ impl SpIBinder { } } +fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> { + if let Some(service) = service { + FromIBinder::try_from(service) + } else { + Err(StatusCode::NAME_NOT_FOUND) + } +} + pub mod unstable_api { use super::{sys, SpIBinder}; @@ -431,10 +440,7 @@ impl SerializeArray for SpIBinder {} impl Deserialize for SpIBinder { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> { - parcel - .read_binder() - .transpose() - .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + parcel.read_binder().transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } } @@ -563,6 +569,9 @@ impl Drop for WpIBinder { /// The cookie in this struct represents an Arc<F> for the owned callback. /// This struct owns a ref-count of it, and so does every binder that we /// have been linked with. +/// +/// Dropping the `DeathRecipient` will `unlink_to_death` any binders it is +/// currently linked to. #[repr(C)] pub struct DeathRecipient { recipient: *mut sys::AIBinder_DeathRecipient, @@ -610,7 +619,10 @@ impl DeathRecipient { // // 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::<F>)); + sys::AIBinder_DeathRecipient_setOnUnlinked( + recipient, + Some(Self::cookie_decr_refcount::<F>), + ); } DeathRecipient { recipient, @@ -776,21 +788,13 @@ pub fn wait_for_service(name: &str) -> Option<SpIBinder> { /// Retrieve an existing service for a particular interface, blocking for a few /// seconds if it doesn't yet exist. pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { - let service = get_service(name); - match service { - Some(service) => FromIBinder::try_from(service), - None => Err(StatusCode::NAME_NOT_FOUND), - } + interface_cast(get_service(name)) } /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { - let service = wait_for_service(name); - match service { - Some(service) => FromIBinder::try_from(service), - None => Err(StatusCode::NAME_NOT_FOUND), - } + interface_cast(wait_for_service(name)) } /// Check if a service is declared (e.g. in a VINTF manifest) diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index 0aef744f98..cc18741a4e 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -124,7 +124,8 @@ impl ThreadState { /// kernel is too old to support this feature. pub fn with_calling_sid<T, F>(check_permission: F) -> T where - for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T { + for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T, + { // Safety: AIBinder_getCallingSid returns a c-string pointer // that is valid for a transaction. Also, the string returned // is thread local. By restricting the lifetime of the CStr diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index c9d6af0d75..ca2cedc19d 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -60,9 +60,7 @@ fn main() -> Result<(), &'static str> { if let Some(extension_name) = extension_name { let extension = BnTest::new_binder(TestService::new(&extension_name), BinderFeatures::default()); - service - .set_extension(&mut extension.as_binder()) - .expect("Could not add extension"); + service.set_extension(&mut extension.as_binder()).expect("Could not add extension"); } binder::add_service(&service_name, service.as_binder()) .expect("Could not register service"); @@ -73,10 +71,7 @@ fn main() -> Result<(), &'static str> { } fn print_usage() { - eprintln!( - "Usage: {} SERVICE_NAME [EXTENSION_NAME]", - RUST_SERVICE_BINARY - ); + eprintln!("Usage: {} SERVICE_NAME [EXTENSION_NAME]", RUST_SERVICE_BINARY); eprintln!(concat!( "Spawn a Binder test service identified by SERVICE_NAME,", " optionally with an extesion named EXTENSION_NAME", @@ -90,10 +85,7 @@ struct TestService { impl TestService { fn new(s: &str) -> Self { - Self { - s: s.to_string(), - dump_args: Mutex::new(Vec::new()), - } + Self { s: s.to_string(), dump_args: Mutex::new(Vec::new()) } } } @@ -111,11 +103,15 @@ impl TryFrom<u32> for TestTransactionCode { fn try_from(c: u32) -> Result<Self, Self::Error> { match c { _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test), - _ if c == TestTransactionCode::GetDumpArgs as u32 => Ok(TestTransactionCode::GetDumpArgs), + _ if c == TestTransactionCode::GetDumpArgs as u32 => { + Ok(TestTransactionCode::GetDumpArgs) + } _ if c == TestTransactionCode::GetSelinuxContext as u32 => { Ok(TestTransactionCode::GetSelinuxContext) } - _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => Ok(TestTransactionCode::GetIsHandlingTransaction), + _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => { + Ok(TestTransactionCode::GetIsHandlingTransaction) + } _ => Err(StatusCode::UNKNOWN_TRANSACTION), } } @@ -200,15 +196,16 @@ fn on_transact( TestTransactionCode::Test => reply.write(&service.test()?), TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?), TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?), - TestTransactionCode::GetIsHandlingTransaction => reply.write(&service.get_is_handling_transaction()?), + TestTransactionCode::GetIsHandlingTransaction => { + reply.write(&service.get_is_handling_transaction()?) + } } } impl ITest for BpTest { fn test(&self) -> Result<String, StatusCode> { let reply = - self.binder - .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?; + self.binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?; reply.read() } @@ -243,31 +240,45 @@ impl<P: binder::BinderAsyncPool> IATest<P> for BpTest { let binder = self.binder.clone(); P::spawn( move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + |reply| async move { reply?.read() }, ) } fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + move || { + binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())) + }, + |reply| async move { reply?.read() }, ) } fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + move || { + binder.transact( + TestTransactionCode::GetSelinuxContext as TransactionCode, + 0, + |_| Ok(()), + ) + }, + |reply| async move { reply?.read() }, ) } fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + move || { + binder.transact( + TestTransactionCode::GetIsHandlingTransaction as TransactionCode, + 0, + |_| Ok(()), + ) + }, + |reply| async move { reply?.read() }, ) } } @@ -374,7 +385,7 @@ mod tests { use binder_tokio::Tokio; - use super::{BnTest, ITest, IATest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY}; + use super::{BnTest, IATest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY}; pub struct ScopedServiceProcess(Child); @@ -405,9 +416,7 @@ mod tests { impl Drop for ScopedServiceProcess { fn drop(&mut self) { self.0.kill().expect("Could not kill child process"); - self.0 - .wait() - .expect("Could not wait for child process to die"); + self.0.wait().expect("Could not wait for child process to die"); } } @@ -428,10 +437,7 @@ mod tests { ); // The service manager service isn't an ITest, so this must fail. - assert_eq!( - binder::get_interface::<dyn ITest>("manager").err(), - Some(StatusCode::BAD_TYPE) - ); + assert_eq!(binder::get_interface::<dyn ITest>("manager").err(), Some(StatusCode::BAD_TYPE)); assert_eq!( binder::get_interface::<dyn IATest<Tokio>>("manager").err(), Some(StatusCode::BAD_TYPE) @@ -450,7 +456,9 @@ mod tests { Some(StatusCode::NAME_NOT_FOUND) ); assert_eq!( - binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").await.err(), + binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist") + .await + .err(), Some(StatusCode::NAME_NOT_FOUND) ); @@ -494,7 +502,7 @@ mod tests { let instances = binder::get_declared_instances("android.hardware.light.ILights") .expect("Could not get declared instances"); - let expected_defaults = if has_lights { 1 } else { 0 }; + let expected_defaults = usize::from(has_lights); assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count()); } @@ -511,8 +519,9 @@ mod tests { async fn trivial_client_async() { let service_name = "trivial_client_test"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); assert_eq!(test_client.test().await.unwrap(), "trivial_client_test"); } @@ -529,8 +538,9 @@ mod tests { async fn wait_for_trivial_client_async() { let service_name = "wait_for_trivial_client_test"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::wait_for_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::wait_for_interface(service_name) + .await + .expect("Did not get manager binder service"); assert_eq!(test_client.test().await.unwrap(), "wait_for_trivial_client_test"); } @@ -539,9 +549,7 @@ mod tests { let mut out_ptr = ptr::null_mut(); assert_eq!(selinux_sys::getcon(&mut out_ptr), 0); assert!(!out_ptr.is_null()); - CStr::from_ptr(out_ptr) - .to_str() - .expect("context was invalid UTF-8") + CStr::from_ptr(out_ptr).to_str().expect("context was invalid UTF-8") } } @@ -551,18 +559,16 @@ mod tests { let _process = ScopedServiceProcess::new(service_name); let test_client: Strong<dyn ITest> = binder::get_interface(service_name).expect("Did not get manager binder service"); - assert_eq!( - test_client.get_selinux_context().unwrap(), - get_expected_selinux_context() - ); + assert_eq!(test_client.get_selinux_context().unwrap(), get_expected_selinux_context()); } #[tokio::test] async fn get_selinux_context_async() { let service_name = "get_selinux_context_async"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); assert_eq!( test_client.get_selinux_context().await.unwrap(), get_expected_selinux_context() @@ -586,13 +592,11 @@ mod tests { async fn get_selinux_context_async_to_sync() { let service_name = "get_selinux_context"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); let test_client = test_client.into_sync(); - assert_eq!( - test_client.get_selinux_context().unwrap(), - get_expected_selinux_context() - ); + assert_eq!(test_client.get_selinux_context().unwrap(), get_expected_selinux_context()); } struct Bools { @@ -605,10 +609,7 @@ mod tests { self.binder_died.load(Ordering::Relaxed) } fn assert_died(&self) { - assert!( - self.is_dead(), - "Did not receive death notification" - ); + assert!(self.is_dead(), "Did not receive death notification"); } fn assert_dropped(&self) { assert!( @@ -639,9 +640,7 @@ mod tests { let mut death_recipient = { let flag = binder_died.clone(); - let set_on_drop = SetOnDrop { - binder_dealloc: binder_dealloc.clone(), - }; + let set_on_drop = SetOnDrop { binder_dealloc: binder_dealloc.clone() }; DeathRecipient::new(move || { flag.store(true, Ordering::Relaxed); // Force the closure to take ownership of set_on_drop. When the closure is @@ -650,14 +649,9 @@ mod tests { }) }; - binder - .link_to_death(&mut death_recipient) - .expect("link_to_death failed"); + binder.link_to_death(&mut death_recipient).expect("link_to_death failed"); - let bools = Bools { - binder_died, - binder_dealloc, - }; + let bools = Bools { binder_died, binder_dealloc }; (bools, death_recipient) } @@ -675,9 +669,7 @@ mod tests { let (bools, recipient) = register_death_notification(&mut remote); drop(service_process); - remote - .ping_binder() - .expect_err("Service should have died already"); + remote.ping_binder().expect_err("Service should have died already"); // Pause to ensure any death notifications get delivered thread::sleep(Duration::from_secs(1)); @@ -701,22 +693,15 @@ mod tests { let (bools, mut recipient) = register_death_notification(&mut remote); - remote - .unlink_to_death(&mut recipient) - .expect("Could not unlink death notifications"); + remote.unlink_to_death(&mut recipient).expect("Could not unlink death notifications"); drop(service_process); - remote - .ping_binder() - .expect_err("Service should have died already"); + remote.ping_binder().expect_err("Service should have died already"); // Pause to ensure any death notifications get delivered thread::sleep(Duration::from_secs(1)); - assert!( - !bools.is_dead(), - "Received unexpected death notification after unlinking", - ); + assert!(!bools.is_dead(), "Received unexpected death notification after unlinking",); bools.assert_not_dropped(); drop(recipient); @@ -771,9 +756,7 @@ mod tests { let dump_args = ["dump", "args", "for", "testing"]; let null_out = File::open("/dev/null").expect("Could not open /dev/null"); - remote - .dump(&null_out, &dump_args) - .expect("Could not dump remote service"); + remote.dump(&null_out, &dump_args).expect("Could not dump remote service"); let remote_args = test_client.get_dump_args().expect("Could not fetched dumped args"); assert_eq!(dump_args, remote_args[..], "Remote args don't match call to dump"); @@ -799,9 +782,7 @@ mod tests { let mut remote = binder::get_service(service_name); assert!(remote.is_binder_alive()); - let extension = remote - .get_extension() - .expect("Could not check for an extension"); + let extension = remote.get_extension().expect("Could not check for an extension"); assert!(extension.is_none()); } @@ -811,9 +792,7 @@ mod tests { let mut remote = binder::get_service(service_name); assert!(remote.is_binder_alive()); - let maybe_extension = remote - .get_extension() - .expect("Could not check for an extension"); + let maybe_extension = remote.get_extension().expect("Could not check for an extension"); let extension = maybe_extension.expect("Remote binder did not have an extension"); @@ -850,15 +829,12 @@ mod tests { #[test] fn reassociate_rust_binder() { let service_name = "testing_service"; - let service_ibinder = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ) - .as_binder(); + let service_ibinder = + BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()) + .as_binder(); - let service: Strong<dyn ITest> = service_ibinder - .into_interface() - .expect("Could not reassociate the generic ibinder"); + let service: Strong<dyn ITest> = + service_ibinder.into_interface().expect("Could not reassociate the generic ibinder"); assert_eq!(service.test().unwrap(), service_name); } @@ -866,10 +842,7 @@ mod tests { #[test] fn weak_binder_upgrade() { let service_name = "testing_service"; - let service = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ); + let service = BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()); let weak = Strong::downgrade(&service); @@ -882,10 +855,8 @@ mod tests { fn weak_binder_upgrade_dead() { let service_name = "testing_service"; let weak = { - let service = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ); + let service = + BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()); Strong::downgrade(&service) }; @@ -896,10 +867,7 @@ mod tests { #[test] fn weak_binder_clone() { let service_name = "testing_service"; - let service = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ); + let service = BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()); let weak = Strong::downgrade(&service); let cloned = weak.clone(); @@ -915,14 +883,10 @@ mod tests { #[test] #[allow(clippy::eq_op)] fn binder_ord() { - let service1 = BnTest::new_binder( - TestService::new("testing_service1"), - BinderFeatures::default(), - ); - let service2 = BnTest::new_binder( - TestService::new("testing_service2"), - BinderFeatures::default(), - ); + let service1 = + BnTest::new_binder(TestService::new("testing_service1"), BinderFeatures::default()); + let service2 = + BnTest::new_binder(TestService::new("testing_service2"), BinderFeatures::default()); assert!((service1 >= service1)); assert!((service1 <= service1)); @@ -931,20 +895,20 @@ mod tests { #[test] fn binder_parcel_mixup() { - let service1 = BnTest::new_binder( - TestService::new("testing_service1"), - BinderFeatures::default(), - ); - let service2 = BnTest::new_binder( - TestService::new("testing_service2"), - BinderFeatures::default(), - ); + let service1 = + BnTest::new_binder(TestService::new("testing_service1"), BinderFeatures::default()); + let service2 = + BnTest::new_binder(TestService::new("testing_service2"), BinderFeatures::default()); let service1 = service1.as_binder(); let service2 = service2.as_binder(); let parcel = service1.prepare_transact().unwrap(); - let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0); + let res = service2.submit_transact( + super::TestTransactionCode::Test as TransactionCode, + parcel, + 0, + ); match res { Ok(_) => panic!("submit_transact should fail"), @@ -967,15 +931,18 @@ mod tests { // Should also be false in spawned thread. std::thread::spawn(|| { assert!(!binder::is_handling_transaction()); - }).join().unwrap(); + }) + .join() + .unwrap(); } #[tokio::test] async fn get_is_handling_transaction_async() { let service_name = "get_is_handling_transaction_async"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); // Should be true externally. assert!(test_client.get_is_handling_transaction().await.unwrap()); @@ -985,11 +952,15 @@ mod tests { // Should also be false in spawned task. tokio::spawn(async { assert!(!binder::is_handling_transaction()); - }).await.unwrap(); + }) + .await + .unwrap(); // And in spawn_blocking task. tokio::task::spawn_blocking(|| { assert!(!binder::is_handling_transaction()); - }).await.unwrap(); + }) + .await + .unwrap(); } } diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp new file mode 100644 index 0000000000..df8a2afb03 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp @@ -0,0 +1,27 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_fuzz { + name: "parcel_fuzzer_rs", + srcs: [ + "parcel_fuzzer.rs", + ], + rustlibs: [ + "libarbitrary", + "libnum_traits", + "libbinder_rs", + "libbinder_random_parcel_rs", + "binderReadParcelIface-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/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs new file mode 100644 index 0000000000..29bf92cb97 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#![allow(missing_docs)] +#![no_main] + +mod read_utils; + +use crate::read_utils::READ_FUNCS; +use binder::binder_impl::{ + Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode, +}; +use binder::{ + declare_binder_interface, BinderFeatures, Interface, Parcelable, ParcelableHolder, SpIBinder, + StatusCode, +}; +use binder_random_parcel_rs::create_random_parcel; +use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target}; + +#[derive(Arbitrary, Debug)] +enum ReadOperation { + SetDataPosition { pos: i32 }, + GetDataSize, + ReadParcelableHolder { is_vintf: bool }, + ReadBasicTypes { instructions: Vec<usize> }, +} + +#[derive(Arbitrary, Debug)] +enum Operation<'a> { + Transact { code: u32, flag: u32, data: &'a [u8] }, + Append { start: i32, len: i32, data1: &'a [u8], data2: &'a [u8], append_all: bool }, + Read { read_operations: Vec<ReadOperation>, data: &'a [u8] }, +} + +/// Interface to fuzz transact with random parcel +pub trait BinderTransactTest: Interface {} + +declare_binder_interface! { + BinderTransactTest["Binder_Transact_Test"] { + native: BnBinderTransactTest(on_transact), + proxy: BpBinderTransactTest, + } +} + +impl BinderTransactTest for Binder<BnBinderTransactTest> {} + +impl BinderTransactTest for BpBinderTransactTest {} + +impl BinderTransactTest for () {} + +fn on_transact( + _service: &dyn BinderTransactTest, + _code: TransactionCode, + _parcel: &BorrowedParcel<'_>, + _reply: &mut BorrowedParcel<'_>, +) -> Result<(), StatusCode> { + Err(StatusCode::UNKNOWN_ERROR) +} + +fn do_transact(code: u32, data: &[u8], flag: u32) { + let p: Parcel = create_random_parcel(data); + let spibinder: Option<SpIBinder> = + Some(BnBinderTransactTest::new_binder((), BinderFeatures::default()).as_binder()); + let _reply = spibinder.submit_transact(code, p, flag); +} + +fn do_append_fuzz(start: i32, len: i32, data1: &[u8], data2: &[u8], append_all: bool) { + let mut p1 = create_random_parcel(data1); + let p2 = create_random_parcel(data2); + + // Fuzz both append methods + if append_all { + match p1.append_all_from(&p2) { + Ok(result) => result, + Err(e) => { + println!("Error occurred while appending a parcel using append_all_from: {:?}", e) + } + } + } else { + match p1.append_from(&p2, start, len) { + Ok(result) => result, + Err(e) => { + println!("Error occurred while appending a parcel using append_from: {:?}", e) + } + } + }; +} + +fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) { + let parcel = create_random_parcel(data); + + for operation in read_operations { + match operation { + ReadOperation::SetDataPosition { pos } => { + 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), + } + } + } + + ReadOperation::GetDataSize => { + let data_size = parcel.get_data_size(); + println!("data size from parcel: {:?}", data_size); + } + + ReadOperation::ReadParcelableHolder { is_vintf } => { + let stability = if is_vintf { Stability::Vintf } else { Stability::Local }; + let mut holder: ParcelableHolder = ParcelableHolder::new(stability); + match holder.read_from_parcel(parcel.borrowed_ref()) { + Ok(result) => result, + Err(err) => { + println!("error occurred while reading from parcel: {:?}", err) + } + } + } + + ReadOperation::ReadBasicTypes { instructions } => { + for instruction in instructions.iter() { + let read_index = instruction % READ_FUNCS.len(); + READ_FUNCS[read_index](parcel.borrowed_ref()); + } + } + } + } +} + +fuzz_target!(|operation: Operation| { + match operation { + Operation::Transact { code, flag, data } => { + do_transact(code, data, flag); + } + + Operation::Append { start, len, data1, data2, append_all } => { + do_append_fuzz(start, len, data1, data2, append_all); + } + + Operation::Read { read_operations, data } => { + do_read_fuzz(read_operations, data); + } + } +}); diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp new file mode 100644 index 0000000000..43a309409d --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp @@ -0,0 +1,52 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_bindgen { + name: "libbinder_random_parcel_bindgen", + crate_name: "binder_random_parcel_bindgen", + host_supported: true, + wrapper_src: "wrappers/RandomParcelWrapper.hpp", + source_stem: "bindings", + visibility: [":__subpackages__"], + bindgen_flags: [ + "--size_t-is-usize", + "--allowlist-function", + "createRandomParcel", + "--allowlist-function", + "fuzzRustService", + ], + shared_libs: [ + "libc++", + "libbinder_ndk", + ], + rustlibs: [ + "libbinder_rs", + ], +} + +rust_library { + name: "libbinder_random_parcel_rs", + crate_name: "binder_random_parcel_rs", + host_supported: true, + srcs: [ + "src/lib.rs", + ], + shared_libs: [ + "libbinder", + "libutils", + "libcutils", + "libc++", + ], + static_libs: [ + "libbinder_create_parcel", + "libbinder_random_parcel", + ], + rustlibs: [ + "libbinder_rs", + "libbinder_random_parcel_bindgen", + ], + lints: "none", + clippy_lints: "none", +} 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 new file mode 100644 index 0000000000..5cb406afc2 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp @@ -0,0 +1,35 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +aidl_interface { + name: "testServiceInterface", + srcs: ["ITestService.aidl"], + unstable: true, + backend: { + rust: { + enabled: true, + }, + }, +} + +rust_fuzz { + name: "example_service_fuzzer", + srcs: [ + "service_fuzzer.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/fuzz_service_test/ITestService.aidl b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl new file mode 100644 index 0000000000..8ce655862f --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl @@ -0,0 +1,19 @@ +/* + * 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. + */ + +interface ITestService { + boolean repeatData(boolean token); +}
\ No newline at end of file diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs new file mode 100644 index 0000000000..c53038210c --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#![allow(missing_docs)] +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use binder::{self, BinderFeatures, Interface}; +use binder_random_parcel_rs::fuzz_service; +use testServiceInterface::aidl::ITestService::{self, BnTestService}; + +struct TestService; + +impl Interface for TestService {} + +impl ITestService::ITestService for TestService { + fn repeatData(&self, token: bool) -> binder::Result<bool> { + Ok(token) + } +} + +fuzz_target!(|data: &[u8]| { + let service = BnTestService::new_binder(TestService, BinderFeatures::default()); + fuzz_service(&mut service.as_binder(), data); +}); 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 new file mode 100644 index 0000000000..1bbd6742f2 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs @@ -0,0 +1,44 @@ +/* + * 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. + */ + +use binder::binder_impl::Parcel; +use binder::unstable_api::{AParcel, AsNative}; +use binder::SpIBinder; +use binder_random_parcel_bindgen::{createRandomParcel, fuzzRustService}; +use std::os::raw::c_void; + +/// This API creates a random parcel to be used by fuzzers +pub fn create_random_parcel(fuzzer_data: &[u8]) -> Parcel { + let mut parcel = Parcel::new(); + let aparcel_ptr: *mut AParcel = parcel.as_native_mut(); + let ptr = aparcel_ptr as *mut c_void; + unsafe { + // Safety: `Parcel::as_native_mut` and `slice::as_ptr` always + // return valid pointers. + createRandomParcel(ptr, fuzzer_data.as_ptr(), fuzzer_data.len()); + } + 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; + unsafe { + // Safety: `SpIBinder::as_native_mut` and `slice::as_ptr` always + // return valid pointers. + fuzzRustService(ptr, 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 new file mode 100644 index 0000000000..831bd5660c --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <cstdint> +#include <cstddef> + +extern "C" { + // This API is used by rust to fill random parcel. + 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 diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs new file mode 100644 index 0000000000..a2d48b6115 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs @@ -0,0 +1,133 @@ +/* + * 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. + */ + +use binder::binder_impl::BorrowedParcel; +use binder::{ParcelFileDescriptor, Parcelable, SpIBinder}; +use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable; +use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable; +use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable; + +macro_rules! read_parcel_interface { + ($data_type:ty) => { + |parcel: &BorrowedParcel<'_>| { + let _res = parcel.read::<$data_type>(); + } + }; +} + +#[derive(Debug, Default)] +pub struct SomeParcelable { + pub data: i32, +} + +impl binder::Parcelable for SomeParcelable { + fn write_to_parcel( + &self, + parcel: &mut binder::binder_impl::BorrowedParcel, + ) -> std::result::Result<(), binder::StatusCode> { + parcel.sized_write(|subparcel| subparcel.write(&self.data)) + } + + fn read_from_parcel( + &mut self, + parcel: &binder::binder_impl::BorrowedParcel, + ) -> std::result::Result<(), binder::StatusCode> { + parcel.sized_read(|subparcel| match subparcel.read() { + Ok(result) => { + self.data = result; + Ok(()) + } + Err(e) => Err(e), + }) + } +} + +binder::impl_deserialize_for_parcelable!(SomeParcelable); + +pub const READ_FUNCS: &[fn(&BorrowedParcel<'_>)] = &[ + //read basic types + read_parcel_interface!(bool), + read_parcel_interface!(i8), + read_parcel_interface!(i32), + read_parcel_interface!(i64), + read_parcel_interface!(f32), + read_parcel_interface!(f64), + read_parcel_interface!(u16), + read_parcel_interface!(u32), + read_parcel_interface!(u64), + read_parcel_interface!(String), + //read vec of basic types + read_parcel_interface!(Vec<i8>), + read_parcel_interface!(Vec<i32>), + read_parcel_interface!(Vec<i64>), + read_parcel_interface!(Vec<f32>), + read_parcel_interface!(Vec<f64>), + read_parcel_interface!(Vec<u16>), + read_parcel_interface!(Vec<u32>), + read_parcel_interface!(Vec<u64>), + read_parcel_interface!(Vec<String>), + read_parcel_interface!(Option<Vec<i8>>), + read_parcel_interface!(Option<Vec<i32>>), + read_parcel_interface!(Option<Vec<i64>>), + read_parcel_interface!(Option<Vec<f32>>), + read_parcel_interface!(Option<Vec<f64>>), + read_parcel_interface!(Option<Vec<u16>>), + read_parcel_interface!(Option<Vec<u32>>), + read_parcel_interface!(Option<Vec<u64>>), + read_parcel_interface!(Option<Vec<String>>), + read_parcel_interface!(ParcelFileDescriptor), + read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>), + read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>), + read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>), + read_parcel_interface!(SpIBinder), + read_parcel_interface!(Vec<Option<SpIBinder>>), + read_parcel_interface!(Option<Vec<SpIBinder>>), + read_parcel_interface!(Option<Vec<Option<SpIBinder>>>), + read_parcel_interface!(SomeParcelable), + read_parcel_interface!(Vec<Option<SomeParcelable>>), + read_parcel_interface!(Option<Vec<SomeParcelable>>), + read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>), + // Fuzz read_from_parcel for AIDL generated parcelables + |parcel| { + let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default(); + match empty_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => { + println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e) + } + } + }, + |parcel| { + let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default(); + match single_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => println!( + "SingleDataParcelable: error occurred while reading from a parcel: {:?}", + e + ), + } + }, + |parcel| { + let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default(); + match generic_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => println!( + "GenericDataParcelable: error occurred while reading from a parcel: {:?}", + e + ), + } + }, +]; diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp index ec780f28a1..3f59dab3a9 100644 --- a/libs/binder/rust/tests/serialization.cpp +++ b/libs/binder/rust/tests/serialization.cpp @@ -381,7 +381,7 @@ TEST_F(SerializationTest, SerializeFileDescriptor) { string expected = "TestingFileDescriptors"; vector<char> buf(expected.length()); base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size()); - ASSERT_EQ(expected, string(buf.data())); + ASSERT_EQ(expected, string(buf.data(), expected.length())); } TEST_F(SerializationTest, SerializeIBinder) { diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index b62da7be03..6220db4b28 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -23,7 +23,7 @@ use binder::{ }; // Import from impl API for testing only, should not be necessary as long as you // are using AIDL. -use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode}; +use binder::binder_impl::{Binder, BorrowedParcel, TransactionCode}; use std::ffi::{c_void, CStr, CString}; use std::sync::Once; @@ -64,13 +64,7 @@ macro_rules! assert_eq { macro_rules! assert { ($expr:expr) => { if !$expr { - eprintln!( - "assertion failed: `{:?}`, {}:{}:{}", - $expr, - file!(), - line!(), - column!() - ); + eprintln!("assertion failed: `{:?}`, {}:{}:{}", $expr, file!(), line!(), column!()); return Err(StatusCode::FAILED_TRANSACTION); } }; @@ -117,11 +111,9 @@ fn on_transact( ) -> Result<(), StatusCode> { match code { bindings::Transaction_TEST_BOOL => { - assert_eq!(parcel.read::<bool>()?, true); - assert_eq!(parcel.read::<bool>()?, false); - assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { - bindings::TESTDATA_BOOL - }); + assert!(parcel.read::<bool>()?); + assert!(!parcel.read::<bool>()?); + assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL }); assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None); reply.write(&true)?; @@ -148,9 +140,7 @@ fn on_transact( assert_eq!(parcel.read::<u16>()?, 0); assert_eq!(parcel.read::<u16>()?, 1); assert_eq!(parcel.read::<u16>()?, u16::max_value()); - assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { - bindings::TESTDATA_CHARS - }); + assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None); reply.write(&0u16)?; @@ -163,9 +153,7 @@ fn on_transact( assert_eq!(parcel.read::<i32>()?, 0); assert_eq!(parcel.read::<i32>()?, 1); assert_eq!(parcel.read::<i32>()?, i32::max_value()); - assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { - bindings::TESTDATA_I32 - }); + assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None); reply.write(&0i32)?; @@ -178,9 +166,7 @@ fn on_transact( assert_eq!(parcel.read::<i64>()?, 0); assert_eq!(parcel.read::<i64>()?, 1); assert_eq!(parcel.read::<i64>()?, i64::max_value()); - assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { - bindings::TESTDATA_I64 - }); + assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None); reply.write(&0i64)?; @@ -193,9 +179,7 @@ fn on_transact( assert_eq!(parcel.read::<u64>()?, 0); assert_eq!(parcel.read::<u64>()?, 1); assert_eq!(parcel.read::<u64>()?, u64::max_value()); - assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { - bindings::TESTDATA_U64 - }); + assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None); reply.write(&0u64)?; @@ -232,16 +216,9 @@ fn on_transact( let s: Option<String> = parcel.read()?; assert_eq!(s, None); let s: Option<Vec<Option<String>>> = parcel.read()?; - for (s, expected) in s - .unwrap() - .iter() - .zip(unsafe { bindings::TESTDATA_STRS }.iter()) - { - let expected = unsafe { - expected - .as_ref() - .and_then(|e| CStr::from_ptr(e).to_str().ok()) - }; + for (s, expected) in s.unwrap().iter().zip(unsafe { bindings::TESTDATA_STRS }.iter()) { + let expected = + unsafe { expected.as_ref().and_then(|e| CStr::from_ptr(e).to_str().ok()) }; assert_eq!(s.as_deref(), expected); } let s: Option<Vec<Option<String>>> = parcel.read()?; @@ -252,10 +229,7 @@ fn on_transact( .iter() .map(|s| { s.as_ref().map(|s| { - CStr::from_ptr(s) - .to_str() - .expect("String was not UTF-8") - .to_owned() + CStr::from_ptr(s).to_str().expect("String was not UTF-8").to_owned() }) }) .collect() @@ -284,12 +258,8 @@ fn on_transact( assert!(ibinders[1].is_none()); assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.is_none()); - let service = unsafe { - SERVICE - .as_ref() - .expect("Global binder service not initialized") - .clone() - }; + let service = + unsafe { SERVICE.as_ref().expect("Global binder service not initialized").clone() }; reply.write(&service)?; reply.write(&(None as Option<&SpIBinder>))?; reply.write(&[Some(&service), None][..])?; @@ -300,10 +270,7 @@ fn on_transact( assert!(status.is_ok()); let status: Status = parcel.read()?; assert_eq!(status.exception_code(), ExceptionCode::NULL_POINTER); - assert_eq!( - status.get_description(), - "Status(-4, EX_NULL_POINTER): 'a status message'" - ); + assert_eq!(status.get_description(), "Status(-4, EX_NULL_POINTER): 'a status message'"); let status: Status = parcel.read()?; assert_eq!(status.service_specific_error(), 42); assert_eq!( diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp index 777f3c9354..692cc95e3b 100644 --- a/libs/binder/servicedispatcher.cpp +++ b/libs/binder/servicedispatcher.cpp @@ -156,6 +156,10 @@ public: std::optional<std::string>* _aidl_return) override { return mImpl->updatableViaApex(name, _aidl_return); } + android::binder::Status getUpdatableNames(const std::string& apexName, + std::vector<std::string>* _aidl_return) override { + return mImpl->getUpdatableNames(apexName, _aidl_return); + } android::binder::Status getConnectionInfo( const std::string& name, std::optional<android::os::ConnectionInfo>* _aidl_return) override { diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 9ebeca0006..0f0d64aea8 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -100,6 +100,7 @@ cc_test { "binderBinderUnitTest.cpp", "binderStatusUnitTest.cpp", "binderMemoryHeapBaseUnitTest.cpp", + "binderRecordedTransactionTest.cpp", ], shared_libs: [ "libbinder", @@ -141,6 +142,7 @@ aidl_interface { unstable: true, srcs: [ "BinderRpcTestClientInfo.aidl", + "BinderRpcTestServerConfig.aidl", "BinderRpcTestServerInfo.aidl", "IBinderRpcCallback.aidl", "IBinderRpcSession.aidl", @@ -167,7 +169,6 @@ cc_library_static { "libbinder_tls_shared_deps", ], shared_libs: [ - "libbinder", "libbase", "liblog", ], @@ -185,30 +186,73 @@ cc_library_static { ], } -cc_test { - name: "binderRpcTest", - // b/269799024 - test_options: { - unit_test: false, - }, - +cc_defaults { + name: "binderRpcTest_common_defaults", host_supported: true, target: { darwin: { enabled: false, }, + }, + defaults: [ + "binder_test_defaults", + ], + + static_libs: [ + "libbinder_tls_static", + "libbinder_tls_test_utils", + "binderRpcTestIface-cpp", + "binderRpcTestIface-ndk", + ], +} + +cc_defaults { + name: "binderRpcTest_service_defaults", + defaults: [ + "binderRpcTest_common_defaults", + ], + gtest: false, + auto_gen_config: false, + srcs: [ + "binderRpcTestCommon.cpp", + "binderRpcTestService.cpp", + ], +} + +cc_defaults { + name: "binderRpcTest_defaults", + target: { android: { test_suites: ["vts"], }, }, defaults: [ - "binder_test_defaults", - "libbinder_tls_shared_deps", + "binderRpcTest_common_defaults", ], srcs: [ "binderRpcTest.cpp", + "binderRpcTestCommon.cpp", + "binderRpcUniversalTests.cpp", ], + + test_suites: ["general-tests"], + require_root: true, + + data_bins: [ + "binder_rpc_test_service", + "binder_rpc_test_service_no_kernel", + "binder_rpc_test_service_single_threaded", + "binder_rpc_test_service_single_threaded_no_kernel", + ], +} + +cc_defaults { + name: "binderRpcTest_shared_defaults", + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], + shared_libs: [ "libbinder", "libbinder_ndk", @@ -217,14 +261,183 @@ cc_test { "libcutils", "liblog", ], +} + +cc_defaults { + name: "binderRpcTest_static_defaults", + + shared_libs: [ + "liblog", + "libutils", + // libcrypto_static is not visible to this module + "libcrypto", + ], + static_libs: [ + "libbase", + "libcutils", + "libssl", + ], + + cflags: [ + // Disable tests that require shared libraries, + // e.g., libbinder.so or libbinder_ndk.so + "-DBINDER_TEST_NO_SHARED_LIBS", + ], +} + +cc_test { + // The module name cannot start with "binderRpcTest" because + // then atest tries to execute it as part of binderRpcTest + name: "binder_rpc_test_service", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_shared_defaults", + "libbinder_tls_shared_deps", + ], +} + +cc_test { + name: "binder_rpc_test_service_no_kernel", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], +} + +cc_test { + name: "binder_rpc_test_service_single_threaded", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_WITH_KERNEL_IPC", + ], + static_libs: [ + "libbinder_rpc_single_threaded", + ], +} + +cc_test { + name: "binder_rpc_test_service_single_threaded_no_kernel", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + static_libs: [ + "libbinder_rpc_single_threaded_no_kernel", + ], +} + +cc_binary { + name: "binderRpcTestService_on_trusty_mock", + defaults: [ + "trusty_mock_defaults", + ], + + srcs: [ + "binderRpcTestCommon.cpp", + "binderRpcTestServiceTrusty.cpp", + ], + + shared_libs: [ + "libbinder_on_trusty_mock", + "libbase", + "libutils", + "libcutils", + ], + static_libs: [ - "libbinder_tls_static", - "libbinder_tls_test_utils", "binderRpcTestIface-cpp", - "binderRpcTestIface-ndk", ], - test_suites: ["general-tests"], - require_root: true, +} + +cc_binary { + name: "binderRpcTest_on_trusty_mock", + defaults: [ + "trusty_mock_defaults", + ], + + srcs: [ + "binderRpcUniversalTests.cpp", + "binderRpcTestCommon.cpp", + "binderRpcTestTrusty.cpp", + ], + + shared_libs: [ + "libbinder_on_trusty_mock", + "libbase", + "libutils", + "libcutils", + ], + + static_libs: [ + "binderRpcTestIface-cpp", + "libgtest", + ], +} + +cc_test { + name: "binderRpcTest", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_shared_defaults", + "libbinder_tls_shared_deps", + ], + + // Add the Trusty mock library as a fake dependency so it gets built + required: [ + "libbinder_on_trusty_mock", + "binderRpcTestService_on_trusty_mock", + "binderRpcTest_on_trusty_mock", + ], +} + +cc_test { + name: "binderRpcTestNoKernel", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], +} + +cc_test { + name: "binderRpcTestSingleThreaded", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_WITH_KERNEL_IPC", + ], + static_libs: [ + "libbinder_rpc_single_threaded", + ], +} + +cc_test { + name: "binderRpcTestSingleThreadedNoKernel", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + static_libs: [ + "libbinder_rpc_single_threaded_no_kernel", + ], } cc_test { @@ -324,7 +537,6 @@ cc_test { "libbinder", "libutils", ], - clang: true, cflags: [ "-g", "-Wno-missing-field-initializers", @@ -445,6 +657,7 @@ cc_test { shared_libs: [ "libbinder", "liblog", + "libcutils", "libutils", "libutilscallstack", "libbase", @@ -529,3 +742,39 @@ cc_test { ], test_suites: ["general-tests"], } + +cc_defaults { + name: "service_fuzzer_defaults", + static_libs: [ + "libbase", + "libbinder_random_parcel", + "libcutils", + ], + target: { + android: { + shared_libs: [ + "libbinder_ndk", + "libbinder", + "libutils", + ], + }, + host: { + static_libs: [ + "libbinder_ndk", + "libbinder", + "libutils", + ], + }, + darwin: { + enabled: false, + }, + }, + fuzz_config: { + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer + hotlists: ["4637097"], + }, +} diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl new file mode 100644 index 0000000000..b2e0ef21c7 --- /dev/null +++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcelable BinderRpcTestServerConfig { + int numThreads; + int[] serverSupportedFileDescriptorTransportModes; + int socketType; + int rpcSecurity; + int serverVersion; + int vsockPort; + int socketFd; // Inherited from the parent process. + @utf8InCpp String addr; +} diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index fdd02a4435..a3ed571462 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -24,6 +24,9 @@ interface IBinderRpcTest { // number of known RPC binders to process, RpcState::countBinders by session int[] countBinders(); + // Return a null binder with a non-nullable return type. + IBinder getNullBinder(); + // Caller sends server, callee pings caller's server and returns error code. int pingMe(IBinder binder); @nullable IBinder repeatBinder(@nullable IBinder binder); @@ -64,4 +67,17 @@ interface IBinderRpcTest { void scheduleShutdown(); void useKernelBinderCallingId(); + + ParcelFileDescriptor echoAsFile(@utf8InCpp String content); + + ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files); + + // FDs sent via `blockingSendFdOneway` can be received via + // `blockingRecvFd`. The handler for `blockingSendFdOneway` will block + // until the next `blockingRecvFd` call. + // + // This is useful for carefully controlling how/when oneway transactions + // get queued. + oneway void blockingSendFdOneway(in ParcelFileDescriptor fd); + ParcelFileDescriptor blockingRecvFd(); } diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index e1f5ed5381..bc40864020 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -15,8 +15,12 @@ */ #include <android-base/logging.h> -#include <binder/Parcel.h> +#include <binder/Binder.h> #include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <cutils/trace.h> #include <gtest/gtest.h> #include <utils/CallStack.h> @@ -24,6 +28,8 @@ #include <functional> #include <vector> +static android::String8 gEmpty(""); // make sure first allocation from optimization runs + struct DestructionAction { DestructionAction(std::function<void()> f) : mF(std::move(f)) {} ~DestructionAction() { mF(); }; @@ -124,12 +130,18 @@ DestructionAction ScopeDisallowMalloc() { }); } +using android::BBinder; +using android::defaultServiceManager; using android::IBinder; +using android::IServiceManager; +using android::OK; using android::Parcel; -using android::String16; -using android::defaultServiceManager; +using android::RpcServer; +using android::RpcSession; using android::sp; -using android::IServiceManager; +using android::status_t; +using android::statusToString; +using android::String16; static sp<IBinder> GetRemoteBinder() { // This gets binder representing the service manager @@ -160,6 +172,28 @@ TEST(BinderAllocation, PingTransaction) { a_binder->pingBinder(); } +TEST(BinderAllocation, InterfaceDescriptorTransaction) { + sp<IBinder> a_binder = GetRemoteBinder(); + + size_t mallocs = 0; + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + // Happens to be SM package length. We could switch to forking + // and registering our own service if it became an issue. +#if defined(__LP64__) + EXPECT_EQ(bytes, 78); +#else + EXPECT_EQ(bytes, 70); +#endif + }); + + a_binder->getInterfaceDescriptor(); + a_binder->getInterfaceDescriptor(); + a_binder->getInterfaceDescriptor(); + + EXPECT_EQ(mallocs, 1); +} + TEST(BinderAllocation, SmallTransaction) { String16 empty_descriptor = String16(""); sp<IServiceManager> manager = defaultServiceManager(); @@ -175,6 +209,36 @@ TEST(BinderAllocation, SmallTransaction) { EXPECT_EQ(mallocs, 1); } +TEST(RpcBinderAllocation, SetupRpcServer) { + std::string tmp = getenv("TMPDIR") ?: "/tmp"; + std::string addr = tmp + "/binderRpcBenchmark"; + (void)unlink(addr.c_str()); + auto server = RpcServer::make(); + server->setRootObject(sp<BBinder>::make()); + + CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())); + + std::thread([server]() { server->join(); }).detach(); + + status_t status; + auto session = RpcSession::make(); + status = session->setupUnixDomainClient(addr.c_str()); + CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str(); + + auto remoteBinder = session->getRootObject(); + + size_t mallocs = 0, totalBytes = 0; + { + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + totalBytes += bytes; + }); + CHECK_EQ(OK, remoteBinder->pingBinder()); + } + EXPECT_EQ(mallocs, 1); + EXPECT_EQ(totalBytes, 40); +} + int main(int argc, char** argv) { if (getenv("LIBC_HOOKS_ENABLE") == nullptr) { CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/)); @@ -182,5 +246,10 @@ int main(int argc, char** argv) { return 1; } ::testing::InitGoogleTest(&argc, argv); + + // if tracing is enabled, take in one-time cost + (void)ATRACE_INIT(); + (void)ATRACE_GET_ENABLED_TAGS(); + return RUN_ALL_TESTS(); } diff --git a/libs/binder/tests/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp index ce2770f943..b6aed0db28 100644 --- a/libs/binder/tests/binderBinderUnitTest.cpp +++ b/libs/binder/tests/binderBinderUnitTest.cpp @@ -15,10 +15,11 @@ */ #include <binder/Binder.h> -#include <binder/IBinder.h> +#include <binder/IInterface.h> #include <gtest/gtest.h> using android::BBinder; +using android::IBinder; using android::OK; using android::sp; @@ -48,3 +49,49 @@ TEST(Binder, AttachExtension) { binder->setExtension(ext); EXPECT_EQ(ext, binder->getExtension()); } + +struct MyCookie { + bool* deleted; +}; + +class UniqueBinder : public BBinder { +public: + UniqueBinder(const void* c) : cookie(reinterpret_cast<const MyCookie*>(c)) { + *cookie->deleted = false; + } + ~UniqueBinder() { *cookie->deleted = true; } + const MyCookie* cookie; +}; + +static sp<IBinder> make(const void* arg) { + return sp<UniqueBinder>::make(arg); +} + +TEST(Binder, LookupOrCreateWeak) { + auto binder = sp<BBinder>::make(); + bool deleted; + MyCookie cookie = {&deleted}; + sp<IBinder> createdBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie); + EXPECT_NE(binder, createdBinder); + + sp<IBinder> lookedUpBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie); + EXPECT_EQ(createdBinder, lookedUpBinder); + EXPECT_FALSE(deleted); +} + +TEST(Binder, LookupOrCreateWeakDropSp) { + auto binder = sp<BBinder>::make(); + bool deleted1 = false; + bool deleted2 = false; + MyCookie cookie1 = {&deleted1}; + MyCookie cookie2 = {&deleted2}; + sp<IBinder> createdBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie1); + EXPECT_NE(binder, createdBinder); + + createdBinder.clear(); + EXPECT_TRUE(deleted1); + + sp<IBinder> lookedUpBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie2); + EXPECT_EQ(&cookie2, sp<UniqueBinder>::cast(lookedUpBinder)->cookie); + EXPECT_FALSE(deleted2); +} diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index b1e17b7892..955c650205 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -30,6 +30,7 @@ #include <android-base/properties.h> #include <android-base/result-gmock.h> #include <android-base/result.h> +#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <binder/Binder.h> @@ -82,6 +83,7 @@ static 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 String16 binderLibTestServiceName = String16("test.binderLib"); @@ -113,8 +115,16 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, BINDER_LIB_TEST_GETPID, BINDER_LIB_TEST_ECHO_VECTOR, + BINDER_LIB_TEST_GET_NON_BLOCKING_FD, BINDER_LIB_TEST_REJECT_OBJECTS, BINDER_LIB_TEST_CAN_GET_SID, + BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, + BINDER_LIB_TEST_SET_MAX_THREAD_COUNT, + BINDER_LIB_TEST_IS_THREADPOOL_STARTED, + BINDER_LIB_TEST_LOCK_UNLOCK, + BINDER_LIB_TEST_PROCESS_LOCK, + BINDER_LIB_TEST_UNLOCK_AFTER_MS, + BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -247,13 +257,11 @@ class BinderLibTest : public ::testing::Test { { int32_t id; Parcel data, reply; - sp<IBinder> binder; EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR)); - EXPECT_FALSE(binder != nullptr); - binder = reply.readStrongBinder(); - EXPECT_TRUE(binder != nullptr); + sp<IBinder> binder = reply.readStrongBinder(); + EXPECT_NE(nullptr, binder); EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); if (idPtr) *idPtr = id; @@ -442,6 +450,12 @@ TEST_F(BinderLibTest, CannotUseBinderAfterFork) { EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork"); } +TEST_F(BinderLibTest, AddManagerToManager) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = IInterface::asBinder(sm); + EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder)); +} + TEST_F(BinderLibTest, WasParceled) { auto binder = sp<BBinder>::make(); EXPECT_FALSE(binder->wasParceled()); @@ -1146,6 +1160,56 @@ TEST_F(BinderLibTest, VectorSent) { EXPECT_EQ(readValue, testValue); } +TEST_F(BinderLibTest, FileDescriptorRemainsNonBlocking) { + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + Parcel reply; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_NON_BLOCKING_FD, {} /*data*/, &reply), + StatusEq(NO_ERROR)); + base::unique_fd fd; + EXPECT_THAT(reply.readUniqueFileDescriptor(&fd), StatusEq(OK)); + + const int result = fcntl(fd.get(), F_GETFL); + ASSERT_NE(result, -1); + EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK); +} + +// see ProcessState.cpp BINDER_VM_SIZE = 1MB. +// This value is not exposed, but some code in the framework relies on being able to use +// buffers near the cap size. +constexpr size_t kSizeBytesAlmostFull = 950'000; +constexpr size_t kSizeBytesOverFull = 1'050'000; + +TEST_F(BinderLibTest, GargantuanVectorSent) { + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + for (size_t i = 0; i < 10; i++) { + // a slight variation in size is used to consider certain possible caching implementations + const std::vector<uint64_t> testValue((kSizeBytesAlmostFull + i) / sizeof(uint64_t), 42); + + Parcel data, reply; + data.writeUint64Vector(testValue); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR)) + << i; + std::vector<uint64_t> readValue; + EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK)); + EXPECT_EQ(readValue, testValue); + } +} + +TEST_F(BinderLibTest, LimitExceededVectorSent) { + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + const std::vector<uint64_t> testValue(kSizeBytesOverFull / sizeof(uint64_t), 42); + + Parcel data, reply; + data.writeUint64Vector(testValue); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), + StatusEq(FAILED_TRANSACTION)); +} + TEST_F(BinderLibTest, BufRejected) { Parcel data, reply; uint32_t buf; @@ -1221,6 +1285,53 @@ TEST_F(BinderLibTest, GotSid) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } +struct TooManyFdsFlattenable : Flattenable<TooManyFdsFlattenable> { + TooManyFdsFlattenable(size_t fdCount) : mFdCount(fdCount) {} + + // Flattenable protocol + size_t getFlattenedSize() const { + // Return a valid non-zero size here so we don't get an unintended + // BAD_VALUE from Parcel::write + return 16; + } + size_t getFdCount() const { return mFdCount; } + status_t flatten(void *& /*buffer*/, size_t & /*size*/, int *&fds, size_t &count) const { + for (size_t i = 0; i < count; i++) { + fds[i] = STDIN_FILENO; + } + return NO_ERROR; + } + status_t unflatten(void const *& /*buffer*/, size_t & /*size*/, int const *& /*fds*/, + size_t & /*count*/) { + /* This doesn't get called */ + return NO_ERROR; + } + + size_t mFdCount; +}; + +TEST_F(BinderLibTest, TooManyFdsFlattenable) { + rlimit origNofile; + int ret = getrlimit(RLIMIT_NOFILE, &origNofile); + ASSERT_EQ(0, ret); + + // Restore the original file limits when the test finishes + base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + + rlimit testNofile = {1024, 1024}; + ret = setrlimit(RLIMIT_NOFILE, &testNofile); + ASSERT_EQ(0, ret); + + Parcel parcel; + // Try to write more file descriptors than supported by the OS + TooManyFdsFlattenable tooManyFds1(1024); + EXPECT_THAT(parcel.write(tooManyFds1), StatusEq(-EMFILE)); + + // Try to write more file descriptors than the internal limit + TooManyFdsFlattenable tooManyFds2(1025); + EXPECT_THAT(parcel.write(tooManyFds2), StatusEq(BAD_VALUE)); +} + TEST(ServiceNotifications, Unregister) { auto sm = defaultServiceManager(); using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; @@ -1234,6 +1345,89 @@ TEST(ServiceNotifications, Unregister) { EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK); } +TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), + StatusEq(NO_ERROR)); + int32_t replyi = reply.readInt32(); + // Expect 16 threads: kKernelThreads = 15 + Pool thread == 16 + EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_LOCK, data, &reply), NO_ERROR); + + /* + * This will use all threads in the pool expect the main pool thread. + * The service should run fine without locking, and the thread count should + * not exceed 16 (15 Max + pool thread). + */ + std::vector<std::thread> ts; + for (size_t i = 0; i < kKernelThreads; i++) { + ts.push_back(std::thread([&] { + Parcel local_reply; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply), + NO_ERROR); + })); + } + + data.writeInt32(100); + // Give a chance for all threads to be used + EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR); + + for (auto &t : ts) { + t.join(); + } + + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), + StatusEq(NO_ERROR)); + replyi = reply.readInt32(); + EXPECT_EQ(replyi, kKernelThreads + 1); +} + +TEST_F(BinderLibTest, ThreadPoolStarted) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR); + EXPECT_TRUE(reply.readBool()); +} + +size_t epochMillis() { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::system_clock; + return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); +} + +TEST_F(BinderLibTest, HangingServices) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + int32_t delay = 1000; // ms + data.writeInt32(delay); + // b/266537959 - must take before taking lock, since countdown is started in the remote + // process there. + size_t epochMsBefore = epochMillis(); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR); + std::vector<std::thread> ts; + for (size_t i = 0; i < kKernelThreads + 1; i++) { + ts.push_back(std::thread([&] { + Parcel local_reply; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply), + NO_ERROR); + })); + } + + for (auto &t : ts) { + t.join(); + } + size_t epochMsAfter = epochMillis(); + + // deadlock occurred and threads only finished after 1s passed. + EXPECT_GE(epochMsAfter, epochMsBefore + delay); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { @@ -1634,17 +1828,74 @@ public: reply->writeUint64Vector(vector); return NO_ERROR; } + case BINDER_LIB_TEST_GET_NON_BLOCKING_FD: { + std::array<int, 2> sockets; + const bool created = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets.data()) == 0; + if (!created) { + ALOGE("Could not create socket pair"); + return UNKNOWN_ERROR; + } + + const int result = fcntl(sockets[0], F_SETFL, O_NONBLOCK); + if (result != 0) { + ALOGE("Could not make socket non-blocking: %s", strerror(errno)); + return UNKNOWN_ERROR; + } + base::unique_fd out(sockets[0]); + status_t writeResult = reply->writeUniqueFileDescriptor(out); + if (writeResult != NO_ERROR) { + ALOGE("Could not write unique_fd"); + return writeResult; + } + close(sockets[1]); // we don't need the other side of the fd + return NO_ERROR; + } case BINDER_LIB_TEST_REJECT_OBJECTS: { return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR; } case BINDER_LIB_TEST_CAN_GET_SID: { return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR; } + case BINDER_LIB_TEST_GET_MAX_THREAD_COUNT: { + reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount()); + return NO_ERROR; + } + case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: { + reply->writeBool(ProcessState::self()->isThreadPoolStarted()); + return NO_ERROR; + } + case BINDER_LIB_TEST_PROCESS_LOCK: { + m_blockMutex.lock(); + return NO_ERROR; + } + case BINDER_LIB_TEST_LOCK_UNLOCK: { + std::lock_guard<std::mutex> _l(m_blockMutex); + return NO_ERROR; + } + case BINDER_LIB_TEST_UNLOCK_AFTER_MS: { + int32_t ms = data.readInt32(); + return unlockInMs(ms); + } + case BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK: { + m_blockMutex.lock(); + sp<BinderLibTestService> thisService = this; + int32_t value = data.readInt32(); + // start local thread to unlock in 1s + std::thread t([=] { thisService->unlockInMs(value); }); + t.detach(); + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; } + status_t unlockInMs(int32_t ms) { + usleep(ms * 1000); + m_blockMutex.unlock(); + return NO_ERROR; + } + private: int32_t m_id; int32_t m_nextServerId; @@ -1655,6 +1906,7 @@ private: sp<IBinder> m_strongRef; sp<IBinder> m_callback; bool m_exitOnDestroy; + std::mutex m_blockMutex; }; int run_server(int index, int readypipefd, bool usePoll) @@ -1756,6 +2008,7 @@ int run_server(int index, int readypipefd, bool usePoll) } } } else { + ProcessState::self()->setThreadPoolMaxThreadCount(kKernelThreads); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp index 21cb70be17..278dd2bf81 100644 --- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp +++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp @@ -23,6 +23,7 @@ using namespace android; #ifdef __BIONIC__ TEST(MemoryHeapBase, ForceMemfdRespected) { auto mHeap = sp<MemoryHeapBase>::make(10, MemoryHeapBase::FORCE_MEMFD, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_FALSE(ashmem_valid(fd)); @@ -33,6 +34,7 @@ TEST(MemoryHeapBase, MemfdSealed) { auto mHeap = sp<MemoryHeapBase>::make(8192, MemoryHeapBase::FORCE_MEMFD, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL); @@ -43,6 +45,7 @@ TEST(MemoryHeapBase, MemfdUnsealed) { MemoryHeapBase::FORCE_MEMFD | MemoryHeapBase::MEMFD_ALLOW_SEALING, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0); @@ -53,6 +56,7 @@ TEST(MemoryHeapBase, MemfdSealedProtected) { MemoryHeapBase::FORCE_MEMFD | MemoryHeapBase::READ_ONLY, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE); @@ -64,6 +68,7 @@ TEST(MemoryHeapBase, MemfdUnsealedProtected) { MemoryHeapBase::READ_ONLY | MemoryHeapBase::MEMFD_ALLOW_SEALING, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE); @@ -74,6 +79,7 @@ TEST(MemoryHeapBase, HostMemfdExpected) { auto mHeap = sp<MemoryHeapBase>::make(8192, MemoryHeapBase::READ_ONLY, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); void* ptr = mHeap->getBase(); EXPECT_NE(ptr, MAP_FAILED); @@ -87,6 +93,7 @@ TEST(MemoryHeapBase,HostMemfdException) { MemoryHeapBase::READ_ONLY | MemoryHeapBase::MEMFD_ALLOW_SEALING, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); void* ptr = mHeap->getBase(); EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY); diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp new file mode 100644 index 0000000000..30172cce87 --- /dev/null +++ b/libs/binder/tests/binderRecordedTransactionTest.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/RecordedTransaction.h> +#include <gtest/gtest.h> +#include <utils/Errors.h> + +using android::Parcel; +using android::status_t; +using android::base::unique_fd; +using android::binder::debug::RecordedTransaction; + +TEST(BinderRecordedTransaction, RoundTripEncoding) { + android::String16 interfaceName("SampleInterface"); + Parcel d; + d.writeInt32(12); + d.writeInt64(2); + Parcel r; + r.writeInt32(99); + timespec ts = {1232456, 567890}; + + auto transaction = RecordedTransaction::fromDetails(interfaceName, 1, 42, ts, d, r, 0); + EXPECT_TRUE(transaction.has_value()); + + auto file = std::tmpfile(); + auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1)); + + status_t status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + + std::rewind(file); + + auto retrievedTransaction = RecordedTransaction::fromFile(fd); + + EXPECT_EQ(retrievedTransaction->getInterfaceName(), android::String8(interfaceName).c_str()); + EXPECT_EQ(retrievedTransaction->getCode(), 1); + EXPECT_EQ(retrievedTransaction->getFlags(), 42); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec); + EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12); + EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4); + EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0); + EXPECT_EQ(retrievedTransaction->getVersion(), 0); + + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12); + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2); + EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99); +} + +TEST(BinderRecordedTransaction, Checksum) { + android::String16 interfaceName("SampleInterface"); + Parcel d; + d.writeInt32(12); + d.writeInt64(2); + Parcel r; + r.writeInt32(99); + timespec ts = {1232456, 567890}; + auto transaction = RecordedTransaction::fromDetails(interfaceName, 1, 42, ts, d, r, 0); + + auto file = std::tmpfile(); + auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1)); + + status_t status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + + lseek(fd.get(), 9, SEEK_SET); + uint32_t badData = 0xffffffff; + write(fd.get(), &badData, sizeof(uint32_t)); + std::rewind(file); + + auto retrievedTransaction = RecordedTransaction::fromFile(fd); + + EXPECT_FALSE(retrievedTransaction.has_value()); +} + +TEST(BinderRecordedTransaction, PayloadsExceedPageBoundaries) { + // File contents are read with mmap. + // This test verifies that transactions are read from portions + // of files that cross page boundaries and don't start at a + // page boundary offset of the fd. + const size_t pageSize = sysconf(_SC_PAGE_SIZE); + const size_t largeDataSize = pageSize + 100; + std::vector<uint8_t> largePayload; + uint8_t filler = 0xaa; + largePayload.insert(largePayload.end(), largeDataSize, filler); + android::String16 interfaceName("SampleInterface"); + Parcel d; + d.writeInt32(12); + d.writeInt64(2); + d.writeByteVector(largePayload); + Parcel r; + r.writeInt32(99); + timespec ts = {1232456, 567890}; + auto transaction = RecordedTransaction::fromDetails(interfaceName, 1, 42, ts, d, r, 0); + + auto file = std::tmpfile(); + auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1)); + + // Write to file twice + status_t status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + + std::rewind(file); + + for (int i = 0; i < 2; i++) { + auto retrievedTransaction = RecordedTransaction::fromFile(fd); + + EXPECT_EQ(retrievedTransaction->getCode(), 1); + EXPECT_EQ(retrievedTransaction->getFlags(), 42); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec); + EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), d.dataSize()); + EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4); + EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0); + EXPECT_EQ(retrievedTransaction->getVersion(), 0); + + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12); + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2); + std::optional<std::vector<uint8_t>> payloadOut; + EXPECT_EQ(retrievedTransaction->getDataParcel().readByteVector(&payloadOut), android::OK); + EXPECT_EQ(payloadOut.value(), largePayload); + + EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99); + } +} diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index c2639e7c67..dcea880ef9 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -14,30 +14,8 @@ * limitations under the License. */ -#include <BinderRpcTestClientInfo.h> -#include <BinderRpcTestServerInfo.h> -#include <BnBinderRpcCallback.h> -#include <BnBinderRpcSession.h> -#include <BnBinderRpcTest.h> #include <aidl/IBinderRpcTest.h> -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android/binder_auto_utils.h> -#include <android/binder_libbinder.h> -#include <binder/Binder.h> -#include <binder/BpBinder.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <binder/ProcessState.h> -#include <binder/RpcServer.h> -#include <binder/RpcSession.h> -#include <binder/RpcTlsTestUtils.h> -#include <binder/RpcTlsUtils.h> -#include <binder/RpcTransport.h> -#include <binder/RpcTransportRaw.h> -#include <binder/RpcTransportTls.h> -#include <gtest/gtest.h> +#include <android-base/stringprintf.h> #include <chrono> #include <cstdlib> @@ -45,14 +23,18 @@ #include <thread> #include <type_traits> +#include <dlfcn.h> #include <poll.h> #include <sys/prctl.h> -#include <unistd.h> +#include <sys/socket.h> -#include "../FdTrigger.h" -#include "../RpcSocketAddress.h" // for testing preconnected clients -#include "../RpcState.h" // for debugging -#include "../vm_sockets.h" // for VMADDR_* +#ifdef __ANDROID_VENDOR__ +#include <binder/RpcTransportTipcAndroid.h> +#include <trusty/tipc.h> +#endif // __ANDROID_VENDOR__ + +#include "binderRpcTestCommon.h" +#include "binderRpcTestFixture.h" using namespace std::chrono_literals; using namespace std::placeholders; @@ -62,278 +44,48 @@ using testing::AssertionSuccess; namespace android { -static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT || - RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); -const char* kLocalInetAddress = "127.0.0.1"; - -enum class RpcSecurity { RAW, TLS }; +#ifdef BINDER_TEST_NO_SHARED_LIBS +constexpr bool kEnableSharedLibs = false; +#else +constexpr bool kEnableSharedLibs = true; +#endif -static inline std::vector<RpcSecurity> RpcSecurityValues() { - return {RpcSecurity::RAW, RpcSecurity::TLS}; -} +#ifdef __ANDROID_VENDOR__ +constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0"; +#endif -static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( - RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr, - std::unique_ptr<RpcAuth> auth = nullptr) { - switch (rpcSecurity) { - case RpcSecurity::RAW: - return RpcTransportCtxFactoryRaw::make(); - case RpcSecurity::TLS: { - if (verifier == nullptr) { - verifier = std::make_shared<RpcCertificateVerifierSimple>(); - } - if (auth == nullptr) { - auth = std::make_unique<RpcAuthSelfSigned>(); - } - return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth)); - } - default: - LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity); +static std::string WaitStatusToString(int wstatus) { + if (WIFEXITED(wstatus)) { + return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus)); } -} - -TEST(BinderRpcParcel, EntireParcelFormatted) { - Parcel p; - p.writeInt32(3); - - EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); -} - -class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> { -public: - static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { - return newFactory(info.param)->toCString(); + if (WIFSIGNALED(wstatus)) { + return base::StringPrintf("term signal %d", WTERMSIG(wstatus)); } -}; - -TEST_P(BinderRpcSimple, SetExternalServerTest) { - base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); - int sinkFd = sink.get(); - auto server = RpcServer::make(newFactory(GetParam())); - ASSERT_FALSE(server->hasServer()); - ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); - ASSERT_TRUE(server->hasServer()); - base::unique_fd retrieved = server->releaseServer(); - ASSERT_FALSE(server->hasServer()); - ASSERT_EQ(sinkFd, retrieved.get()); + return base::StringPrintf("unexpected state %d", wstatus); } -TEST(BinderRpc, CannotUseNextWireVersion) { - auto session = RpcSession::make(); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT)); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1)); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2)); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15)); +static void debugBacktrace(pid_t pid) { + std::cerr << "TAKING BACKTRACE FOR PID " << pid << std::endl; + system((std::string("debuggerd -b ") + std::to_string(pid)).c_str()); } -TEST(BinderRpc, CanUseExperimentalWireVersion) { - auto session = RpcSession::make(); - EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); -} - -using android::binder::Status; - -#define EXPECT_OK(status) \ - do { \ - Status stat = (status); \ - EXPECT_TRUE(stat.isOk()) << stat; \ - } while (false) - -class MyBinderRpcSession : public BnBinderRpcSession { -public: - static std::atomic<int32_t> gNum; - - MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } - Status getName(std::string* name) override { - *name = mName; - return Status::ok(); - } - ~MyBinderRpcSession() { gNum--; } - -private: - std::string mName; -}; -std::atomic<int32_t> MyBinderRpcSession::gNum; - -class MyBinderRpcCallback : public BnBinderRpcCallback { - Status sendCallback(const std::string& value) { - std::unique_lock _l(mMutex); - mValues.push_back(value); - _l.unlock(); - mCv.notify_one(); - return Status::ok(); - } - Status sendOnewayCallback(const std::string& value) { return sendCallback(value); } - -public: - std::mutex mMutex; - std::condition_variable mCv; - std::vector<std::string> mValues; -}; - -class MyBinderRpcTest : public BnBinderRpcTest { -public: - wp<RpcServer> server; - int port = 0; - - Status sendString(const std::string& str) override { - (void)str; - return Status::ok(); - } - Status doubleString(const std::string& str, std::string* strstr) override { - *strstr = str + str; - return Status::ok(); - } - Status getClientPort(int* out) override { - *out = port; - return Status::ok(); - } - Status countBinders(std::vector<int32_t>* out) override { - sp<RpcServer> spServer = server.promote(); - if (spServer == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - out->clear(); - for (auto session : spServer->listSessions()) { - size_t count = session->state()->countBinders(); - out->push_back(count); - } - return Status::ok(); - } - Status pingMe(const sp<IBinder>& binder, int32_t* out) override { - if (binder == nullptr) { - std::cout << "Received null binder!" << std::endl; - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - *out = binder->pingBinder(); - return Status::ok(); - } - Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { - *out = binder; - return Status::ok(); - } - static sp<IBinder> mHeldBinder; - Status holdBinder(const sp<IBinder>& binder) override { - mHeldBinder = binder; - return Status::ok(); - } - Status getHeldBinder(sp<IBinder>* held) override { - *held = mHeldBinder; - return Status::ok(); - } - Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { - if (count <= 0) return Status::ok(); - return binder->nestMe(this, count - 1); - } - Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { - static sp<IBinder> binder = new BBinder; - *out = binder; - return Status::ok(); - } - Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { - *out = new MyBinderRpcSession(name); - return Status::ok(); - } - Status getNumOpenSessions(int32_t* out) override { - *out = MyBinderRpcSession::gNum; - return Status::ok(); - } - - std::mutex blockMutex; - Status lock() override { - blockMutex.lock(); - return Status::ok(); - } - Status unlockInMsAsync(int32_t ms) override { - usleep(ms * 1000); - blockMutex.unlock(); - return Status::ok(); - } - Status lockUnlock() override { - std::lock_guard<std::mutex> _l(blockMutex); - return Status::ok(); - } - - Status sleepMs(int32_t ms) override { - usleep(ms * 1000); - return Status::ok(); - } - - Status sleepMsAsync(int32_t ms) override { - // In-process binder calls are asynchronous, but the call to this method - // is synchronous wrt its client. This in/out-process threading model - // diffentiation is a classic binder leaky abstraction (for better or - // worse) and is preserved here the way binder sockets plugs itself - // into BpBinder, as nothing is changed at the higher levels - // (IInterface) which result in this behavior. - return sleepMs(ms); - } - - Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, - const std::string& value) override { - if (callback == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - - if (delayed) { - std::thread([=]() { - ALOGE("Executing delayed callback: '%s'", value.c_str()); - Status status = doCallback(callback, oneway, false, value); - ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); - }).detach(); - return Status::ok(); - } - - if (oneway) { - return callback->sendOnewayCallback(value); - } - - return callback->sendCallback(value); - } - - Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, - const std::string& value) override { - return doCallback(callback, oneway, delayed, value); - } - - Status die(bool cleanup) override { - if (cleanup) { - exit(1); - } else { - _exit(1); - } - } - - Status scheduleShutdown() override { - sp<RpcServer> strongServer = server.promote(); - if (strongServer == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - std::thread([=] { - LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown"); - }).detach(); - return Status::ok(); - } - - Status useKernelBinderCallingId() override { - // this is WRONG! It does not make sense when using RPC binder, and - // because it is SO wrong, and so much code calls this, it should abort! - - (void)IPCThreadState::self()->getCallingPid(); - return Status::ok(); - } -}; -sp<IBinder> MyBinderRpcTest::mHeldBinder; - class Process { public: - Process(Process&&) = default; + Process(Process&& other) + : mCustomExitStatusCheck(std::move(other.mCustomExitStatusCheck)), + mReadEnd(std::move(other.mReadEnd)), + mWriteEnd(std::move(other.mWriteEnd)) { + // The default move constructor doesn't clear mPid after moving it, + // which we need to do because the destructor checks for mPid!=0 + mPid = other.mPid; + other.mPid = 0; + } Process(const std::function<void(android::base::borrowed_fd /* writeEnd */, android::base::borrowed_fd /* readEnd */)>& f) { android::base::unique_fd childWriteEnd; android::base::unique_fd childReadEnd; - CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno); - CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno); + CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno); + CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno); if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -345,13 +97,30 @@ public: } ~Process() { if (mPid != 0) { - waitpid(mPid, nullptr, 0); + int wstatus; + waitpid(mPid, &wstatus, 0); + if (mCustomExitStatusCheck) { + mCustomExitStatusCheck(wstatus); + } else { + EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + << "server process failed: " << WaitStatusToString(wstatus); + } } } android::base::borrowed_fd readEnd() { return mReadEnd; } android::base::borrowed_fd writeEnd() { return mWriteEnd; } + void setCustomExitStatusCheck(std::function<void(int wstatus)> f) { + mCustomExitStatusCheck = std::move(f); + } + + // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead. + void terminate() { kill(mPid, SIGTERM); } + + pid_t getPid() { return mPid; } + private: + std::function<void(int wstatus)> mCustomExitStatusCheck; pid_t mPid = 0; android::base::unique_fd mReadEnd; android::base::unique_fd mWriteEnd; @@ -366,25 +135,30 @@ static std::string allocateSocketAddress() { }; static unsigned int allocateVsockPort() { - static unsigned int vsockPort = 3456; + static unsigned int vsockPort = 34567; return vsockPort++; } -struct ProcessSession { - // reference to process hosting a socket server - Process host; +static base::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()))); + return fd; +} - struct SessionInfo { - sp<RpcSession> session; - sp<IBinder> root; - }; +// Destructors need to be defined, even if pure virtual +ProcessSession::~ProcessSession() {} - // client session objects associated with other process - // each one represents a separate session - std::vector<SessionInfo> sessions; +class LinuxProcessSession : public ProcessSession { +public: + // reference to process hosting a socket server + Process host; - ProcessSession(ProcessSession&&) = default; - ~ProcessSession() { + LinuxProcessSession(LinuxProcessSession&&) = default; + LinuxProcessSession(Process&& host) : host(std::move(host)) {} + ~LinuxProcessSession() override { for (auto& session : sessions) { session.root = nullptr; } @@ -398,72 +172,30 @@ struct ProcessSession { wp<RpcSession> weakSession = session; session = nullptr; - EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session"; - } - } -}; - -// Process session where the process hosts IBinderRpcTest, the server used -// for most testing here -struct BinderRpcTestProcessSession { - ProcessSession proc; - - // pre-fetched root object (for first session) - sp<IBinder> rootBinder; - - // pre-casted root object (for first session) - sp<IBinderRpcTest> rootIface; - - // whether session should be invalidated by end of run - bool expectAlreadyShutdown = false; - BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; - ~BinderRpcTestProcessSession() { - EXPECT_NE(nullptr, rootIface); - if (rootIface == nullptr) return; - - if (!expectAlreadyShutdown) { - std::vector<int32_t> remoteCounts; - // calling over any sessions counts across all sessions - EXPECT_OK(rootIface->countBinders(&remoteCounts)); - EXPECT_EQ(remoteCounts.size(), proc.sessions.size()); - for (auto remoteCount : remoteCounts) { - EXPECT_EQ(remoteCount, 1); - } - - // even though it is on another thread, shutdown races with - // the transaction reply being written - if (auto status = rootIface->scheduleShutdown(); !status.isOk()) { - EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + // b/244325464 - 'getStrongCount' is printing '1' on failure here, which indicates the + // the object should not actually be promotable. By looping, we distinguish a race here + // from a bug causing the object to not be promotable. + for (size_t i = 0; i < 3; i++) { + sp<RpcSession> strongSession = weakSession.promote(); + EXPECT_EQ(nullptr, strongSession) + << (debugBacktrace(host.getPid()), debugBacktrace(getpid()), + "Leaked sess: ") + << strongSession->getStrongCount() << " checked time " << i; + + if (strongSession != nullptr) { + sleep(1); + } } } + } - rootIface = nullptr; - rootBinder = nullptr; + void setCustomExitStatusCheck(std::function<void(int wstatus)> f) override { + host.setCustomExitStatusCheck(std::move(f)); } -}; -enum class SocketType { - PRECONNECTED, - UNIX, - VSOCK, - INET, + void terminate() override { host.terminate(); } }; -static inline std::string PrintToString(SocketType socketType) { - switch (socketType) { - case SocketType::PRECONNECTED: - return "preconnected_uds"; - case SocketType::UNIX: - return "unix_domain_socket"; - case SocketType::VSOCK: - return "vm_socket"; - case SocketType::INET: - return "inet_socket"; - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - return ""; - } -} static base::unique_fd connectTo(const RpcSocketAddress& addr) { base::unique_fd serverFd( @@ -480,128 +212,125 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { return serverFd; } -class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> { -public: - struct Options { - size_t numThreads = 1; - size_t numSessions = 1; - size_t numIncomingConnections = 0; - size_t numOutgoingConnections = SIZE_MAX; - }; - - static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [type, security] = info.param; - return PrintToString(type) + "_" + newFactory(security)->toCString(); +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 inline void writeString(android::base::borrowed_fd fd, std::string_view str) { - uint64_t length = str.length(); - CHECK(android::base::WriteFully(fd, &length, sizeof(length))); - CHECK(android::base::WriteFully(fd, str.data(), str.length())); + int zero = 0; + iovec iov{&zero, sizeof(zero)}; + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds; + fds.emplace_back(std::move(sockServer)); + + if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) { + int savedErrno = errno; + LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno); } + return std::move(sockClient); +} - static inline std::string readString(android::base::borrowed_fd fd) { - uint64_t length; - CHECK(android::base::ReadFully(fd, &length, sizeof(length))); - std::string ret(length, '\0'); - CHECK(android::base::ReadFully(fd, ret.data(), length)); - return ret; +std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& 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) { + ret += "_single_threaded"; + } + if (noKernel) { + ret += "_no_kernel"; } + return ret; +} - static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { - Parcel parcel; - CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); - writeString(fd, - std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); - } - - template <typename T> - static inline T readFromFd(android::base::borrowed_fd fd) { - std::string data = readString(fd); - Parcel parcel; - CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); - T object; - CHECK_EQ(OK, object.readFromParcel(&parcel)); - return object; - } - - // This creates a new process serving an interface on a certain number of - // threads. - ProcessSession createRpcTestSocketServerProcess( - const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) { - CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; - - SocketType socketType = std::get<0>(GetParam()); - RpcSecurity rpcSecurity = std::get<1>(GetParam()); - - unsigned int vsockPort = allocateVsockPort(); - std::string addr = allocateSocketAddress(); - - auto ret = ProcessSession{ - .host = Process([&](android::base::borrowed_fd writeEnd, - android::base::borrowed_fd readEnd) { - auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); - sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); - - server->setMaxThreads(options.numThreads); - - unsigned int outPort = 0; - - switch (socketType) { - case SocketType::PRECONNECTED: - [[fallthrough]]; - case SocketType::UNIX: - CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())) << addr; - break; - case SocketType::VSOCK: - CHECK_EQ(OK, server->setupVsockServer(vsockPort)); - break; - case SocketType::INET: { - CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); - CHECK_NE(0, outPort); - break; - } - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - } +// This creates a new process serving an interface on a certain number of +// threads. +std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( + const BinderRpcOptions& options) { + CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; - BinderRpcTestServerInfo serverInfo; - serverInfo.port = static_cast<int64_t>(outPort); - serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); - writeToFd(writeEnd, serverInfo); - auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); - - if (rpcSecurity == RpcSecurity::TLS) { - for (const auto& clientCert : clientInfo.certs) { - CHECK_EQ(OK, - certVerifier - ->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - clientCert.data)); - } - } + 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()); + + 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" : ""); - configure(server); + base::unique_fd bootstrapClientFd, socketFd; - server->join(); + auto addr = allocateSocketAddress(); + // Initializes the socket before the fork/exec. + if (socketType == SocketType::UNIX_RAW) { + socketFd = initUnixSocket(addr); + } 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); + } + } - // Another thread calls shutdown. Wait for it to complete. - (void)server->shutdown(); - }), - }; + auto ret = std::make_unique<LinuxProcessSession>( + Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { + if (socketType == SocketType::TIPC) { + // Trusty has a single persistent service + return; + } - std::vector<sp<RpcSession>> sessions; - auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); - for (size_t i = 0; i < options.numSessions; i++) { - sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier))); + auto writeFd = std::to_string(writeEnd.get()); + auto readFd = std::to_string(readEnd.get()); + execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(), + NULL); + })); + + BinderRpcTestServerConfig serverConfig; + serverConfig.numThreads = options.numThreads; + serverConfig.socketType = static_cast<int32_t>(socketType); + serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); + serverConfig.serverVersion = serverVersion; + serverConfig.vsockPort = allocateVsockPort(); + serverConfig.addr = addr; + serverConfig.socketFd = socketFd.get(); + for (auto mode : options.serverSupportedFileDescriptorTransportModes) { + serverConfig.serverSupportedFileDescriptorTransportModes.push_back( + static_cast<int32_t>(mode)); + } + if (socketType != SocketType::TIPC) { + writeToFd(ret->host.writeEnd(), serverConfig); + } + + std::vector<sp<RpcSession>> sessions; + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + for (size_t i = 0; i < options.numSessions; i++) { + std::unique_ptr<RpcTransportCtxFactory> factory; + if (socketType == SocketType::TIPC) { +#ifdef __ANDROID_VENDOR__ + factory = RpcTransportCtxFactoryTipcAndroid::make(); +#else + LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor"); +#endif + } else { + factory = newFactory(rpcSecurity, certVerifier); } + sessions.emplace_back(RpcSession::make(std::move(factory))); + } - auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd()); + BinderRpcTestServerInfo serverInfo; + if (socketType != SocketType::TIPC) { + serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd()); BinderRpcTestClientInfo clientInfo; for (const auto& session : sessions) { auto& parcelableCert = clientInfo.certs.emplace_back(); parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM); } - writeToFd(ret.host.writeEnd(), clientInfo); + writeToFd(ret->host.writeEnd(), clientInfo); CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max()); if (socketType == SocketType::INET) { @@ -614,423 +343,67 @@ public: certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert)); } - - status_t status; - - for (const auto& session : sessions) { - session->setMaxIncomingThreads(options.numIncomingConnections); - session->setMaxOutgoingThreads(options.numOutgoingConnections); - - switch (socketType) { - case SocketType::PRECONNECTED: - status = session->setupPreconnectedClient({}, [=]() { - return connectTo(UnixSocketAddress(addr.c_str())); - }); - break; - case SocketType::UNIX: - status = session->setupUnixDomainClient(addr.c_str()); - break; - case SocketType::VSOCK: - status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort); - break; - case SocketType::INET: - status = session->setupInetClient("127.0.0.1", serverInfo.port); - break; - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - } - CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); - ret.sessions.push_back({session, session->getRootObject()}); - } - return ret; - } - - BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) { - BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcess( - options, - [&](const sp<RpcServer>& server) { - server->setPerSessionRootObject([&](const sockaddr* addr, - socklen_t len) { - sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make(); - switch (addr->sa_family) { - case AF_UNIX: - // nothing to save - break; - case AF_VSOCK: - CHECK_EQ(len, sizeof(sockaddr_vm)); - service->port = reinterpret_cast<const sockaddr_vm*>(addr) - ->svm_port; - break; - case AF_INET: - CHECK_EQ(len, sizeof(sockaddr_in)); - service->port = - ntohs(reinterpret_cast<const sockaddr_in*>(addr) - ->sin_port); - break; - case AF_INET6: - CHECK_EQ(len, sizeof(sockaddr_in)); - service->port = - ntohs(reinterpret_cast<const sockaddr_in6*>(addr) - ->sin6_port); - break; - default: - LOG_ALWAYS_FATAL("Unrecognized address family %d", - addr->sa_family); - } - service->server = server; - return service; - }); - }), - }; - - ret.rootBinder = ret.proc.sessions.at(0).root; - ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); - - return ret; - } - - void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, - size_t sleepMs = 500); -}; - -TEST_P(BinderRpc, Ping) { - auto proc = createRpcTestSocketServerProcess({}); - ASSERT_NE(proc.rootBinder, nullptr); - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); -} - -TEST_P(BinderRpc, GetInterfaceDescriptor) { - auto proc = createRpcTestSocketServerProcess({}); - ASSERT_NE(proc.rootBinder, nullptr); - EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor()); -} - -TEST_P(BinderRpc, MultipleSessions) { - auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5}); - for (auto session : proc.proc.sessions) { - ASSERT_NE(nullptr, session.root); - EXPECT_EQ(OK, session.root->pingBinder()); - } -} - -TEST_P(BinderRpc, SeparateRootObject) { - SocketType type = std::get<0>(GetParam()); - if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) { - // we can't get port numbers for unix sockets - return; } - auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); - - int port1 = 0; - EXPECT_OK(proc.rootIface->getClientPort(&port1)); - - sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root); - int port2; - EXPECT_OK(rootIface2->getClientPort(&port2)); - - // we should have a different IBinderRpcTest object created for each - // session, because we use setPerSessionRootObject - EXPECT_NE(port1, port2); -} - -TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { - auto proc = createRpcTestSocketServerProcess({}); - Parcel data; - Parcel reply; - EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0)); -} - -TEST_P(BinderRpc, AppendSeparateFormats) { - auto proc1 = createRpcTestSocketServerProcess({}); - auto proc2 = createRpcTestSocketServerProcess({}); - - Parcel pRaw; - - Parcel p1; - p1.markForBinder(proc1.rootBinder); - p1.writeInt32(3); - - EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize())); - EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); - - Parcel p2; - p2.markForBinder(proc2.rootBinder); - p2.writeInt32(7); - - EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize())); - EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize())); -} - -TEST_P(BinderRpc, UnknownTransaction) { - auto proc = createRpcTestSocketServerProcess({}); - Parcel data; - data.markForBinder(proc.rootBinder); - Parcel reply; - EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0)); -} - -TEST_P(BinderRpc, SendSomethingOneway) { - auto proc = createRpcTestSocketServerProcess({}); - EXPECT_OK(proc.rootIface->sendString("asdf")); -} - -TEST_P(BinderRpc, SendAndGetResultBack) { - auto proc = createRpcTestSocketServerProcess({}); - std::string doubled; - EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled)); - EXPECT_EQ("cool cool ", doubled); -} - -TEST_P(BinderRpc, SendAndGetResultBackBig) { - auto proc = createRpcTestSocketServerProcess({}); - std::string single = std::string(1024, 'a'); - std::string doubled; - EXPECT_OK(proc.rootIface->doubleString(single, &doubled)); - EXPECT_EQ(single + single, doubled); -} - -TEST_P(BinderRpc, CallMeBack) { - auto proc = createRpcTestSocketServerProcess({}); - - int32_t pingResult; - EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult)); - EXPECT_EQ(OK, pingResult); - - EXPECT_EQ(0, MyBinderRpcSession::gNum); -} - -TEST_P(BinderRpc, RepeatBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> inBinder = new MyBinderRpcSession("foo"); - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); - EXPECT_EQ(inBinder, outBinder); - - wp<IBinder> weak = inBinder; - inBinder = nullptr; - outBinder = nullptr; - - // Force reading a reply, to process any pending dec refs from the other - // process (the other process will process dec refs there before processing - // the ping here). - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - - EXPECT_EQ(nullptr, weak.promote()); - - EXPECT_EQ(0, MyBinderRpcSession::gNum); -} - -TEST_P(BinderRpc, RepeatTheirBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinderRpcSession> session; - EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); - - sp<IBinder> inBinder = IInterface::asBinder(session); - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); - EXPECT_EQ(inBinder, outBinder); - - wp<IBinder> weak = inBinder; - session = nullptr; - inBinder = nullptr; - outBinder = nullptr; - - // Force reading a reply, to process any pending dec refs from the other - // process (the other process will process dec refs there before processing - // the ping here). - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - - EXPECT_EQ(nullptr, weak.promote()); -} - -TEST_P(BinderRpc, RepeatBinderNull) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder)); - EXPECT_EQ(nullptr, outBinder); -} - -TEST_P(BinderRpc, HoldBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - IBinder* ptr = nullptr; - { - sp<IBinder> binder = new BBinder(); - ptr = binder.get(); - EXPECT_OK(proc.rootIface->holdBinder(binder)); + status_t status; + + for (const auto& session : sessions) { + CHECK(session->setProtocolVersion(clientVersion)); + session->setMaxIncomingThreads(options.numIncomingConnections); + session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); + + switch (socketType) { + case SocketType::PRECONNECTED: + status = session->setupPreconnectedClient({}, [=]() { + return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); + }); + break; + case SocketType::UNIX_RAW: + case SocketType::UNIX: + status = session->setupUnixDomainClient(serverConfig.addr.c_str()); + break; + case SocketType::UNIX_BOOTSTRAP: + status = session->setupUnixDomainSocketBootstrapClient( + base::unique_fd(dup(bootstrapClientFd.get()))); + break; + case SocketType::VSOCK: + status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort); + break; + case SocketType::INET: + status = session->setupInetClient("127.0.0.1", serverInfo.port); + break; + case SocketType::TIPC: + status = session->setupPreconnectedClient({}, [=]() { +#ifdef __ANDROID_VENDOR__ + auto port = trustyIpcPort(serverVersion); + int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str()); + return tipcFd >= 0 ? android::base::unique_fd(tipcFd) + : android::base::unique_fd(); +#else + LOG_ALWAYS_FATAL("Tried to connect to Trusty outside of vendor"); + return android::base::unique_fd(); +#endif + }); + break; + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + } + if (options.allowConnectFailure && status != OK) { + ret->sessions.clear(); + break; + } + CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); + ret->sessions.push_back({session, session->getRootObject()}); } - - sp<IBinder> held; - EXPECT_OK(proc.rootIface->getHeldBinder(&held)); - - EXPECT_EQ(held.get(), ptr); - - // stop holding binder, because we test to make sure references are cleaned - // up - EXPECT_OK(proc.rootIface->holdBinder(nullptr)); - // and flush ref counts - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); -} - -// START TESTS FOR LIMITATIONS OF SOCKET BINDER -// These are behavioral differences form regular binder, where certain usecases -// aren't supported. - -TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { - auto proc1 = createRpcTestSocketServerProcess({}); - auto proc2 = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_EQ(INVALID_OPERATION, - proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); -} - -TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { - auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2}); - - sp<IBinder> outBinder; - EXPECT_EQ(INVALID_OPERATION, - proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder) - .transactionError()); -} - -TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); - sp<IBinder> outBinder; - EXPECT_EQ(INVALID_OPERATION, - proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError()); -} - -TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - // for historical reasons, IServiceManager interface only returns the - // exception code - EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED, - defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder)); -} - -// END TESTS FOR LIMITATIONS OF SOCKET BINDER - -TEST_P(BinderRpc, RepeatRootObject) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder)); - EXPECT_EQ(proc.rootBinder, outBinder); -} - -TEST_P(BinderRpc, NestedTransactions) { - auto proc = createRpcTestSocketServerProcess({}); - - auto nastyNester = sp<MyBinderRpcTest>::make(); - EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); - - wp<IBinder> weak = nastyNester; - nastyNester = nullptr; - EXPECT_EQ(nullptr, weak.promote()); -} - -TEST_P(BinderRpc, SameBinderEquality) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> a; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); - - sp<IBinder> b; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); - - EXPECT_EQ(a, b); -} - -TEST_P(BinderRpc, SameBinderEqualityWeak) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> a; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); - wp<IBinder> weak = a; - a = nullptr; - - sp<IBinder> b; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); - - // this is the wrong behavior, since BpBinder - // doesn't implement onIncStrongAttempted - // but make sure there is no crash - EXPECT_EQ(nullptr, weak.promote()); - - GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder."; - - // In order to fix this: - // - need to have incStrongAttempted reflected across IPC boundary (wait for - // response to promote - round trip...) - // - sendOnLastWeakRef, to delete entries out of RpcState table - EXPECT_EQ(b, weak.promote()); -} - -#define expectSessions(expected, iface) \ - do { \ - int session; \ - EXPECT_OK((iface)->getNumOpenSessions(&session)); \ - EXPECT_EQ(expected, session); \ - } while (false) - -TEST_P(BinderRpc, SingleSession) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinderRpcSession> session; - EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); - std::string out; - EXPECT_OK(session->getName(&out)); - EXPECT_EQ("aoeu", out); - - expectSessions(1, proc.rootIface); - session = nullptr; - expectSessions(0, proc.rootIface); + return ret; } -TEST_P(BinderRpc, ManySessions) { - auto proc = createRpcTestSocketServerProcess({}); - - std::vector<sp<IBinderRpcSession>> sessions; - - for (size_t i = 0; i < 15; i++) { - expectSessions(i, proc.rootIface); - sp<IBinderRpcSession> session; - EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session)); - sessions.push_back(session); - } - expectSessions(sessions.size(), proc.rootIface); - for (size_t i = 0; i < sessions.size(); i++) { - std::string out; - EXPECT_OK(sessions.at(i)->getName(&out)); - EXPECT_EQ(std::to_string(i), out); - } - expectSessions(sessions.size(), proc.rootIface); - - while (!sessions.empty()) { - sessions.pop_back(); - expectSessions(sessions.size(), proc.rootIface); +TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; } - expectSessions(0, proc.rootIface); -} -size_t epochMillis() { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::seconds; - using std::chrono::system_clock; - return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); -} - -TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { constexpr size_t kNumThreads = 10; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); @@ -1048,7 +421,7 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { // other calls still work EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - constexpr size_t blockTimeMs = 500; + constexpr size_t blockTimeMs = 100; size_t epochMsBefore = epochMillis(); // after this, we should never see a response within this time EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs)); @@ -1062,8 +435,8 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { for (auto& t : ts) t.join(); } -void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, - size_t sleepMs) { +static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, + size_t sleepMs = 500) { size_t epochMsBefore = epochMillis(); std::vector<std::thread> ts; @@ -1082,6 +455,10 @@ void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t num } TEST_P(BinderRpc, ThreadPoolOverSaturated) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumThreads = 10; constexpr size_t kNumCalls = kNumThreads + 3; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); @@ -1089,6 +466,10 @@ TEST_P(BinderRpc, ThreadPoolOverSaturated) { } TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumThreads = 20; constexpr size_t kNumOutgoingConnections = 10; constexpr size_t kNumCalls = kNumOutgoingConnections + 3; @@ -1098,6 +479,10 @@ TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { } TEST_P(BinderRpc, ThreadingStressTest) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumClientThreads = 10; constexpr size_t kNumServerThreads = 10; constexpr size_t kNumCalls = 100; @@ -1127,6 +512,10 @@ static void saturateThreadPool(size_t threadCount, const sp<IBinderRpcTest>& ifa } TEST_P(BinderRpc, OnewayStressTest) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumClientThreads = 10; constexpr size_t kNumServerThreads = 10; constexpr size_t kNumCalls = 1000; @@ -1147,21 +536,54 @@ TEST_P(BinderRpc, OnewayStressTest) { saturateThreadPool(kNumServerThreads, proc.rootIface); } -TEST_P(BinderRpc, OnewayCallDoesNotWait) { - constexpr size_t kReallyLongTimeMs = 100; - constexpr size_t kSleepMs = kReallyLongTimeMs * 5; +TEST_P(BinderRpc, OnewayCallQueueingWithFds) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested elsewhere)"; + } + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } - auto proc = createRpcTestSocketServerProcess({}); + constexpr size_t kNumServerThreads = 3; - size_t epochMsBefore = epochMillis(); + // This test forces a oneway transaction to be queued by issuing two + // `blockingSendFdOneway` calls, then drains the queue by issuing two + // `blockingRecvFd` calls. + // + // For more details about the queuing semantics see + // https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY - EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs)); + auto proc = createRpcTestSocketServerProcess({ + .numThreads = kNumServerThreads, + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); - size_t epochMsAfter = epochMillis(); - EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs); + EXPECT_OK(proc.rootIface->blockingSendFdOneway( + android::os::ParcelFileDescriptor(mockFileDescriptor("a")))); + EXPECT_OK(proc.rootIface->blockingSendFdOneway( + android::os::ParcelFileDescriptor(mockFileDescriptor("b")))); + + android::os::ParcelFileDescriptor fdA; + EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA)); + std::string result; + CHECK(android::base::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)); + EXPECT_EQ(result, "b"); + + saturateThreadPool(kNumServerThreads, proc.rootIface); } TEST_P(BinderRpc, OnewayCallQueueing) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumSleeps = 10; constexpr size_t kNumExtraServerThreads = 4; constexpr size_t kSleepMs = 50; @@ -1185,12 +607,16 @@ TEST_P(BinderRpc, OnewayCallQueueing) { size_t epochMsAfter = epochMillis(); - EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); + EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface); } TEST_P(BinderRpc, OnewayCallExhaustion) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumClients = 2; constexpr size_t kTooLongMs = 1000; @@ -1199,7 +625,7 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { // Build up oneway calls on the second session to make sure it terminates // and shuts down. The first session should be unaffected (proc destructor // checks the first session). - auto iface = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root); + auto iface = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root); std::vector<std::thread> threads; for (size_t i = 0; i < kNumClients; i++) { @@ -1227,64 +653,134 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { // any pending commands). We need to erase this session from the record // here, so that the destructor for our session won't check that this // session is valid, but we still want it to test the other session. - proc.proc.sessions.erase(proc.proc.sessions.begin() + 1); + proc.proc->sessions.erase(proc.proc->sessions.begin() + 1); } -TEST_P(BinderRpc, Callbacks) { - const static std::string kTestString = "good afternoon!"; - - for (bool callIsOneway : {true, false}) { - for (bool callbackIsOneway : {true, false}) { - for (bool delayed : {true, false}) { - auto proc = createRpcTestSocketServerProcess( - {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); - auto cb = sp<MyBinderRpcCallback>::make(); - - if (callIsOneway) { - EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed, - kTestString)); - } else { - EXPECT_OK( - proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString)); - } +TEST_P(BinderRpc, SingleDeathRecipient) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + dead = true; + mCv.notify_one(); + } + std::mutex mMtx; + std::condition_variable mCv; + bool dead = false; + }; - using std::literals::chrono_literals::operator""s; - std::unique_lock<std::mutex> _l(cb->mMutex); - cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); - - EXPECT_EQ(cb->mValues.size(), 1) - << "callIsOneway: " << callIsOneway - << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; - if (cb->mValues.empty()) continue; - EXPECT_EQ(cb->mValues.at(0), kTestString) - << "callIsOneway: " << callIsOneway - << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; - - // since we are severing the connection, we need to go ahead and - // tell the server to shutdown and exit so that waitpid won't hang - if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { - EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; - } + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); - // since this session has an incoming connection w/ a threadpool, we - // need to manually shut it down - EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + auto dr = sp<MyDeathRec>::make(); + ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); - proc.expectAlreadyShutdown = true; - } + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + std::unique_lock<std::mutex> lock(dr->mMtx); + ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; })); + + // need to wait for the session to shutdown so we don't "Leak session" + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, SingleDeathRecipientOnShutdown) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + dead = true; + mCv.notify_one(); } + std::mutex mMtx; + std::condition_variable mCv; + bool dead = false; + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + EXPECT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + + // Explicitly calling shutDownAndWait will cause the death recipients + // to be called. + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); + + std::unique_lock<std::mutex> lock(dr->mMtx); + if (!dr->dead) { + EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 100ms)); } + EXPECT_TRUE(dr->dead) << "Failed to receive the death notification."; + + proc.proc->terminate(); + proc.proc->setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; } -TEST_P(BinderRpc, OnewayCallbackWithNoThread) { - auto proc = createRpcTestSocketServerProcess({}); - auto cb = sp<MyBinderRpcCallback>::make(); +TEST_P(BinderRpc, DeathRecipientFailsWithoutIncoming) { + if (socketType() == SocketType::TIPC) { + // This should work, but Trusty takes too long to restart the service + GTEST_SKIP() << "Service death test not supported on Trusty"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override {} + }; + + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 0}); + + auto dr = sp<MyDeathRec>::make(); + EXPECT_EQ(INVALID_OPERATION, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); +} + +TEST_P(BinderRpc, UnlinkDeathRecipient) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + GTEST_FAIL() << "This should not be called after unlinkToDeath"; + } + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + ASSERT_EQ(OK, proc.rootBinder->unlinkToDeath(dr, (void*)1, 0, nullptr)); - Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything"); - EXPECT_EQ(WOULD_BLOCK, status.transactionError()); + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + // need to wait for the session to shutdown so we don't "Leak session" + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; } TEST_P(BinderRpc, Die) { + if (socketType() == SocketType::TIPC) { + // This should work, but Trusty takes too long to restart the service + GTEST_SKIP() << "Service death test not supported on Trusty"; + } + for (bool doDeathCleanup : {true, false}) { auto proc = createRpcTestSocketServerProcess({}); @@ -1299,33 +795,237 @@ TEST_P(BinderRpc, Die) { EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError()) << "Do death cleanup: " << doDeathCleanup; + proc.proc->setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } } TEST_P(BinderRpc, UseKernelBinderCallingId) { - bool okToFork = ProcessState::selfOrNull() == nullptr; + // This test only works if the current process shared the internal state of + // ProcessState with the service across the call to fork(). Both the static + // libraries and libbinder.so have their own separate copies of all the + // globals, so the test only works when the test client and service both use + // libbinder.so (when using static libraries, even a client and service + // using the same kind of static library should have separate copies of the + // variables). + if (!kEnableSharedLibs || serverSingleThreaded() || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } auto proc = createRpcTestSocketServerProcess({}); - // If this process has used ProcessState already, then the forked process - // cannot use it at all. If this process hasn't used it (depending on the - // order tests are run), then the forked process can use it, and we'll only - // catch the invalid usage the second time. Such is the burden of global - // state! - if (okToFork) { - // we can't allocate IPCThreadState so actually the first time should - // succeed :( - EXPECT_OK(proc.rootIface->useKernelBinderCallingId()); - } + // we can't allocate IPCThreadState so actually the first time should + // succeed :( + EXPECT_OK(proc.rootIface->useKernelBinderCallingId()); // second time! we catch the error :) EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError()); + proc.proc->setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, FileDescriptorTransportRejectNone) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + .allowConnectFailure = true, + }); + EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed"; + proc.proc->terminate(); + proc.proc->setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::NONE}, + .allowConnectFailure = true, + }); + EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed"; + proc.proc->terminate(); + proc.proc->setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } +TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::NONE, + RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->echoAsFile("hello", &out); + EXPECT_EQ(status.transactionError(), FDS_NOT_ALLOWED) << status; +} + +TEST_P(BinderRpc, ReceiveFile) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->echoAsFile("hello", &out); + if (!supportsFdTransport()) { + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; + return; + } + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, "hello"); +} + +TEST_P(BinderRpc, SendFiles) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("123"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("b"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("cd"))); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + if (!supportsFdTransport()) { + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; + return; + } + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, "123abcd"); +} + +TEST_P(BinderRpc, SendMaxFiles) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + for (int i = 0; i < 253; i++) { + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + } + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, std::string(253, 'a')); +} + +TEST_P(BinderRpc, SendTooManyFiles) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + for (int i = 0; i < 254; i++) { + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + } + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; +} + +TEST_P(BinderRpc, AppendInvalidFd) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + int badFd = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 0); + ASSERT_NE(badFd, -1); + + // Close the file descriptor so it becomes invalid for dup + close(badFd); + + Parcel p1; + p1.markForBinder(proc.rootBinder); + p1.writeInt32(3); + EXPECT_EQ(OK, p1.writeFileDescriptor(badFd, false)); + + Parcel pRaw; + pRaw.markForBinder(proc.rootBinder); + EXPECT_EQ(OK, pRaw.appendFrom(&p1, 0, p1.dataSize())); + + pRaw.setDataPosition(0); + EXPECT_EQ(3, pRaw.readInt32()); + ASSERT_EQ(-1, pRaw.readFileDescriptor()); +} + +#ifndef __ANDROID_VENDOR__ // No AIBinder_fromPlatformBinder on vendor TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { + if constexpr (!kEnableSharedLibs) { + GTEST_SKIP() << "Test disabled because Binder was built as a static library"; + } + auto proc = createRpcTestSocketServerProcess({}); ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder)); @@ -1335,6 +1035,10 @@ TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { } TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) { + if constexpr (!kEnableSharedLibs) { + GTEST_SKIP() << "Test disabled because Binder was built as a static library"; + } + auto proc = createRpcTestSocketServerProcess({}); ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder)); @@ -1348,6 +1052,7 @@ TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) { ASSERT_TRUE(status.isOk()) << status.getDescription(); ASSERT_EQ("aoeuaoeu", out); } +#endif // __ANDROID_VENDOR__ ssize_t countFds() { DIR* dir = opendir("/proc/self/fd/"); @@ -1360,6 +1065,13 @@ ssize_t countFds() { } TEST_P(BinderRpc, Fds) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)"; + } + ssize_t beforeFds = countFds(); ASSERT_GE(beforeFds, 0); { @@ -1369,37 +1081,98 @@ TEST_P(BinderRpc, Fds) { ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?"); } -TEST_P(BinderRpc, AidlDelegatorTest) { - auto proc = createRpcTestSocketServerProcess({}); - auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface); - ASSERT_NE(nullptr, myDelegator); - - std::string doubled; - EXPECT_OK(myDelegator->doubleString("cool ", &doubled)); - EXPECT_EQ("cool cool ", doubled); -} - static bool testSupportVsockLoopback() { // We don't need to enable TLS to know if vsock is supported. unsigned int vsockPort = allocateVsockPort(); - sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make()); - if (status_t status = server->setupVsockServer(vsockPort); status != OK) { - if (status == -EAFNOSUPPORT) { - return false; + + android::base::unique_fd serverFd( + TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno)); + + sockaddr_vm serverAddr{ + .svm_family = AF_VSOCK, + .svm_port = vsockPort, + .svm_cid = VMADDR_CID_ANY, + }; + int ret = TEMP_FAILURE_RETRY( + bind(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr))); + LOG_ALWAYS_FATAL_IF(0 != ret, "Could not bind socket to port %u: %s", vsockPort, + strerror(errno)); + + ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/)); + LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", vsockPort, + strerror(errno)); + + // Try to connect to the server using the VMADDR_CID_LOCAL cid + // to see if the kernel supports it. It's safe to use a blocking + // connect because vsock sockets have a 2 second connection timeout, + // and they return ETIMEDOUT after that. + android::base::unique_fd connectFd( + TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort, + strerror(errno)); + + bool success = false; + sockaddr_vm connectAddr{ + .svm_family = AF_VSOCK, + .svm_port = vsockPort, + .svm_cid = VMADDR_CID_LOCAL, + }; + ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr), + sizeof(connectAddr))); + if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) { + android::base::unique_fd acceptFd; + while (true) { + pollfd pfd[]{ + {.fd = serverFd.get(), .events = POLLIN, .revents = 0}, + {.fd = connectFd.get(), .events = POLLOUT, .revents = 0}, + }; + ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); + LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno)); + + if (pfd[0].revents & POLLIN) { + sockaddr_vm acceptAddr; + socklen_t acceptAddrLen = sizeof(acceptAddr); + ret = TEMP_FAILURE_RETRY(accept4(serverFd.get(), + reinterpret_cast<sockaddr*>(&acceptAddr), + &acceptAddrLen, SOCK_CLOEXEC)); + LOG_ALWAYS_FATAL_IF(ret < 0, "Could not accept4 socket: %s", strerror(errno)); + LOG_ALWAYS_FATAL_IF(acceptAddrLen != static_cast<socklen_t>(sizeof(acceptAddr)), + "Truncated address"); + + // Store the fd in acceptFd so we keep the connection alive + // while polling connectFd + acceptFd.reset(ret); + } + + if (pfd[1].revents & POLLOUT) { + // Connect either succeeded or timed out + int connectErrno; + socklen_t connectErrnoLen = sizeof(connectErrno); + int ret = getsockopt(connectFd.get(), SOL_SOCKET, SO_ERROR, &connectErrno, + &connectErrnoLen); + LOG_ALWAYS_FATAL_IF(ret == -1, + "Could not getsockopt() after connect() " + "on non-blocking socket: %s.", + strerror(errno)); + + // We're done, this is all we wanted + success = connectErrno == 0; + break; + } } - LOG_ALWAYS_FATAL("Could not setup vsock server: %s", statusToString(status).c_str()); + } else { + success = ret == 0; } - server->start(); - sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); - status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort); - while (!server->shutdown()) usleep(10000); - ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str()); - return status == OK; + ALOGE("Detected vsock loopback supported: %s", success ? "yes" : "no"); + + return success; } static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { - std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET}; + std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET, + SocketType::UNIX_RAW}; if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED); @@ -1412,9 +1185,36 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { return ret; } +static std::vector<SocketType> testTipcSocketTypes() { +#ifdef __ANDROID_VENDOR__ + auto port = trustyIpcPort(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str()); + if (tipcFd >= 0) { + close(tipcFd); + return {SocketType::TIPC}; + } +#endif // __ANDROID_VENDOR__ + + // TIPC is not supported on this device, most likely + // because /dev/trusty-ipc-dev0 is missing + return {}; +} + INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::Combine(::testing::ValuesIn(testSocketTypes()), - ::testing::ValuesIn(RpcSecurityValues())), + ::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions()), + ::testing::ValuesIn(testVersions()), + ::testing::Values(false, true), + ::testing::Values(false, true)), + BinderRpc::PrintParamInfo); + +INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, + ::testing::Combine(::testing::ValuesIn(testTipcSocketTypes()), + ::testing::Values(RpcSecurity::RAW), + ::testing::ValuesIn(testVersions()), + ::testing::ValuesIn(testVersions()), + ::testing::Values(true), ::testing::Values(true)), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject @@ -1468,37 +1268,17 @@ private: bool mValue = false; }; -TEST_P(BinderRpcSimple, Shutdown) { - auto addr = allocateSocketAddress(); - auto server = RpcServer::make(newFactory(GetParam())); - ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); - auto joinEnds = std::make_shared<OneOffSignal>(); - - // If things are broken and the thread never stops, don't block other tests. Because the thread - // may run after the test finishes, it must not access the stack memory of the test. Hence, - // shared pointers are passed. - std::thread([server, joinEnds] { - server->join(); - joinEnds->notify(); - }).detach(); - - bool shutdown = false; - for (int i = 0; i < 10 && !shutdown; i++) { - usleep(300 * 1000); // 300ms; total 3s - if (server->shutdown()) shutdown = true; - } - ASSERT_TRUE(shutdown) << "server->shutdown() never returns true"; - - ASSERT_TRUE(joinEnds->wait(2s)) - << "After server->shutdown() returns true, join() did not stop after 2s"; -} - TEST(BinderRpc, Java) { #if !defined(__ANDROID__) GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on" "createRpcDelegateServiceManager() with a device attached, such test belongs " "to binderHostDeviceTest. Hence, just disable this test on host."; #endif // !__ANDROID__ + if constexpr (!kEnableKernelIpc) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + sp<IServiceManager> sm = defaultServiceManager(); ASSERT_NE(nullptr, sm); // Any Java service with non-empty getInterfaceDescriptor() would do. @@ -1540,25 +1320,84 @@ TEST(BinderRpc, Java) { ASSERT_EQ(OK, rpcBinder->pingBinder()); } -INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), - BinderRpcSimple::PrintTestParam); +class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> { +public: + static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { + return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" + + std::to_string(std::get<1>(info.param)); + } +}; + +TEST_P(BinderRpcServerOnly, SetExternalServerTest) { + base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); + int sinkFd = sink.get(); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_FALSE(server->hasServer()); + ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); + ASSERT_TRUE(server->hasServer()); + base::unique_fd retrieved = server->releaseServer(); + ASSERT_FALSE(server->hasServer()); + ASSERT_EQ(sinkFd, retrieved.get()); +} + +TEST_P(BinderRpcServerOnly, Shutdown) { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + + auto addr = allocateSocketAddress(); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); + auto joinEnds = std::make_shared<OneOffSignal>(); + + // If things are broken and the thread never stops, don't block other tests. Because the thread + // may run after the test finishes, it must not access the stack memory of the test. Hence, + // shared pointers are passed. + std::thread([server, joinEnds] { + server->join(); + joinEnds->notify(); + }).detach(); + + bool shutdown = false; + for (int i = 0; i < 10 && !shutdown; i++) { + usleep(30 * 1000); // 30ms; total 300ms + if (server->shutdown()) shutdown = true; + } + ASSERT_TRUE(shutdown) << "server->shutdown() never returns true"; + + ASSERT_TRUE(joinEnds->wait(2s)) + << "After server->shutdown() returns true, join() did not stop after 2s"; +} + +INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly, + ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions())), + BinderRpcServerOnly::PrintTestParam); class RpcTransportTestUtils { public: - using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>; + // Only parameterized only server version because `RpcSession` is bypassed + // in the client half of the tests. + using Param = + std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>; using ConnectToServer = std::function<base::unique_fd()>; // A server that handles client socket connections. class Server { public: + using AcceptConnection = std::function<base::unique_fd(Server*)>; + explicit Server() {} Server(Server&&) = default; ~Server() { shutdownAndWait(); } [[nodiscard]] AssertionResult setUp( const Param& param, std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) { - auto [socketType, rpcSecurity, certificateFormat] = param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newFactory(rpcSecurity)); + rpcServer->setProtocolVersion(serverVersion); switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; @@ -1574,9 +1413,35 @@ public: return connectTo(UnixSocketAddress(addr.c_str())); }; } break; + case SocketType::UNIX_BOOTSTRAP: { + base::unique_fd bootstrapFdClient, bootstrapFdServer; + if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) { + return AssertionFailure() << "Socketpair() failed"; + } + auto status = rpcServer->setupUnixDomainSocketBootstrapServer( + std::move(bootstrapFdServer)); + if (status != OK) { + return AssertionFailure() << "setupUnixDomainSocketBootstrapServer: " + << statusToString(status); + } + mBootstrapSocket = RpcTransportFd(std::move(bootstrapFdClient)); + mAcceptConnection = &Server::recvmsgServerConnection; + mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); }; + } break; + case SocketType::UNIX_RAW: { + auto addr = allocateSocketAddress(); + auto status = rpcServer->setupRawSocketServer(initUnixSocket(addr)); + if (status != OK) { + return AssertionFailure() + << "setupRawSocketServer: " << statusToString(status); + } + mConnectToServer = [addr] { + return connectTo(UnixSocketAddress(addr.c_str())); + }; + } break; case SocketType::VSOCK: { auto port = allocateVsockPort(); - auto status = rpcServer->setupVsockServer(port); + auto status = rpcServer->setupVsockServer(VMADDR_CID_LOCAL, port); if (status != OK) { return AssertionFailure() << "setupVsockServer: " << statusToString(status); } @@ -1603,10 +1468,13 @@ public: addr, port); return base::unique_fd{}; }; - } + } break; + case SocketType::TIPC: { + LOG_ALWAYS_FATAL("RpcTransportTest should not be enabled for TIPC"); + } break; } mFd = rpcServer->releaseServer(); - if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd"; + if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd"; mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx(); if (mCtx == nullptr) return AssertionFailure() << "newServerCtx"; mSetup = true; @@ -1621,14 +1489,33 @@ public: LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!"); mThread = std::make_unique<std::thread>(&Server::run, this); } + + base::unique_fd acceptServerConnection() { + return base::unique_fd(TEMP_FAILURE_RETRY( + accept4(mFd.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))); + } + + base::unique_fd recvmsgServerConnection() { + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> 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(); + } + return std::move(std::get<base::unique_fd>(fds[0])); + } + void run() { LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!"); std::vector<std::thread> threads; while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) { - base::unique_fd acceptedFd( - TEMP_FAILURE_RETRY(accept4(mFd.get(), nullptr, nullptr /*length*/, - SOCK_CLOEXEC | SOCK_NONBLOCK))); + base::unique_fd acceptedFd = mAcceptConnection(this); threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd)); } @@ -1636,7 +1523,8 @@ public: } void handleOne(android::base::unique_fd acceptedFd) { ASSERT_TRUE(acceptedFd.ok()); - auto serverTransport = mCtx->newTransport(std::move(acceptedFd), mFdTrigger.get()); + RpcTransportFd transportFd(std::move(acceptedFd)); + auto serverTransport = mCtx->newTransport(std::move(transportFd), mFdTrigger.get()); if (serverTransport == nullptr) return; // handshake failed ASSERT_TRUE(mPostConnect(serverTransport.get(), mFdTrigger.get())); } @@ -1654,8 +1542,9 @@ public: private: std::unique_ptr<std::thread> mThread; ConnectToServer mConnectToServer; + AcceptConnection mAcceptConnection = &Server::acceptServerConnection; std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make(); - base::unique_fd mFd; + RpcTransportFd mFd, mBootstrapSocket; std::unique_ptr<RpcTransportCtx> mCtx; std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier = std::make_shared<RpcCertificateVerifierSimple>(); @@ -1676,7 +1565,8 @@ public: FdTrigger* fdTrigger) { std::string message(kMessage); iovec messageIov{message.data(), message.size()}; - auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {}); + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); if (status != OK) return AssertionFailure() << statusToString(status); return AssertionSuccess(); } @@ -1687,7 +1577,8 @@ public: explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {} Client(Client&&) = default; [[nodiscard]] AssertionResult setUp(const Param& param) { - auto [socketType, rpcSecurity, certificateFormat] = param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; + (void)serverVersion; mFdTrigger = FdTrigger::make(); mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx(); if (mCtx == nullptr) return AssertionFailure() << "newClientCtx"; @@ -1700,7 +1591,7 @@ public: // connect() and do handshake bool setUpTransport() { mFd = mConnectToServer(); - if (!mFd.ok()) return AssertionFailure() << "Cannot connect to server"; + if (!mFd.fd.ok()) return AssertionFailure() << "Cannot connect to server"; mClientTransport = mCtx->newTransport(std::move(mFd), mFdTrigger.get()); return mClientTransport != nullptr; } @@ -1708,8 +1599,9 @@ public: LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed"); std::string readMessage(expectedMessage.size(), '\0'); iovec readMessageIov{readMessage.data(), readMessage.size()}; - status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(), - &readMessageIov, 1, {}); + status_t readStatus = + mClientTransport->interruptableReadFully(mFdTrigger.get(), &readMessageIov, 1, + std::nullopt, nullptr); if (readStatus != OK) { return AssertionFailure() << statusToString(readStatus); } @@ -1728,9 +1620,11 @@ public: ASSERT_EQ(readOk, readMessage()); } + bool isTransportWaiting() { return mClientTransport->isWaiting(); } + private: ConnectToServer mConnectToServer; - base::unique_fd mFd; + RpcTransportFd mFd; std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make(); std::unique_ptr<RpcTransportCtx> mCtx; std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier = @@ -1757,23 +1651,28 @@ public: using Server = RpcTransportTestUtils::Server; using Client = RpcTransportTestUtils::Client; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, rpcSecurity, certificateFormat] = info.param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param; auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString(); if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat); + ret += "_serverV" + std::to_string(serverVersion); return ret; } static std::vector<ParamType> getRpcTranportTestParams() { std::vector<ParamType> ret; - for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { - for (auto rpcSecurity : RpcSecurityValues()) { - switch (rpcSecurity) { - case RpcSecurity::RAW: { - ret.emplace_back(socketType, rpcSecurity, std::nullopt); - } break; - case RpcSecurity::TLS: { - ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM); - ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER); - } break; + for (auto serverVersion : testVersions()) { + for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { + for (auto rpcSecurity : RpcSecurityValues()) { + switch (rpcSecurity) { + case RpcSecurity::RAW: { + ret.emplace_back(socketType, rpcSecurity, std::nullopt, serverVersion); + } break; + case RpcSecurity::TLS: { + ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM, + serverVersion); + ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER, + serverVersion); + } break; + } } } } @@ -1781,9 +1680,15 @@ public: } template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b); } + void SetUp() override { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + } }; TEST_P(RpcTransportTest, GoodCertificate) { @@ -1817,7 +1722,8 @@ TEST_P(RpcTransportTest, MultipleClients) { } TEST_P(RpcTransportTest, UntrustedServer) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; auto untrustedServer = std::make_unique<Server>(); ASSERT_TRUE(untrustedServer->setUp(GetParam())); @@ -1835,7 +1741,9 @@ TEST_P(RpcTransportTest, UntrustedServer) { client.run(handshakeOk); } TEST_P(RpcTransportTest, MaliciousServer) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto validServer = std::make_unique<Server>(); ASSERT_TRUE(validServer->setUp(GetParam())); @@ -1858,7 +1766,9 @@ TEST_P(RpcTransportTest, MaliciousServer) { } TEST_P(RpcTransportTest, UntrustedClient) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); @@ -1877,7 +1787,9 @@ TEST_P(RpcTransportTest, UntrustedClient) { } TEST_P(RpcTransportTest, MaliciousClient) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); @@ -1904,7 +1816,8 @@ TEST_P(RpcTransportTest, Trigger) { auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) { std::string message(RpcTransportTestUtils::kMessage); iovec messageIov{message.data(), message.size()}; - auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {}); + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); if (status != OK) return AssertionFailure() << statusToString(status); { @@ -1915,7 +1828,8 @@ TEST_P(RpcTransportTest, Trigger) { } iovec msg2Iov{msg2.data(), msg2.size()}; - status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, {}); + status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt, + nullptr); if (status != DEAD_OBJECT) return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully " "should return DEAD_OBJECT, but it is " @@ -1957,28 +1871,83 @@ TEST_P(RpcTransportTest, Trigger) { ASSERT_FALSE(client.readMessage(msg2)); } +TEST_P(RpcTransportTest, CheckWaitingForRead) { + std::mutex readMutex; + std::condition_variable readCv; + bool shouldContinueReading = false; + // Server will write data on transport once its started + auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) { + std::string message(RpcTransportTestUtils::kMessage); + iovec messageIov{message.data(), message.size()}; + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); + if (status != OK) return AssertionFailure() << statusToString(status); + + { + std::unique_lock<std::mutex> lock(readMutex); + shouldContinueReading = true; + lock.unlock(); + readCv.notify_all(); + } + return AssertionSuccess(); + }; + + // Setup Server and client + auto server = std::make_unique<Server>(); + ASSERT_TRUE(server->setUp(GetParam())); + + Client client(server->getConnectToServerFn()); + ASSERT_TRUE(client.setUp(GetParam())); + + ASSERT_EQ(OK, trust(&client, server)); + ASSERT_EQ(OK, trust(server, &client)); + server->setPostConnect(serverPostConnect); + + server->start(); + ASSERT_TRUE(client.setUpTransport()); + { + // Wait till server writes data + std::unique_lock<std::mutex> lock(readMutex); + ASSERT_TRUE(readCv.wait_for(lock, 3s, [&] { return shouldContinueReading; })); + } + + // Since there is no read polling here, we will get polling count 0 + ASSERT_FALSE(client.isTransportWaiting()); + ASSERT_TRUE(client.readMessage(RpcTransportTestUtils::kMessage)); + // Thread should increment polling count, read and decrement polling count + // Again, polling count should be zero here + ASSERT_FALSE(client.isTransportWaiting()); + + server->shutdown(); +} + INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, ::testing::ValuesIn(RpcTransportTest::getRpcTranportTestParams()), RpcTransportTest::PrintParamInfo); class RpcTransportTlsKeyTest - : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> { + : public testing::TestWithParam< + std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> { public: template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b); } static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, certificateFormat, keyFormat] = info.param; - auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + - "_key_" + PrintToString(keyFormat); - return ret; + auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param; + return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + + "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion); }; }; TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); std::vector<uint8_t> pkeyData, certData; { @@ -1993,8 +1962,8 @@ TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat); auto desCert = deserializeCertificate(certData, certificateFormat); auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert)); - auto utilsParam = - std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat)); + auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, + std::make_optional(certificateFormat), serverVersion); auto server = std::make_unique<RpcTransportTestUtils::Server>(); ASSERT_TRUE(server->setUp(utilsParam, std::move(auth))); @@ -2013,7 +1982,8 @@ INSTANTIATE_TEST_CASE_P( BinderRpc, RpcTransportTlsKeyTest, testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)), testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER), - testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)), + testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER), + testing::ValuesIn(testVersions())), RpcTransportTlsKeyTest::PrintParamInfo); } // namespace android diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp new file mode 100644 index 0000000000..fe9a5a1af7 --- /dev/null +++ b/libs/binder/tests/binderRpcTestCommon.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "binderRpcTestCommon.h" + +namespace android { + +std::atomic<int32_t> MyBinderRpcSession::gNum; +sp<IBinder> MyBinderRpcTestBase::mHeldBinder; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h new file mode 100644 index 0000000000..a467ee389b --- /dev/null +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <BinderRpcTestClientInfo.h> +#include <BinderRpcTestServerConfig.h> +#include <BinderRpcTestServerInfo.h> +#include <BnBinderRpcCallback.h> +#include <BnBinderRpcSession.h> +#include <BnBinderRpcTest.h> +#include <android-base/stringprintf.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <binder/RpcThreads.h> +#include <binder/RpcTransport.h> +#include <binder/RpcTransportRaw.h> +#include <unistd.h> +#include <cinttypes> +#include <string> +#include <vector> + +#ifndef __TRUSTY__ +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/binder_auto_utils.h> +#include <android/binder_libbinder.h> +#include <binder/ProcessState.h> +#include <binder/RpcTlsTestUtils.h> +#include <binder/RpcTlsUtils.h> +#include <binder/RpcTransportTls.h> + +#include <signal.h> + +#include "../OS.h" // for testing UnixBootstrap clients +#include "../RpcSocketAddress.h" // for testing preconnected clients +#include "../vm_sockets.h" // for VMADDR_* +#endif // __TRUSTY__ + +#include "../BuildFlags.h" +#include "../FdTrigger.h" +#include "../RpcState.h" // for debugging +#include "utils/Errors.h" + +namespace android { + +constexpr char kLocalInetAddress[] = "127.0.0.1"; + +enum class RpcSecurity { RAW, TLS }; + +static inline std::vector<RpcSecurity> RpcSecurityValues() { + return {RpcSecurity::RAW, RpcSecurity::TLS}; +} + +static inline std::vector<uint32_t> testVersions() { + std::vector<uint32_t> versions; + for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) { + versions.push_back(i); + } + versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + return versions; +} + +static inline std::string trustyIpcPort(uint32_t serverVersion) { + return base::StringPrintf("com.android.trusty.binderRpcTestService.V%" PRIu32, serverVersion); +} + +enum class SocketType { + PRECONNECTED, + UNIX, + UNIX_BOOTSTRAP, + UNIX_RAW, + VSOCK, + INET, + TIPC, +}; + +static inline std::string PrintToString(SocketType socketType) { + switch (socketType) { + case SocketType::PRECONNECTED: + return "preconnected_uds"; + case SocketType::UNIX: + return "unix_domain_socket"; + case SocketType::UNIX_BOOTSTRAP: + return "unix_domain_socket_bootstrap"; + case SocketType::UNIX_RAW: + return "raw_uds"; + case SocketType::VSOCK: + return "vm_socket"; + case SocketType::INET: + return "inet_socket"; + case SocketType::TIPC: + return "trusty_ipc"; + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + return ""; + } +} + +static inline size_t epochMillis() { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::system_clock; + return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); +} + +struct BinderRpcOptions { + size_t numThreads = 1; + size_t numSessions = 1; + size_t numIncomingConnections = 0; + size_t numOutgoingConnections = SIZE_MAX; + RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode = + RpcSession::FileDescriptorTransportMode::NONE; + std::vector<RpcSession::FileDescriptorTransportMode> + serverSupportedFileDescriptorTransportModes = { + RpcSession::FileDescriptorTransportMode::NONE}; + + // If true, connection failures will result in `ProcessSession::sessions` being empty + // instead of a fatal error. + bool allowConnectFailure = false; +}; + +#ifndef __TRUSTY__ +static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { + uint64_t length = str.length(); + CHECK(android::base::WriteFully(fd, &length, sizeof(length))); + CHECK(android::base::WriteFully(fd, str.data(), str.length())); +} + +static inline std::string readString(android::base::borrowed_fd fd) { + uint64_t length; + CHECK(android::base::ReadFully(fd, &length, sizeof(length))); + std::string ret(length, '\0'); + CHECK(android::base::ReadFully(fd, ret.data(), length)); + return ret; +} + +static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { + Parcel parcel; + CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); + writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); +} + +template <typename T> +static inline T readFromFd(android::base::borrowed_fd fd) { + std::string data = readString(fd); + Parcel parcel; + CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); + T object; + CHECK_EQ(OK, object.readFromParcel(&parcel)); + return object; +} + +static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( + RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr, + std::unique_ptr<RpcAuth> auth = nullptr) { + switch (rpcSecurity) { + case RpcSecurity::RAW: + return RpcTransportCtxFactoryRaw::make(); + case RpcSecurity::TLS: { + if (verifier == nullptr) { + verifier = std::make_shared<RpcCertificateVerifierSimple>(); + } + if (auth == nullptr) { + auth = std::make_unique<RpcAuthSelfSigned>(); + } + return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth)); + } + default: + LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity); + } +} + +// Create an FD that returns `contents` when read. +static inline base::unique_fd mockFileDescriptor(std::string contents) { + android::base::unique_fd readFd, writeFd; + CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno); + RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() { + signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write + if (!WriteStringToFd(contents, writeFd)) { + int savedErrno = errno; + LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s", + strerror(savedErrno)); + } + }).detach(); + return readFd; +} +#endif // __TRUSTY__ + +// A threadsafe channel where writes block until the value is read. +template <typename T> +class HandoffChannel { +public: + void write(T v) { + { + RpcMutexUniqueLock lock(mMutex); + // Wait for space to send. + mCvEmpty.wait(lock, [&]() { return !mValue.has_value(); }); + mValue.emplace(std::move(v)); + } + mCvFull.notify_all(); + RpcMutexUniqueLock lock(mMutex); + // Wait for it to be taken. + mCvEmpty.wait(lock, [&]() { return !mValue.has_value(); }); + } + + T read() { + RpcMutexUniqueLock lock(mMutex); + if (!mValue.has_value()) { + mCvFull.wait(lock, [&]() { return mValue.has_value(); }); + } + T v = std::move(mValue.value()); + mValue.reset(); + lock.unlock(); + mCvEmpty.notify_all(); + return std::move(v); + } + +private: + RpcMutex mMutex; + RpcConditionVariable mCvEmpty; + RpcConditionVariable mCvFull; + std::optional<T> mValue; +}; + +using android::binder::Status; + +class MyBinderRpcSession : public BnBinderRpcSession { +public: + static std::atomic<int32_t> gNum; + + MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } + Status getName(std::string* name) override { + *name = mName; + return Status::ok(); + } + ~MyBinderRpcSession() { gNum--; } + +private: + std::string mName; +}; + +class MyBinderRpcCallback : public BnBinderRpcCallback { + Status sendCallback(const std::string& value) { + RpcMutexUniqueLock _l(mMutex); + mValues.push_back(value); + _l.unlock(); + mCv.notify_one(); + return Status::ok(); + } + Status sendOnewayCallback(const std::string& value) { return sendCallback(value); } + +public: + RpcMutex mMutex; + RpcConditionVariable mCv; + std::vector<std::string> mValues; +}; + +// Base class for all concrete implementations of MyBinderRpcTest. +// Sub-classes that want to provide a full implementation should derive +// from this class instead of MyBinderRpcTestDefault below so the compiler +// checks that all methods are implemented. +class MyBinderRpcTestBase : public BnBinderRpcTest { +public: + int port = 0; + + Status sendString(const std::string& str) override { + (void)str; + return Status::ok(); + } + Status doubleString(const std::string& str, std::string* strstr) override { + *strstr = str + str; + return Status::ok(); + } + Status getClientPort(int* out) override { + *out = port; + return Status::ok(); + } + Status getNullBinder(sp<IBinder>* out) override { + out->clear(); + return Status::ok(); + } + Status pingMe(const sp<IBinder>& binder, int32_t* out) override { + if (binder == nullptr) { + std::cout << "Received null binder!" << std::endl; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + *out = binder->pingBinder(); + return Status::ok(); + } + Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { + *out = binder; + return Status::ok(); + } + static sp<IBinder> mHeldBinder; + Status holdBinder(const sp<IBinder>& binder) override { + mHeldBinder = binder; + return Status::ok(); + } + Status getHeldBinder(sp<IBinder>* held) override { + *held = mHeldBinder; + return Status::ok(); + } + Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { + if (count <= 0) return Status::ok(); + return binder->nestMe(this, count - 1); + } + Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { + static sp<IBinder> binder = new BBinder; + *out = binder; + return Status::ok(); + } + Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { + *out = new MyBinderRpcSession(name); + return Status::ok(); + } + Status getNumOpenSessions(int32_t* out) override { + *out = MyBinderRpcSession::gNum; + return Status::ok(); + } + + RpcMutex blockMutex; + Status lock() override { + blockMutex.lock(); + return Status::ok(); + } + Status unlockInMsAsync(int32_t ms) override { + usleep(ms * 1000); + blockMutex.unlock(); + return Status::ok(); + } + Status lockUnlock() override { + RpcMutexLockGuard _l(blockMutex); + return Status::ok(); + } + + Status sleepMs(int32_t ms) override { + usleep(ms * 1000); + return Status::ok(); + } + + Status sleepMsAsync(int32_t ms) override { + // In-process binder calls are asynchronous, but the call to this method + // is synchronous wrt its client. This in/out-process threading model + // diffentiation is a classic binder leaky abstraction (for better or + // worse) and is preserved here the way binder sockets plugs itself + // into BpBinder, as nothing is changed at the higher levels + // (IInterface) which result in this behavior. + return sleepMs(ms); + } + + Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, + const std::string& value) override { + if (callback == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + if (delayed) { + RpcMaybeThread([=]() { + ALOGE("Executing delayed callback: '%s'", value.c_str()); + Status status = doCallback(callback, oneway, false, value); + ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); + }).detach(); + return Status::ok(); + } + + if (oneway) { + return callback->sendOnewayCallback(value); + } + + return callback->sendCallback(value); + } + + Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, + const std::string& value) override { + return doCallback(callback, oneway, delayed, value); + } + +protected: + // Generic version of countBinders that works with both + // RpcServer and RpcServerTrusty + template <typename T> + Status countBindersImpl(const wp<T>& server, std::vector<int32_t>* out) { + sp<T> spServer = server.promote(); + if (spServer == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + out->clear(); + for (auto session : spServer->listSessions()) { + size_t count = session->state()->countBinders(); + out->push_back(count); + } + return Status::ok(); + } +}; + +// Default implementation of MyBinderRpcTest that can be used as-is +// or derived from by classes that only want to implement a subset of +// the unimplemented methods +class MyBinderRpcTestDefault : public MyBinderRpcTestBase { +public: + Status countBinders(std::vector<int32_t>* /*out*/) override { + return Status::fromStatusT(UNKNOWN_TRANSACTION); + } + + Status die(bool /*cleanup*/) override { return Status::fromStatusT(UNKNOWN_TRANSACTION); } + + Status scheduleShutdown() override { return Status::fromStatusT(UNKNOWN_TRANSACTION); } + + Status useKernelBinderCallingId() override { return Status::fromStatusT(UNKNOWN_TRANSACTION); } + + Status echoAsFile(const std::string& /*content*/, + android::os::ParcelFileDescriptor* /*out*/) override { + return Status::fromStatusT(UNKNOWN_TRANSACTION); + } + + Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& /*files*/, + android::os::ParcelFileDescriptor* /*out*/) override { + return Status::fromStatusT(UNKNOWN_TRANSACTION); + } + + Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& /*fd*/) override { + return Status::fromStatusT(UNKNOWN_TRANSACTION); + } + + Status blockingRecvFd(android::os::ParcelFileDescriptor* /*fd*/) override { + return Status::fromStatusT(UNKNOWN_TRANSACTION); + } +}; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h new file mode 100644 index 0000000000..c99d68aacf --- /dev/null +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gtest/gtest.h> + +#include "binderRpcTestCommon.h" + +#define EXPECT_OK(status) \ + do { \ + android::binder::Status stat = (status); \ + EXPECT_TRUE(stat.isOk()) << stat; \ + } while (false) + +namespace android { + +// Abstract base class with a virtual destructor that handles the +// ownership of a process session for BinderRpcTestSession below +class ProcessSession { +public: + struct SessionInfo { + sp<RpcSession> session; + sp<IBinder> root; + }; + + // client session objects associated with other process + // each one represents a separate session + std::vector<SessionInfo> sessions; + + virtual ~ProcessSession() = 0; + + // If the process exits with a status, run the given callback on that value. + virtual void setCustomExitStatusCheck(std::function<void(int wstatus)> f) = 0; + + // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead. + virtual void terminate() = 0; +}; + +// Process session where the process hosts IBinderRpcTest, the server used +// for most testing here +struct BinderRpcTestProcessSession { + std::unique_ptr<ProcessSession> proc; + + // pre-fetched root object (for first session) + sp<IBinder> rootBinder; + + // pre-casted root object (for first session) + sp<IBinderRpcTest> rootIface; + + // whether session should be invalidated by end of run + bool expectAlreadyShutdown = false; + + BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; + ~BinderRpcTestProcessSession() { + if (!expectAlreadyShutdown) { + EXPECT_NE(nullptr, rootIface); + if (rootIface == nullptr) return; + + std::vector<int32_t> remoteCounts; + // calling over any sessions counts across all sessions + EXPECT_OK(rootIface->countBinders(&remoteCounts)); + EXPECT_EQ(remoteCounts.size(), proc->sessions.size()); + for (auto remoteCount : remoteCounts) { + EXPECT_EQ(remoteCount, 1); + } + + // even though it is on another thread, shutdown races with + // the transaction reply being written + if (auto status = rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + } + + rootIface = nullptr; + rootBinder = nullptr; + } +}; + +class BinderRpc : public ::testing::TestWithParam< + std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> { +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()); } + + bool clientOrServerSingleThreaded() const { + return !kEnableRpcThreads || serverSingleThreaded(); + } + + // Whether the test params support sending FDs in parcels. + bool supportsFdTransport() const { + if (socketType() == SocketType::TIPC) { + // Trusty does not support file descriptors yet + return false; + } + return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && + (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX || + socketType() == SocketType::UNIX_BOOTSTRAP || + socketType() == SocketType::UNIX_RAW); + } + + void SetUp() override { + if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) { + GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport"; + } + } + + BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { + BinderRpcTestProcessSession ret{ + .proc = createRpcTestSocketServerProcessEtc(options), + }; + + ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root; + ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); + + return ret; + } + + static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info); + +protected: + std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc( + const BinderRpcOptions& options); +}; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp new file mode 100644 index 0000000000..a27bd2f2e6 --- /dev/null +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "binderRpcTestCommon.h" + +using namespace android; + +class MyBinderRpcTestAndroid : public MyBinderRpcTestBase { +public: + wp<RpcServer> server; + + Status countBinders(std::vector<int32_t>* out) override { + return countBindersImpl(server, out); + } + + Status die(bool cleanup) override { + if (cleanup) { + exit(1); + } else { + _exit(1); + } + } + + Status scheduleShutdown() override { + sp<RpcServer> strongServer = server.promote(); + if (strongServer == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + RpcMaybeThread([=] { + LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown"); + }).detach(); + return Status::ok(); + } + + Status useKernelBinderCallingId() override { + // this is WRONG! It does not make sense when using RPC binder, and + // because it is SO wrong, and so much code calls this, it should abort! + + if constexpr (kEnableKernelIpc) { + (void)IPCThreadState::self()->getCallingPid(); + } + return Status::ok(); + } + + Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override { + out->reset(mockFileDescriptor(content)); + return Status::ok(); + } + + Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files, + android::os::ParcelFileDescriptor* out) override { + std::string acc; + for (const auto& file : files) { + std::string result; + CHECK(android::base::ReadFdToString(file.get(), &result)); + acc.append(result); + } + out->reset(mockFileDescriptor(acc)); + return Status::ok(); + } + + HandoffChannel<android::base::unique_fd> mFdChannel; + + Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override { + mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0))); + return Status::ok(); + } + + Status blockingRecvFd(android::os::ParcelFileDescriptor* fd) override { + fd->reset(mFdChannel.read()); + return Status::ok(); + } +}; + +int main(int argc, char* argv[]) { + android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + + LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc); + base::unique_fd writeEnd(atoi(argv[1])); + base::unique_fd readEnd(atoi(argv[2])); + + auto serverConfig = readFromFd<BinderRpcTestServerConfig>(readEnd); + auto socketType = static_cast<SocketType>(serverConfig.socketType); + auto rpcSecurity = static_cast<RpcSecurity>(serverConfig.rpcSecurity); + + std::vector<RpcSession::FileDescriptorTransportMode> + serverSupportedFileDescriptorTransportModes; + for (auto mode : serverConfig.serverSupportedFileDescriptorTransportModes) { + serverSupportedFileDescriptorTransportModes.push_back( + static_cast<RpcSession::FileDescriptorTransportMode>(mode)); + } + + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); + + server->setProtocolVersion(serverConfig.serverVersion); + server->setMaxThreads(serverConfig.numThreads); + server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); + + unsigned int outPort = 0; + base::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; + break; + case SocketType::UNIX_BOOTSTRAP: + CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd))); + break; + case SocketType::UNIX_RAW: + CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd))); + break; + case SocketType::VSOCK: + CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort)); + break; + case SocketType::INET: { + CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); + CHECK_NE(0, outPort); + break; + } + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + } + + BinderRpcTestServerInfo serverInfo; + serverInfo.port = static_cast<int64_t>(outPort); + serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); + writeToFd(writeEnd, serverInfo); + auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); + + if (rpcSecurity == RpcSecurity::TLS) { + for (const auto& clientCert : clientInfo.certs) { + CHECK_EQ(OK, + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + clientCert.data)); + } + } + + server->setPerSessionRootObject([&](const void* addrPtr, size_t len) { + // UNIX sockets with abstract addresses return + // sizeof(sa_family_t)==2 in addrlen + CHECK_GE(len, sizeof(sa_family_t)); + const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr); + sp<MyBinderRpcTestAndroid> service = sp<MyBinderRpcTestAndroid>::make(); + switch (addr->sa_family) { + case AF_UNIX: + // nothing to save + break; + case AF_VSOCK: + CHECK_EQ(len, sizeof(sockaddr_vm)); + service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port; + break; + case AF_INET: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port); + break; + case AF_INET6: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port); + break; + default: + LOG_ALWAYS_FATAL("Unrecognized address family %d", addr->sa_family); + } + service->server = server; + return service; + }); + + server->join(); + + // Another thread calls shutdown. Wait for it to complete. + (void)server->shutdown(); + + return 0; +} diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp new file mode 100644 index 0000000000..85573895e9 --- /dev/null +++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp @@ -0,0 +1,107 @@ +/* + * 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 "binderRpcTestService" + +#include <android-base/stringprintf.h> +#include <binder/RpcServerTrusty.h> +#include <inttypes.h> +#include <lib/tipc/tipc.h> +#include <lk/err_ptr.h> +#include <stdio.h> +#include <trusty_log.h> +#include <vector> + +#include "binderRpcTestCommon.h" + +using namespace android; +using android::base::StringPrintf; +using binder::Status; + +static int gConnectionCounter = 0; + +class MyBinderRpcTestTrusty : public MyBinderRpcTestDefault { +public: + wp<RpcServerTrusty> server; + + Status countBinders(std::vector<int32_t>* out) override { + return countBindersImpl(server, out); + } + + Status scheduleShutdown() override { + // TODO: Trusty does not support shutting down the tipc event loop, + // so we just terminate the service app since it is marked + // restart_on_exit + exit(EXIT_SUCCESS); + } + + // TODO(b/242940548): implement echoAsFile and concatFiles +}; + +struct ServerInfo { + std::unique_ptr<std::string> port; + sp<RpcServerTrusty> server; +}; + +int main(void) { + TLOGI("Starting service\n"); + + tipc_hset* hset = tipc_hset_create(); + if (IS_ERR(hset)) { + TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset)); + return EXIT_FAILURE; + } + + const auto port_acl = RpcServerTrusty::PortAcl{ + .flags = IPC_PORT_ALLOW_NS_CONNECT | IPC_PORT_ALLOW_TA_CONNECT, + }; + + std::vector<ServerInfo> servers; + for (auto serverVersion : testVersions()) { + ServerInfo serverInfo{ + .port = std::make_unique<std::string>(trustyIpcPort(serverVersion)), + }; + TLOGI("Adding service port '%s'\n", serverInfo.port->c_str()); + + // 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 = + RpcServerTrusty::make(hset, serverInfo.port->c_str(), + std::shared_ptr<const RpcServerTrusty::PortAcl>(&port_acl), + max_msg_size); + if (!serverOrErr.ok()) { + TLOGE("Failed to create RpcServer (%d)\n", serverOrErr.error()); + 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<MyBinderRpcTestTrusty>::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)); + } + + return tipc_run_event_loop(hset); +} diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp new file mode 100644 index 0000000000..b3bb5ebda7 --- /dev/null +++ b/libs/binder/tests/binderRpcTestTrusty.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "binderRpcTest" + +#include <android-base/stringprintf.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <trusty-gtest.h> +#include <trusty_ipc.h> + +#include "binderRpcTestFixture.h" + +namespace android { + +// Destructors need to be defined, even if pure virtual +ProcessSession::~ProcessSession() {} + +class TrustyProcessSession : public ProcessSession { +public: + ~TrustyProcessSession() override {} + + void setCustomExitStatusCheck(std::function<void(int wstatus)> /*f*/) override { + LOG_ALWAYS_FATAL("setCustomExitStatusCheck() not supported"); + } + + void terminate() override { LOG_ALWAYS_FATAL("terminate() not supported"); } +}; + +std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { + auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param; + auto ret = PrintToString(type) + "_clientV" + std::to_string(clientVersion) + "_serverV" + + std::to_string(serverVersion); + if (singleThreaded) { + ret += "_single_threaded"; + } + if (noKernel) { + ret += "_no_kernel"; + } + return ret; +} + +// This creates a new process serving an interface on a certain number of +// threads. +std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( + const BinderRpcOptions& options) { + LOG_ALWAYS_FATAL_IF(options.numIncomingConnections != 0, + "Non-zero incoming connections %zu on Trusty", + options.numIncomingConnections); + + uint32_t clientVersion = std::get<2>(GetParam()); + uint32_t serverVersion = std::get<3>(GetParam()); + + auto ret = std::make_unique<TrustyProcessSession>(); + + status_t status; + for (size_t i = 0; i < options.numSessions; i++) { + auto factory = android::RpcTransportCtxFactoryTipcTrusty::make(); + auto session = android::RpcSession::make(std::move(factory)); + + EXPECT_TRUE(session->setProtocolVersion(clientVersion)); + session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); + + status = session->setupPreconnectedClient({}, [&]() { + 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); + }); + if (options.allowConnectFailure && status != OK) { + ret->sessions.clear(); + break; + } + LOG_ALWAYS_FATAL_IF(status != OK, "Failed to connect to service: %s", + statusToString(status).c_str()); + ret->sessions.push_back({session, session->getRootObject()}); + } + + 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)), + BinderRpc::PrintParamInfo); + +} // namespace android + +PORT_GTEST(BinderRpcTest, "com.android.trusty.binderRpcTest"); diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp new file mode 100644 index 0000000000..11a22b0fb6 --- /dev/null +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <chrono> +#include <cstdlib> +#include <type_traits> + +#include "binderRpcTestCommon.h" +#include "binderRpcTestFixture.h" + +using namespace std::chrono_literals; +using namespace std::placeholders; +using testing::AssertionFailure; +using testing::AssertionResult; +using testing::AssertionSuccess; + +namespace android { + +static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT || + RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + +TEST(BinderRpcParcel, EntireParcelFormatted) { + Parcel p; + p.writeInt32(3); + + EXPECT_DEATH_IF_SUPPORTED(p.markForBinder(sp<BBinder>::make()), + "format must be set before data is written"); +} + +TEST(BinderRpc, CannotUseNextWireVersion) { + auto session = RpcSession::make(); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT)); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1)); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2)); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15)); +} + +TEST(BinderRpc, CanUseExperimentalWireVersion) { + auto session = RpcSession::make(); + EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); +} + +TEST_P(BinderRpc, Ping) { + auto proc = createRpcTestSocketServerProcess({}); + ASSERT_NE(proc.rootBinder, nullptr); + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); +} + +TEST_P(BinderRpc, GetInterfaceDescriptor) { + auto proc = createRpcTestSocketServerProcess({}); + ASSERT_NE(proc.rootBinder, nullptr); + EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor()); +} + +TEST_P(BinderRpc, MultipleSessions) { + if (serverSingleThreaded()) { + // Tests with multiple sessions require a multi-threaded service, + // but work fine on a single-threaded client + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + + auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5}); + for (auto session : proc.proc->sessions) { + ASSERT_NE(nullptr, session.root); + EXPECT_EQ(OK, session.root->pingBinder()); + } +} + +TEST_P(BinderRpc, SeparateRootObject) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + + SocketType type = std::get<0>(GetParam()); + if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || + type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) { + // we can't get port numbers for unix sockets + return; + } + + auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); + + int port1 = 0; + EXPECT_OK(proc.rootIface->getClientPort(&port1)); + + sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root); + int port2; + EXPECT_OK(rootIface2->getClientPort(&port2)); + + // we should have a different IBinderRpcTest object created for each + // session, because we use setPerSessionRootObject + EXPECT_NE(port1, port2); +} + +TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { + auto proc = createRpcTestSocketServerProcess({}); + Parcel data; + Parcel reply; + EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0)); +} + +TEST_P(BinderRpc, AppendSeparateFormats) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "Trusty does not support multiple server processes"; + } + + auto proc1 = createRpcTestSocketServerProcess({}); + auto proc2 = createRpcTestSocketServerProcess({}); + + Parcel pRaw; + + Parcel p1; + p1.markForBinder(proc1.rootBinder); + p1.writeInt32(3); + + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize())); + EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); + + Parcel p2; + p2.markForBinder(proc2.rootBinder); + p2.writeInt32(7); + + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize())); + EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize())); +} + +TEST_P(BinderRpc, UnknownTransaction) { + auto proc = createRpcTestSocketServerProcess({}); + Parcel data; + data.markForBinder(proc.rootBinder); + Parcel reply; + EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0)); +} + +TEST_P(BinderRpc, SendSomethingOneway) { + auto proc = createRpcTestSocketServerProcess({}); + EXPECT_OK(proc.rootIface->sendString("asdf")); +} + +TEST_P(BinderRpc, SendAndGetResultBack) { + auto proc = createRpcTestSocketServerProcess({}); + std::string doubled; + EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled)); + EXPECT_EQ("cool cool ", doubled); +} + +TEST_P(BinderRpc, SendAndGetResultBackBig) { + auto proc = createRpcTestSocketServerProcess({}); + // Trusty has a limit of 4096 bytes for the entire RPC Binder message + size_t singleLen = socketType() == SocketType::TIPC ? 512 : 4096; + std::string single = std::string(singleLen, 'a'); + std::string doubled; + EXPECT_OK(proc.rootIface->doubleString(single, &doubled)); + EXPECT_EQ(single + single, doubled); +} + +TEST_P(BinderRpc, InvalidNullBinderReturn) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL); +} + +TEST_P(BinderRpc, CallMeBack) { + auto proc = createRpcTestSocketServerProcess({}); + + int32_t pingResult; + EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult)); + EXPECT_EQ(OK, pingResult); + + EXPECT_EQ(0, MyBinderRpcSession::gNum); +} + +TEST_P(BinderRpc, RepeatBinder) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> inBinder = new MyBinderRpcSession("foo"); + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); + EXPECT_EQ(inBinder, outBinder); + + wp<IBinder> weak = inBinder; + inBinder = nullptr; + outBinder = nullptr; + + // Force reading a reply, to process any pending dec refs from the other + // process (the other process will process dec refs there before processing + // the ping here). + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + EXPECT_EQ(nullptr, weak.promote()); + + EXPECT_EQ(0, MyBinderRpcSession::gNum); +} + +TEST_P(BinderRpc, RepeatTheirBinder) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); + + sp<IBinder> inBinder = IInterface::asBinder(session); + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); + EXPECT_EQ(inBinder, outBinder); + + wp<IBinder> weak = inBinder; + session = nullptr; + inBinder = nullptr; + outBinder = nullptr; + + // Force reading a reply, to process any pending dec refs from the other + // process (the other process will process dec refs there before processing + // the ping here). + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + EXPECT_EQ(nullptr, weak.promote()); +} + +TEST_P(BinderRpc, RepeatBinderNull) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder)); + EXPECT_EQ(nullptr, outBinder); +} + +TEST_P(BinderRpc, HoldBinder) { + auto proc = createRpcTestSocketServerProcess({}); + + IBinder* ptr = nullptr; + { + sp<IBinder> binder = new BBinder(); + ptr = binder.get(); + EXPECT_OK(proc.rootIface->holdBinder(binder)); + } + + sp<IBinder> held; + EXPECT_OK(proc.rootIface->getHeldBinder(&held)); + + EXPECT_EQ(held.get(), ptr); + + // stop holding binder, because we test to make sure references are cleaned + // up + EXPECT_OK(proc.rootIface->holdBinder(nullptr)); + // and flush ref counts + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); +} + +// START TESTS FOR LIMITATIONS OF SOCKET BINDER +// These are behavioral differences form regular binder, where certain usecases +// aren't supported. + +TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "Trusty does not support multiple server processes"; + } + + auto proc1 = createRpcTestSocketServerProcess({}); + auto proc2 = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); +} + +TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + + auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2}); + + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder) + .transactionError()); +} + +TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError()); +} + +TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + + auto proc = createRpcTestSocketServerProcess({}); + + // for historical reasons, IServiceManager interface only returns the + // exception code + EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED, + defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder)); +} + +// END TESTS FOR LIMITATIONS OF SOCKET BINDER + +TEST_P(BinderRpc, RepeatRootObject) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder)); + EXPECT_EQ(proc.rootBinder, outBinder); +} + +TEST_P(BinderRpc, NestedTransactions) { + auto fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX; + if (socketType() == SocketType::TIPC) { + // TIPC does not support file descriptors yet + fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE; + } + auto proc = createRpcTestSocketServerProcess({ + // Enable FD support because it uses more stack space and so represents + // something closer to a worst case scenario. + .clientFileDescriptorTransportMode = fileDescriptorTransportMode, + .serverSupportedFileDescriptorTransportModes = {fileDescriptorTransportMode}, + }); + + auto nastyNester = sp<MyBinderRpcTestDefault>::make(); + EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); + + wp<IBinder> weak = nastyNester; + nastyNester = nullptr; + EXPECT_EQ(nullptr, weak.promote()); +} + +TEST_P(BinderRpc, SameBinderEquality) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> a; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + + sp<IBinder> b; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); + + EXPECT_EQ(a, b); +} + +TEST_P(BinderRpc, SameBinderEqualityWeak) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> a; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + wp<IBinder> weak = a; + a = nullptr; + + sp<IBinder> b; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); + + // this is the wrong behavior, since BpBinder + // doesn't implement onIncStrongAttempted + // but make sure there is no crash + EXPECT_EQ(nullptr, weak.promote()); + + GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder."; + + // In order to fix this: + // - need to have incStrongAttempted reflected across IPC boundary (wait for + // response to promote - round trip...) + // - sendOnLastWeakRef, to delete entries out of RpcState table + EXPECT_EQ(b, weak.promote()); +} + +#define EXPECT_SESSIONS(expected, iface) \ + do { \ + int session; \ + EXPECT_OK((iface)->getNumOpenSessions(&session)); \ + EXPECT_EQ(static_cast<int>(expected), session); \ + } while (false) + +TEST_P(BinderRpc, SingleSession) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); + std::string out; + EXPECT_OK(session->getName(&out)); + EXPECT_EQ("aoeu", out); + + EXPECT_SESSIONS(1, proc.rootIface); + session = nullptr; + EXPECT_SESSIONS(0, proc.rootIface); +} + +TEST_P(BinderRpc, ManySessions) { + auto proc = createRpcTestSocketServerProcess({}); + + std::vector<sp<IBinderRpcSession>> sessions; + + for (size_t i = 0; i < 15; i++) { + EXPECT_SESSIONS(i, proc.rootIface); + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session)); + sessions.push_back(session); + } + EXPECT_SESSIONS(sessions.size(), proc.rootIface); + for (size_t i = 0; i < sessions.size(); i++) { + std::string out; + EXPECT_OK(sessions.at(i)->getName(&out)); + EXPECT_EQ(std::to_string(i), out); + } + EXPECT_SESSIONS(sessions.size(), proc.rootIface); + + while (!sessions.empty()) { + sessions.pop_back(); + EXPECT_SESSIONS(sessions.size(), proc.rootIface); + } + EXPECT_SESSIONS(0, proc.rootIface); +} + +TEST_P(BinderRpc, OnewayCallDoesNotWait) { + constexpr size_t kReallyLongTimeMs = 100; + constexpr size_t kSleepMs = kReallyLongTimeMs * 5; + + auto proc = createRpcTestSocketServerProcess({}); + + size_t epochMsBefore = epochMillis(); + + EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs)); + + size_t epochMsAfter = epochMillis(); + EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs); +} + +TEST_P(BinderRpc, Callbacks) { + const static std::string kTestString = "good afternoon!"; + + for (bool callIsOneway : {true, false}) { + for (bool callbackIsOneway : {true, false}) { + for (bool delayed : {true, false}) { + if (clientOrServerSingleThreaded() && + (callIsOneway || callbackIsOneway || delayed)) { + // we have no incoming connections to receive the callback + continue; + } + + size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1; + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, + .numSessions = 1, + .numIncomingConnections = numIncomingConnections}); + auto cb = sp<MyBinderRpcCallback>::make(); + + if (callIsOneway) { + EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed, + kTestString)); + } else { + EXPECT_OK( + proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString)); + } + + // if both transactions are synchronous and the response is sent back on the + // same thread, everything should have happened in a nested call. Otherwise, + // the callback will be processed on another thread. + if (callIsOneway || callbackIsOneway || delayed) { + using std::literals::chrono_literals::operator""s; + RpcMutexUniqueLock _l(cb->mMutex); + cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + } + + EXPECT_EQ(cb->mValues.size(), 1UL) + << "callIsOneway: " << callIsOneway + << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; + if (cb->mValues.empty()) continue; + EXPECT_EQ(cb->mValues.at(0), kTestString) + << "callIsOneway: " << callIsOneway + << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; + + // since we are severing the connection, we need to go ahead and + // tell the server to shutdown and exit so that waitpid won't hang + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + // since this session has an incoming connection w/ a threadpool, we + // need to manually shut it down + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; + } + } + } +} + +TEST_P(BinderRpc, OnewayCallbackWithNoThread) { + auto proc = createRpcTestSocketServerProcess({}); + auto cb = sp<MyBinderRpcCallback>::make(); + + Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything"); + EXPECT_EQ(WOULD_BLOCK, status.transactionError()); +} + +TEST_P(BinderRpc, AidlDelegatorTest) { + auto proc = createRpcTestSocketServerProcess({}); + auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface); + ASSERT_NE(nullptr, myDelegator); + + std::string doubled; + EXPECT_OK(myDelegator->doubleString("cool ", &doubled)); + EXPECT_EQ("cool cool ", doubled); +} + +} // namespace android diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index 4fcf42d6e5..3dab2c748b 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -233,11 +233,15 @@ const std::string kCurrentRepr = "0100000025000000|03000000|00000000|ffffffff|03000000|00000000|00000000|" "07000000020000003a0044000000000000000000|f8ffffff020000003a002f00000000000000000008000000"; +TEST(RpcWire, V0) { + checkRepr(kCurrentRepr, 0); +} + TEST(RpcWire, CurrentVersion) { checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION); } -static_assert(RPC_WIRE_PROTOCOL_VERSION == 0, +static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL, "If the binder wire protocol is updated, this test should test additional versions. " "The binder wire protocol should only be updated on upstream AOSP."); diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index 3b1faa8c2f..cfaf2a987f 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -249,12 +249,13 @@ Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bo pid_t pid = fork(); if (pid) { /* parent */ - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } else { /* child */ - worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair))); + worker_fx(num, worker_count, iterations, payload_size, cs_pair, + std::move(get<1>(pipe_pair))); /* never get here */ - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } } diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 4330e3e615..25e286ce0c 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -37,17 +37,24 @@ TEST(UtilsHost, ExecuteImmediately) { EXPECT_EQ(result->stdoutStr, "foo\n"); } +template <typename T> +auto millisSince(std::chrono::time_point<T> now) { + auto elapsed = std::chrono::system_clock::now() - now; + return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); +} + TEST(UtilsHost, ExecuteLongRunning) { - auto now = std::chrono::system_clock::now(); + auto start = std::chrono::system_clock::now(); { - std::vector<std::string> args{"sh", "-c", - "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"}; - auto result = execute(std::move(args), [](const CommandResult& commandResult) { + std::vector<std::string> + args{"sh", "-c", "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 100"}; + auto result = execute(std::move(args), [&](const CommandResult& commandResult) { + std::cout << millisSince(start) + << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl; return android::base::EndsWith(commandResult.stdoutStr, "\n"); }); - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); + auto elapsedMs = millisSince(start); EXPECT_GE(elapsedMs, 1000); EXPECT_LT(elapsedMs, 2000); @@ -58,22 +65,21 @@ TEST(UtilsHost, ExecuteLongRunning) { // ~CommandResult() called, child process is killed. // Assert that the second sleep does not finish. - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); - EXPECT_LT(elapsedMs, 2000); + EXPECT_LT(millisSince(start), 2000); } TEST(UtilsHost, ExecuteLongRunning2) { - auto now = std::chrono::system_clock::now(); + auto start = std::chrono::system_clock::now(); { std::vector<std::string> args{"sh", "-c", - "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"}; - auto result = execute(std::move(args), [](const CommandResult& commandResult) { + "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 100"}; + auto result = execute(std::move(args), [&](const CommandResult& commandResult) { + std::cout << millisSince(start) + << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl; return android::base::EndsWith(commandResult.stdoutStr, "\n"); }); - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); + auto elapsedMs = millisSince(start); EXPECT_GE(elapsedMs, 4000); EXPECT_LT(elapsedMs, 6000); @@ -84,9 +90,7 @@ TEST(UtilsHost, ExecuteLongRunning2) { // ~CommandResult() called, child process is killed. // Assert that the second sleep does not finish. - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); - EXPECT_LT(elapsedMs, 6000); + EXPECT_LT(millisSince(start), 6000); } TEST(UtilsHost, KillWithSigKill) { diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index e5d32da4cc..35866adf20 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -7,6 +7,26 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aidl_interface { + name: "binderReadParcelIface", + host_supported: true, + unstable: true, + srcs: [ + "parcelables/EmptyParcelable.aidl", + "parcelables/SingleDataParcelable.aidl", + "parcelables/GenericDataParcelable.aidl", + ], + backend: { + java: { + enabled: true, + platform_apis: true, + }, + rust: { + enabled: true, + }, + }, +} + cc_fuzz { name: "binder_parcel_fuzzer", host_supported: true, @@ -29,6 +49,8 @@ cc_fuzz { "libcutils", "libhidlbase", "liblog", + "binderReadParcelIface-cpp", + "binderReadParcelIface-ndk", ], target: { @@ -59,12 +81,14 @@ cc_fuzz { cc_library_static { name: "libbinder_random_parcel", host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, }, }, srcs: [ + "random_binder.cpp", "random_fd.cpp", "random_parcel.cpp", "libbinder_driver.cpp", diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 47ec776990..768fbe118d 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -16,6 +16,9 @@ #define FUZZ_LOG_TAG "binder" #include "binder.h" +#include "parcelables/EmptyParcelable.h" +#include "parcelables/GenericDataParcelable.h" +#include "parcelables/SingleDataParcelable.h" #include "util.h" #include <android-base/hex.h> @@ -73,20 +76,20 @@ struct BigStruct { uint8_t data[1337]; }; -#define PARCEL_READ_WITH_STATUS(T, FUN) \ - [] (const ::android::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\ - T t{};\ - status_t status = p.FUN(&t);\ - FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\ +#define PARCEL_READ_WITH_STATUS(T, FUN) \ + [](const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ + T t{}; \ + status_t status = p.FUN(&t); \ + FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/; \ } -#define PARCEL_READ_NO_STATUS(T, FUN) \ - [] (const ::android::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\ - T t = p.FUN();\ - (void) t;\ - FUZZ_LOG() << #T " done " /* << " value: " << t*/;\ +#define PARCEL_READ_NO_STATUS(T, FUN) \ + [](const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \ + T t = p.FUN(); \ + (void)t; \ + FUZZ_LOG() << #T " done " /* << " value: " << t*/; \ } #define PARCEL_READ_OPT_STATUS(T, FUN) \ @@ -102,7 +105,9 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, dataPosition), PARCEL_READ_NO_STATUS(size_t, dataCapacity), PARCEL_READ_NO_STATUS(::android::binder::Status, enforceNoDataAvail), - [] (const ::android::Parcel& p, uint8_t pos) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + // aborts on larger values + size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX); FUZZ_LOG() << "about to setDataPosition: " << pos; p.setDataPosition(pos); FUZZ_LOG() << "setDataPosition done"; @@ -111,13 +116,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors), - [] (const ::android::Parcel& p, uint8_t len) { - std::string interface(len, 'a'); + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + std::string interface = provider.ConsumeRandomLengthString(); FUZZ_LOG() << "about to enforceInterface: " << interface; bool b = p.enforceInterface(::android::String16(interface.c_str())); FUZZ_LOG() << "enforced interface: " << b; }, - [] (const ::android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to checkInterface"; android::sp<android::IBinder> aBinder = new android::BBinder(); bool b = p.checkInterface(aBinder.get()); @@ -125,13 +130,16 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { }, PARCEL_READ_NO_STATUS(size_t, objectsCount), PARCEL_READ_NO_STATUS(status_t, errorCheck), - [] (const ::android::Parcel& p, uint8_t len) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + // Read at least a bit. Unbounded allocation would OOM. + size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024); FUZZ_LOG() << "about to read void*"; std::vector<uint8_t> data(len); status_t status = p.read(data.data(), len); FUZZ_LOG() << "read status: " << status; }, - [] (const ::android::Parcel& p, uint8_t len) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + size_t len = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readInplace"; const void* r = p.readInplace(len); FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << (r ? HexString(r, len) : "null"); @@ -149,13 +157,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16), PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16), PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16), - [] (const ::android::Parcel& p, uint8_t /*data*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read c-str"; const char* str = p.readCString(); FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>"); }, PARCEL_READ_OPT_STATUS(android::String8, readString8), - [] (const ::android::Parcel& p, uint8_t /*data*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readString8Inplace"; size_t outLen = 0; const char* str = p.readString8Inplace(&outLen); @@ -165,7 +173,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_OPT_STATUS(android::String16, readString16), PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16), PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16), - [] (const ::android::Parcel& p, uint8_t /*data*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readString16Inplace"; size_t outLen = 0; const char16_t* str = p.readString16Inplace(&outLen); @@ -263,13 +271,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray), #undef COMMA - [] (const android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read flattenable"; ExampleFlattenable f; status_t status = p.read(f); FUZZ_LOG() << "read flattenable: " << status; }, - [] (const android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read lite flattenable"; ExampleLightFlattenable f; status_t status = p.read(f); @@ -284,7 +292,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<BigStruct>>, resizeOutVector), PARCEL_READ_NO_STATUS(int32_t, readExceptionCode), - [] (const android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readNativeHandle"; native_handle_t* t = p.readNativeHandle(); FUZZ_LOG() << "readNativeHandle: " << t; @@ -303,15 +311,16 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector), PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector), - [] (const android::Parcel& p, uint8_t len) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + size_t len = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readBlob"; ::android::Parcel::ReadableBlob blob; status_t status = p.readBlob(len, &blob); FUZZ_LOG() << "readBlob status: " << status; }, - [] (const android::Parcel& p, uint8_t options) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { FUZZ_LOG() << "about to readObject"; - bool nullMetaData = options & 0x1; + bool nullMetaData = provider.ConsumeBool(); const void* obj = static_cast<const void*>(p.readObject(nullMetaData)); FUZZ_LOG() << "readObject: " << obj; }, @@ -319,20 +328,19 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize), // additional parcelable objects defined in libbinder - [] (const ::android::Parcel& p, uint8_t data) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { using ::android::os::ParcelableHolder; using ::android::Parcelable; FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status"; - Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL; - if ( (data & 1) == 1 ) { - stability = Parcelable::Stability::STABILITY_VINTF; - } + Parcelable::Stability stability = provider.ConsumeBool() + ? Parcelable::Stability::STABILITY_LOCAL + : Parcelable::Stability::STABILITY_VINTF; ParcelableHolder t = ParcelableHolder(stability); status_t status = p.readParcelable(&t); FUZZ_LOG() << "ParcelableHolder status: " << status; }, PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable), - [] (const ::android::Parcel& p, uint8_t /* data */) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to call hasFileDescriptorsInRange() with status"; size_t offset = p.readUint32(); size_t length = p.readUint32(); @@ -340,7 +348,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { status_t status = p.hasFileDescriptorsInRange(offset, length, &result); FUZZ_LOG() << " status: " << status << " result: " << result; }, - [] (const ::android::Parcel& p, uint8_t /* data */) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to call compareDataInRange() with status"; size_t thisOffset = p.readUint32(); size_t otherOffset = p.readUint32(); @@ -349,6 +357,24 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result); FUZZ_LOG() << " status: " << status << " result: " << result; }, + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable"; + parcelables::EmptyParcelable emptyParcelable{}; + status_t status = emptyParcelable.readFromParcel(&p); + FUZZ_LOG() << " status: " << status; + }, + [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable"; + parcelables::SingleDataParcelable singleDataParcelable; + status_t status = singleDataParcelable.readFromParcel(&p); + FUZZ_LOG() <<" status: " << status; + }, + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable"; + parcelables::GenericDataParcelable genericDataParcelable; + status_t status = genericDataParcelable.readFromParcel(&p); + FUZZ_LOG() <<" status: " << status; + }, }; // clang-format on #pragma clang diagnostic pop diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 5aeb5cc6f2..08eb27a8a7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -16,6 +16,9 @@ #define FUZZ_LOG_TAG "binder_ndk" #include "binder_ndk.h" +#include "aidl/parcelables/EmptyParcelable.h" +#include "aidl/parcelables/GenericDataParcelable.h" +#include "aidl/parcelables/SingleDataParcelable.h" #include <android/binder_parcel_utils.h> #include <android/binder_parcelable_utils.h> @@ -70,7 +73,7 @@ binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, } #define PARCEL_READ(T, FUN) \ - [](const NdkParcelAdapter& p, uint8_t /*data*/) { \ + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { \ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ T t{}; \ binder_status_t status = FUN(p.aParcel(), &t); \ @@ -80,32 +83,37 @@ binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, // clang-format off std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ // methods from binder_parcel.h - [](const NdkParcelAdapter& p, uint8_t pos) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { + // aborts on larger values + size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX); FUZZ_LOG() << "about to set data position to " << pos; binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos); FUZZ_LOG() << "set data position: " << status; }, - [](const NdkParcelAdapter& p, uint8_t /*data*/) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read status header"; ndk::ScopedAStatus t; binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR()); FUZZ_LOG() << "read status header: " << status; }, - [](const NdkParcelAdapter& p, uint8_t /*data*/) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to getDataSize the parcel"; AParcel_getDataSize(p.aParcel()); FUZZ_LOG() << "getDataSize done"; }, - [](const NdkParcelAdapter& p, uint8_t data) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { FUZZ_LOG() << "about to read a ParcelableHolder"; - ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF}; + ndk::AParcelableHolder ph {provider.ConsumeBool() ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF}; binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph); FUZZ_LOG() << "read the ParcelableHolder: " << status; }, - [](const NdkParcelAdapter& p, uint8_t data) { - FUZZ_LOG() << "about to appendFrom"; + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { + size_t offset = provider.ConsumeIntegral<size_t>(); + size_t pos = provider.ConsumeIntegral<size_t>(); + FUZZ_LOG() << "about to appendFrom " << pos; + // TODO: create random parcel AParcel* parcel = AParcel_create(); - binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data); + binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, offset, pos); AParcel_delete(parcel); FUZZ_LOG() << "appendFrom: " << status; }, @@ -172,5 +180,44 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData), PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData), #undef COMMA + + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable"; + aidl::parcelables::EmptyParcelable emptyParcelable; + binder_status_t status = emptyParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable"; + aidl::parcelables::SingleDataParcelable singleDataParcelable; + binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable"; + aidl::parcelables::GenericDataParcelable genericDataParcelable; + binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { + FUZZ_LOG() << "about to marshal AParcel"; + size_t start = provider.ConsumeIntegral<size_t>(); + // limit 1MB to avoid OOM issues + size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1000000); + uint8_t buffer[len]; + binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) { + FUZZ_LOG() << "about to unmarshal AParcel"; + size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()); + std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len); + const uint8_t* buffer = parcelData.data(); + const size_t bufferLen = parcelData.size(); + NdkParcelAdapter adapter; + binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen); + FUZZ_LOG() << "status: " << status; + }, + }; // clang-format on diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h index cf24ab932b..d19f25bc88 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h @@ -18,29 +18,31 @@ #include <android/binder_auto_utils.h> #include <vector> +#include <android/binder_libbinder.h> #include <android/binder_parcel.h> #include "parcel_fuzzer.h" -// libbinder_ndk doesn't export this header which breaks down its API for NDK -// and APEX users, but we need access to it to fuzz. -#include "../../ndk/parcel_internal.h" - class NdkParcelAdapter { public: - NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {} + NdkParcelAdapter() : mParcel(AParcel_create()) {} const AParcel* aParcel() const { return mParcel.get(); } AParcel* aParcel() { return mParcel.get(); } - android::Parcel* parcel() { return aParcel()->get(); } + const android::Parcel* parcel() const { return AParcel_viewPlatformParcel(aParcel()); } + android::Parcel* parcel() { return AParcel_viewPlatformParcel(aParcel()); } - const uint8_t* data() const { return aParcel()->get()->data(); } - size_t dataSize() const { return aParcel()->get()->dataSize(); } - size_t dataAvail() const { return aParcel()->get()->dataAvail(); } - size_t dataPosition() const { return aParcel()->get()->dataPosition(); } - size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); } + const uint8_t* data() const { return parcel()->data(); } + size_t dataSize() const { return parcel()->dataSize(); } + size_t dataAvail() const { return parcel()->dataAvail(); } + size_t dataPosition() const { return parcel()->dataPosition(); } + size_t dataCapacity() const { return parcel()->dataCapacity(); } android::status_t setData(const uint8_t* buffer, size_t len) { - return aParcel()->get()->setData(buffer, len); + return parcel()->setData(buffer, len); + } + + android::status_t appendFrom(const NdkParcelAdapter* parcel, int32_t start, int32_t len) { + return AParcel_appendFrom(parcel->aParcel(), aParcel(), start, len); } private: diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp index ee9840f133..438e8ae9b2 100644 --- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp +++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp @@ -35,19 +35,19 @@ std::ostream& operator<<(std::ostream& os, const ::android::sp<::android::hardwa #define PARCEL_READ_OPT_STATUS(T, FUN) \ PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN) -#define PARCEL_READ_NO_STATUS(T, FUN) \ - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\ - T t = p.FUN();\ - FUZZ_LOG() << #T " value: " << t;\ +#define PARCEL_READ_NO_STATUS(T, FUN) \ + [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \ + T t = p.FUN(); \ + FUZZ_LOG() << #T " value: " << t; \ } -#define PARCEL_READ_WITH_STATUS(T, FUN) \ - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\ - T t;\ - status_t status = p.FUN(&t);\ - FUZZ_LOG() << #T " status: " << status << " value: " << t;\ +#define PARCEL_READ_WITH_STATUS(T, FUN) \ + [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ + T t; \ + status_t status = p.FUN(&t); \ + FUZZ_LOG() << #T " status: " << status << " value: " << t; \ } // clang-format off @@ -56,27 +56,30 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI PARCEL_READ_NO_STATUS(size_t, dataAvail), PARCEL_READ_NO_STATUS(size_t, dataPosition), PARCEL_READ_NO_STATUS(size_t, dataCapacity), - [] (const ::android::hardware::Parcel& p, uint8_t pos) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + // aborts on larger values + size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX); FUZZ_LOG() << "about to setDataPosition: " << pos; p.setDataPosition(pos); FUZZ_LOG() << "setDataPosition done"; }, - [] (const ::android::hardware::Parcel& p, uint8_t length) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { FUZZ_LOG() << "about to enforceInterface"; - std::string interfaceName(length, 'a'); - bool okay = p.enforceInterface(interfaceName.c_str()); + bool okay = p.enforceInterface(provider.ConsumeRandomLengthString().c_str()); FUZZ_LOG() << "enforceInterface status: " << okay; }, PARCEL_READ_NO_STATUS(size_t, objectsCount), - [] (const ::android::hardware::Parcel& p, uint8_t length) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + // Read at least a bit. Unbounded allocation would OOM. + size_t length = provider.ConsumeIntegralInRange<size_t>(0, 1024); FUZZ_LOG() << "about to read"; std::vector<uint8_t> data (length); status_t status = p.read(data.data(), length); FUZZ_LOG() << "read status: " << status << " data: " << HexString(data.data(), data.size()); }, - [] (const ::android::hardware::Parcel& p, uint8_t length) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t length = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to read"; - std::vector<uint8_t> data (length); const void* inplace = p.readInplace(length); FUZZ_LOG() << "read status: " << (inplace ? HexString(inplace, length) : "null"); }, @@ -91,14 +94,14 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI PARCEL_READ_OPT_STATUS(float, readFloat), PARCEL_READ_OPT_STATUS(double, readDouble), PARCEL_READ_OPT_STATUS(bool, readBool), - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readCString"; const char* str = p.readCString(); FUZZ_LOG() << "readCString " << (str ? str : "<null>"); }, PARCEL_READ_OPT_STATUS(::android::String16, readString16), PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16), - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readString16Inplace"; size_t outSize = 0; const char16_t* str = p.readString16Inplace(&outSize); @@ -106,7 +109,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI }, PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder), PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder), - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readBuffer"; size_t handle = 0; const void* data = nullptr; @@ -116,7 +120,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readNullableBuffer"; size_t handle = 0; const void* data = nullptr; @@ -126,7 +131,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readEmbeddedBuffer"; size_t handle = 0; size_t parent_buffer_handle = 0; @@ -138,7 +144,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readNullableEmbeddedBuffer"; size_t handle = 0; size_t parent_buffer_handle = 0; @@ -150,7 +157,7 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readNativeHandleNoDup"; const native_handle_t* handle = nullptr; status_t status = p.readNativeHandleNoDup(&handle); diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h new file mode 100644 index 0000000000..8fc9263b98 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { + +// Get a random binder object for use in fuzzing. +// +// May return nullptr. +sp<IBinder> getRandomBinder(FuzzedDataProvider* provider); + +} // namespace android 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 843b6e3011..6ea970825b 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 @@ -19,10 +19,14 @@ #include <android-base/unique_fd.h> #include <fuzzer/FuzzedDataProvider.h> +#include <vector> + namespace android { // always valid or aborts // get a random FD for use in fuzzing, of a few different specific types -base::unique_fd getRandomFd(FuzzedDataProvider* provider); +// +// may return multiple FDs (e.g. pipe), but will always return at least one +std::vector<base::unique_fd> 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 459fb127c2..27587a9162 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 @@ -33,10 +33,13 @@ struct RandomParcelOptions { /** * Fill parcel data, including some random binder objects and FDs * + * May insert additional FDs/binders if they own data related to the Parcel (e.g. the other + * end of a pipe). + * * p - the Parcel to fill * provider - takes ownership and completely consumes provider * writeHeader - optional function to write a specific header once the format of the parcel is * picked (for instance, to write an interface header) */ -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {}); +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index d5aa353af0..8bef33f2ca 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -17,6 +17,10 @@ #include <fuzzbinder/random_parcel.h> +#include <android-base/logging.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> + namespace android { void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { @@ -27,10 +31,19 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { .extraFds = {}, }; + if (provider.ConsumeBool()) { + // set calling uid + IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>()); + } + while (provider.remaining_bytes() > 0) { - uint32_t code = provider.ConsumeIntegral<uint32_t>(); + // Most of the AIDL services will have small set of transaction codes. + uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>() + : provider.ConsumeIntegralInRange<uint32_t>(0, 100); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; + // for increased fuzz coverage + data.setEnforceNoDataAvail(provider.ConsumeBool()); sp<IBinder> target = options.extraBinders.at( provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1)); @@ -44,9 +57,11 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); - fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options); + fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options); Parcel reply; + // for increased fuzz coverage + reply.setEnforceNoDataAvail(provider.ConsumeBool()); (void)target->transact(code, data, &reply, flags); // feed back in binders and fds that are returned from the service, so that @@ -60,6 +75,15 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { options.extraFds.push_back(base::unique_fd(dup(retFds[i]))); } } + + // invariants + + auto ps = ProcessState::selfOrNull(); + if (ps) { + CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount()) + << "Binder threadpool should not be started by fuzzer because coverage can only " + "cover in-process calls."; + } } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp index 462ef9a5e9..a1fb70131e 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp @@ -29,3 +29,12 @@ void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { } } // namespace android + +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<AIBinder*>(binder); + FuzzedDataProvider provider(data, len); + android::fuzzService(aiBinder, std::move(provider)); +} +} // extern "C" diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index f435dae659..bef4ab6e99 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -35,17 +35,22 @@ #include <sys/time.h> using android::fillRandomParcel; +using android::RandomParcelOptions; using android::sp; using android::base::HexString; -void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) { +void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { // TODO: functionality to create random parcels for libhwbinder parcels + (void)options; + std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>(); p->setData(input.data(), input.size()); } -static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) { +static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { // fill underlying parcel using functions to fill random libbinder parcel - fillRandomParcel(p->parcel(), std::move(provider)); + fillRandomParcel(p->parcel(), std::move(provider), options); } template <typename P, typename B> @@ -55,9 +60,11 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider FUZZ_LOG() << "backend: " << backend; + RandomParcelOptions options; + P reply; P data; - fillRandomParcel(&data, std::move(provider)); + fillRandomParcel(&data, std::move(provider), &options); (void)binder->transact(code, data, &reply, flag); } @@ -73,8 +80,10 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, maxInstructions)); + RandomParcelOptions options; + P p; - fillRandomParcel(&p, std::move(provider)); + fillRandomParcel(&p, std::move(provider), &options); // since we are only using a byte to index CHECK(reads.size() <= 255) << reads.size(); @@ -83,22 +92,39 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize()); FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size()); - for (size_t i = 0; i + 1 < instructions.size(); i += 2) { - uint8_t a = instructions[i]; - uint8_t readIdx = a % reads.size(); + FuzzedDataProvider instructionsProvider(instructions.data(), instructions.size()); + while (instructionsProvider.remaining_bytes() > 0) { + uint8_t idx = instructionsProvider.ConsumeIntegralInRange<uint8_t>(0, reads.size() - 1); - uint8_t b = instructions[i + 1]; + FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail() + << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity(); - FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2 - << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx) - << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize() - << " avail: " << p.dataAvail() << " pos: " << p.dataPosition() - << " cap: " << p.dataCapacity(); - - reads[readIdx](p, b); + reads[idx](p, instructionsProvider); } } +// Append two random parcels. +template <typename P> +void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) { + int32_t start = provider.ConsumeIntegral<int32_t>(); + int32_t len = provider.ConsumeIntegral<int32_t>(); + + std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>( + provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); + + // same options so that FDs and binders could be shared in both Parcels + RandomParcelOptions options; + + P p0, p1; + fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options); + fillRandomParcel(&p1, std::move(provider), &options); + + FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "start: " << start << " len: " << len; + + p0.appendFrom(&p1, start, len); +} + void* NothingClass_onCreate(void* args) { return args; } @@ -148,6 +174,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS, std::move(provider)); }, + [](FuzzedDataProvider&& provider) { + doAppendFuzz<::android::Parcel>("binder", std::move(provider)); + }, + [](FuzzedDataProvider&& provider) { + doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider)); + }, }; provider.PickValueInArray(fuzzBackend)(std::move(provider)); diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h index b68a8a91e6..765a93e8c9 100644 --- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h +++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h @@ -15,5 +15,9 @@ */ #pragma once +#include <fuzzer/FuzzedDataProvider.h> + +#include <functional> + template <typename P> -using ParcelRead = std::function<void(const P& p, uint8_t data)>; +using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>; diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl new file mode 100644 index 0000000000..1216250cd9 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl @@ -0,0 +1,18 @@ +/* + * 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. + */ +package parcelables; +parcelable EmptyParcelable { +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl new file mode 100644 index 0000000000..f1079e9047 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelables; + +parcelable GenericDataParcelable { + int data; + float majorVersion; + float minorVersion; + IBinder binder; + ParcelFileDescriptor fileDescriptor; + int[] array; +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl new file mode 100644 index 0000000000..0187168d89 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelables; + +parcelable SingleDataParcelable{ + int data; +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/random_binder.cpp b/libs/binder/tests/parcel_fuzzer/random_binder.cpp new file mode 100644 index 0000000000..8a1fecb212 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/random_binder.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <fuzzbinder/random_binder.h> + +#include <fuzzbinder/random_parcel.h> + +#include <android-base/logging.h> +#include <binder/IInterface.h> +#include <binder/IServiceManager.h> + +namespace android { + +class RandomBinder : public BBinder { +public: + RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes) + : mDescriptor(descriptor), + mBytes(std::move(bytes)), + mProvider(mBytes.data(), mBytes.size()) {} + const String16& getInterfaceDescriptor() const override { return mDescriptor; } + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override { + (void)code; + (void)data; + (void)reply; + (void)flags; // note - for maximum coverage even ignore if oneway + + if (mProvider.ConsumeBool()) { + return mProvider.ConsumeIntegral<status_t>(); + } + + if (reply == nullptr) return OK; + + // TODO: things we could do to increase state space + // - also pull FDs and binders from 'data' + // (optionally combine these into random parcel 'options') + // - also pull FDs and binders from random parcel 'options' + RandomParcelOptions options; + + // random output + std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>( + mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes())); + fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options); + + return OK; + } + +private: + String16 mDescriptor; + + // note may not all be used + std::vector<uint8_t> mBytes; + FuzzedDataProvider mProvider; +}; + +sp<IBinder> getRandomBinder(FuzzedDataProvider* provider) { + auto makeFunc = provider->PickValueInArray<const std::function<sp<IBinder>()>>({ + [&]() { + // descriptor is the length of a class name, e.g. + // "some.package.Foo" + std::string str = provider->ConsumeRandomLengthString(100 /*max length*/); + + // arbitrarily consume remaining data to create a binder that can return + // random results - coverage guided fuzzer should ensure all of the remaining + // data isn't always used + std::vector<uint8_t> bytes = provider->ConsumeBytes<uint8_t>( + provider->ConsumeIntegralInRange<size_t>(0, provider->remaining_bytes())); + + return new RandomBinder(String16(str.c_str()), std::move(bytes)); + }, + []() { + // this is the easiest remote binder to get ahold of, and it + // should be able to handle anything thrown at it, and + // essentially every process can talk to it, so it's a good + // candidate for checking usage of an actual BpBinder + return IInterface::asBinder(defaultServiceManager()); + }, + [&]() -> sp<IBinder> { return nullptr; }, + }); + return makeFunc(); +} + +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index ab0b7e306f..e4dbb2d955 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,13 +23,50 @@ namespace android { -base::unique_fd getRandomFd(FuzzedDataProvider* provider) { - int fd = provider->PickValueInArray<std::function<int()>>({ - []() { return ashmem_create_region("binder test region", 1024); }, - []() { return open("/dev/null", O_RDWR); }, +using base::unique_fd; + +std::vector<unique_fd> getRandomFds(FuzzedDataProvider* provider) { + const char* fdType; + + std::vector<unique_fd> fds = provider->PickValueInArray< + std::function<std::vector<unique_fd>()>>({ + [&]() { + fdType = "ashmem"; + std::vector<unique_fd> ret; + ret.push_back(unique_fd( + ashmem_create_region("binder test region", + provider->ConsumeIntegralInRange<size_t>(0, 4096)))); + return ret; + }, + [&]() { + fdType = "/dev/null"; + std::vector<unique_fd> 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<unique_fd> ret; + ret.push_back(unique_fd(pipefds[0])); + ret.push_back(unique_fd(pipefds[1])); + return ret; + }, })(); - CHECK(fd >= 0); - return base::unique_fd(fd); + + for (const auto& fd : fds) CHECK(fd.ok()) << fd.get() << " " << fdType; + + return fds; } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 0204f5ed61..f0beed234b 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -17,45 +17,42 @@ #include <fuzzbinder/random_parcel.h> #include <android-base/logging.h> -#include <binder/IServiceManager.h> #include <binder/RpcSession.h> #include <binder/RpcTransportRaw.h> +#include <fuzzbinder/random_binder.h> #include <fuzzbinder/random_fd.h> #include <utils/String16.h> namespace android { -class NamedBinder : public BBinder { -public: - NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {} - const String16& getInterfaceDescriptor() const override { return mDescriptor; } - -private: - String16 mDescriptor; -}; - static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes()); CHECK(OK == p->write(data.data(), data.size())); } -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, - const RandomParcelOptions& options) { +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) { + CHECK_NE(options, nullptr); + if (provider.ConsumeBool()) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); + // Set the protocol version so that we don't crash if the session + // actually gets used. This isn't cheating because the version should + // always be set if the session init succeeded and we aren't testing the + // session init here (it is bypassed by addNullDebuggingClient). + session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION); p->markForRpc(session); - if (options.writeHeader) { - options.writeHeader(p, provider); + if (options->writeHeader) { + options->writeHeader(p, provider); } fillRandomParcelData(p, std::move(provider)); return; } - if (options.writeHeader) { - options.writeHeader(p, provider); + if (options->writeHeader) { + options->writeHeader(p, provider); } while (provider.remaining_bytes() > 0) { @@ -69,45 +66,40 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, }, // write FD [&]() { - if (options.extraFds.size() > 0 && provider.ConsumeBool()) { - const base::unique_fd& fd = options.extraFds.at( + if (options->extraFds.size() > 0 && provider.ConsumeBool()) { + const base::unique_fd& fd = options->extraFds.at( provider.ConsumeIntegralInRange<size_t>(0, - options.extraFds.size() - + options->extraFds.size() - 1)); CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/)); } else { - base::unique_fd fd = getRandomFd(&provider); - CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/)); + // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion + if (options->extraFds.size() > 1000) { + return; + } + + std::vector<base::unique_fd> 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())); } }, // write binder [&]() { - auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({ - [&]() { - // descriptor is the length of a class name, e.g. - // "some.package.Foo" - std::string str = - provider.ConsumeRandomLengthString(100 /*max length*/); - return new NamedBinder(String16(str.c_str())); - }, - []() { - // this is the easiest remote binder to get ahold of, and it - // should be able to handle anything thrown at it, and - // essentially every process can talk to it, so it's a good - // candidate for checking usage of an actual BpBinder - return IInterface::asBinder(defaultServiceManager()); - }, - [&]() -> sp<IBinder> { - if (options.extraBinders.size() > 0 && provider.ConsumeBool()) { - return options.extraBinders.at( - provider.ConsumeIntegralInRange< - size_t>(0, options.extraBinders.size() - 1)); - } else { - return nullptr; - } - }, - }); - sp<IBinder> binder = makeFunc(); + sp<IBinder> binder; + if (options->extraBinders.size() > 0 && provider.ConsumeBool()) { + binder = options->extraBinders.at( + provider.ConsumeIntegralInRange<size_t>(0, + options->extraBinders + .size() - + 1)); + } else { + binder = getRandomBinder(&provider); + } CHECK(OK == p->writeStrongBinder(binder)); }, }); diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp new file mode 100644 index 0000000000..b48dc27241 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp @@ -0,0 +1,24 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_static { + name: "libbinder_create_parcel", + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, + srcs: [ + "RandomParcelWrapper.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + ], + static_libs: [ + "libbinder_random_parcel", + ], +} diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp new file mode 100644 index 0000000000..2fb7820b0b --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <android/binder_libbinder.h> +#include <android/binder_parcel.h> +#include <fuzzbinder/random_parcel.h> + +extern "C" { + +void createRandomParcel(void* aParcel, const uint8_t* data, size_t len) { + CHECK_NE(aParcel, nullptr); + AParcel* parcel = static_cast<AParcel*>(aParcel); + FuzzedDataProvider provider(data, len); + android::RandomParcelOptions options; + + android::Parcel* platformParcel = AParcel_viewPlatformParcel(parcel); + fillRandomParcel(platformParcel, std::move(provider), &options); +} + +} // extern "C"
\ No newline at end of file diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp index a8713a24d6..b8ae84d5c0 100644 --- a/libs/binder/tests/rpc_fuzzer/main.cpp +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -133,8 +133,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 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; + while (provider.remaining_bytes() > 0) { - if (connections.empty() || provider.ConsumeBool()) { + if (connections.empty() || + (connections.size() < kMaxConnections && provider.ConsumeBool())) { base::unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); CHECK_NE(fd.get(), -1); CHECK_EQ(0, @@ -157,8 +162,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } } - usleep(10000); - if (hangupBeforeShutdown) { connections.clear(); while (!server->listSessions().empty() || server->numUninitializedSessions()) { @@ -167,6 +170,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } } + while (server->hasActiveRequests()) { + usleep(10); + } + while (!server->shutdown()) usleep(1); serverThread.join(); diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index 56d958c6be..0035e4ee5a 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp @@ -398,14 +398,13 @@ Pipe make_process(int num, int iterations, int no_process, int payload_size) { pid_t pid = fork(); if (pid) { // parent - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } else { // child thread_dump(is_client(num) ? "client" : "server"); - worker_fx(num, no_process, iterations, payload_size, - move(get<1>(pipe_pair))); + worker_fx(num, no_process, iterations, payload_size, std::move(get<1>(pipe_pair))); // never get here - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } } diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp index e77c55c669..910c9dc25c 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp @@ -49,6 +49,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { }); sp<RpcSession> session = RpcSession::make(); + session->setMaxIncomingThreads(1); status_t status; for (size_t tries = 0; tries < 5; tries++) { usleep(10000); diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp new file mode 100644 index 0000000000..8ec982345d --- /dev/null +++ b/libs/binder/trusty/OS.cpp @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#if defined(TRUSTY_USERSPACE) +#include <openssl/rand.h> +#include <trusty_ipc.h> +#else +#include <lib/rand/rand.h> +#endif + +#include <binder/RpcTransportTipcTrusty.h> + +#include "../OS.h" +#include "TrustyStatus.h" + +using android::base::Result; + +namespace android { + +Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) { + // Trusty IPC syscalls are all non-blocking by default. + return {}; +} + +status_t getRandomBytes(uint8_t* data, size_t size) { +#if defined(TRUSTY_USERSPACE) + int res = RAND_bytes(data, size); + return res == 1 ? OK : UNKNOWN_ERROR; +#else + int res = rand_get_bytes(data, size); + return res == 0 ? OK : UNKNOWN_ERROR; +#endif // TRUSTY_USERSPACE +} + +status_t dupFileDescriptor(int oldFd, int* newFd) { + int res = dup(oldFd); + if (res < 0) { + return statusFromTrusty(res); + } + + *newFd = res; + return OK; +} + +std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() { + return RpcTransportCtxFactoryTipcTrusty::make(); +} + +ssize_t sendMessageOnSocket( + const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) { + errno = ENOTSUP; + return -1; +} + +ssize_t receiveMessageFromSocket( + const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) { + errno = ENOTSUP; + return -1; +} + +} // namespace android diff --git a/libs/binder/trusty/README.md b/libs/binder/trusty/README.md new file mode 100644 index 0000000000..8a60af8b23 --- /dev/null +++ b/libs/binder/trusty/README.md @@ -0,0 +1,45 @@ +# Binder for Trusty + +This is the Trusty port of the libbinder library. +To build it, first you will need a checkout of the Trusty tree: +```shell +$ mkdir /path/to/trusty +$ cd /path/to/trusty +$ repo init -u https://android.googlesource.com/trusty/manifest -b master +$ repo sync -j$(nproc) -c --no-tags +``` + +After the checkout is complete, you can use the `build.py` script for both +building and testing Trusty. For a quick build without any tests, run: +```shell +$ ./trusty/vendor/google/aosp/scripts/build.py generic-arm64-test-debug +``` +This will build the smaller `generic-arm64-test-debug` project which +does not run any tests. + +The qemu-generic-arm64-test-debug` project includes the QEMU emulator and +a full Trusty test suite, including a set of libbinder tests. +To run the latter, use the command: +```shell +$ ./trusty/vendor/google/aosp/scripts/build.py \ + --test "boot-test:com.android.trusty.binder.test" \ + qemu-generic-arm64-test-debug +``` + +## Building AIDL files on Trusty +To compile AIDL interfaces into Trusty libraries, include the `make/aidl.mk` +in your `rules.mk` file, e.g.: +``` +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_AIDLS := \ + $(LOCAL_DIR)/IFoo.aidl \ + +include make/aidl.mk +``` + +## Examples +The Trusty tree contains some sample test apps at +`trusty/user/app/sample/binder-test`. diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp new file mode 100644 index 0000000000..109da7509a --- /dev/null +++ b/libs/binder/trusty/RpcServerTrusty.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcServerTrusty" + +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/RpcServerTrusty.h> +#include <binder/RpcThreads.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <log/log.h> + +#include "../FdTrigger.h" +#include "../RpcState.h" +#include "TrustyStatus.h" + +using android::base::unexpected; + +namespace android { + +android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make( + tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, + size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { + // Default is without TLS. + if (rpcTransportCtxFactory == nullptr) + rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make(); + auto ctx = rpcTransportCtxFactory->newServerCtx(); + if (ctx == nullptr) { + return unexpected(ERR_NO_MEMORY); + } + + auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl), + msgMaxSize); + if (srv == nullptr) { + return unexpected(ERR_NO_MEMORY); + } + + int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps); + if (rc != NO_ERROR) { + return unexpected(rc); + } + return srv; +} + +RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, + std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize) + : mRpcServer(sp<RpcServer>::make(std::move(ctx))), + mPortName(std::move(portName)), + mPortAcl(std::move(portAcl)) { + mTipcPort.name = mPortName.c_str(); + mTipcPort.msg_max_size = msgMaxSize; + mTipcPort.msg_queue_len = 6; // Three each way + mTipcPort.priv = this; + + if (mPortAcl) { + // Initialize the array of pointers to uuids. + // The pointers in mUuidPtrs should stay valid across moves of + // RpcServerTrusty (the addresses of a std::vector's elements + // shouldn't change when the vector is moved), but would be invalidated + // by a copy which is why we disable the copy constructor and assignment + // operator for RpcServerTrusty. + auto numUuids = mPortAcl->uuids.size(); + mUuidPtrs.resize(numUuids); + for (size_t i = 0; i < numUuids; i++) { + mUuidPtrs[i] = &mPortAcl->uuids[i]; + } + + // Copy the contents of portAcl into the tipc_port_acl structure that we + // pass to tipc_add_service + mTipcPortAcl.flags = mPortAcl->flags; + mTipcPortAcl.uuid_num = numUuids; + mTipcPortAcl.uuids = mUuidPtrs.data(); + mTipcPortAcl.extra_data = mPortAcl->extraData; + + mTipcPort.acl = &mTipcPortAcl; + } else { + mTipcPort.acl = nullptr; + } +} + +int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, + void** ctx_p) { + auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv)); + server->mRpcServer->mShutdownTrigger = FdTrigger::make(); + server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread(); + + int rc = NO_ERROR; + auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) { + if (result.status != OK) { + rc = statusToTrusty(result.status); + return; + } + + /* Save the session and connection for the other callbacks */ + auto* channelContext = new (std::nothrow) ChannelContext; + if (channelContext == nullptr) { + rc = ERR_NO_MEMORY; + return; + } + + channelContext->session = std::move(session); + channelContext->connection = std::move(result.connection); + + *ctx_p = channelContext; + }; + + // We need to duplicate the channel handle here because the tipc library + // owns the original handle and closes is automatically on channel cleanup. + // We use dup() because Trusty does not have fcntl(). + // NOLINTNEXTLINE(android-cloexec-dup) + handle_t chanDup = dup(chan); + if (chanDup < 0) { + return chanDup; + } + base::unique_fd clientFd(chanDup); + android::RpcTransportFd transportFd(std::move(clientFd)); + + std::array<uint8_t, RpcServer::kRpcAddressSize> addr; + constexpr size_t addrLen = sizeof(*peer); + memcpy(addr.data(), peer, addrLen); + RpcServer::establishConnection(sp(server->mRpcServer), std::move(transportFd), addr, addrLen, + joinFn); + + return rc; +} + +int RpcServerTrusty::handleMessage(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) { + auto* channelContext = reinterpret_cast<ChannelContext*>(ctx); + LOG_ALWAYS_FATAL_IF(channelContext == nullptr, + "bad state: message received on uninitialized channel"); + + auto& session = channelContext->session; + auto& connection = channelContext->connection; + status_t status = + session->state()->drainCommands(connection, session, RpcState::CommandType::ANY); + if (status != OK) { + LOG_RPC_DETAIL("Binder connection thread closing w/ status %s", + statusToString(status).c_str()); + } + + return NO_ERROR; +} + +void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/, + void* /*ctx*/) {} + +void RpcServerTrusty::handleChannelCleanup(void* ctx) { + auto* channelContext = reinterpret_cast<ChannelContext*>(ctx); + if (channelContext == nullptr) { + return; + } + + auto& session = channelContext->session; + auto& connection = channelContext->connection; + LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection), + "bad state: connection object guaranteed to be in list"); + + delete channelContext; +} + +} // namespace android diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp new file mode 100644 index 0000000000..d249b2e1eb --- /dev/null +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcTransportTipcTrusty" + +#include <inttypes.h> +#include <trusty_ipc.h> + +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <log/log.h> + +#include "../FdTrigger.h" +#include "../RpcState.h" +#include "TrustyStatus.h" + +namespace android { + +namespace { + +// RpcTransport for Trusty. +class RpcTransportTipcTrusty : public RpcTransport { +public: + explicit RpcTransportTipcTrusty(android::RpcTransportFd socket) : mSocket(std::move(socket)) {} + ~RpcTransportTipcTrusty() { releaseMessage(); } + + status_t pollRead() override { + auto status = ensureMessage(false); + if (status != OK) { + return status; + } + return mHaveMessage ? OK : WOULD_BLOCK; + } + + status_t interruptableWriteFully( + FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + if (niovs < 0) { + return BAD_VALUE; + } + + size_t size = 0; + for (int i = 0; i < niovs; i++) { + size += iovs[i].iov_len; + } + + handle_t msgHandles[IPC_MAX_MSG_HANDLES]; + ipc_msg_t msg{ + .num_iov = static_cast<uint32_t>(niovs), + .iov = iovs, + .num_handles = 0, + .handles = nullptr, + }; + + if (ancillaryFds != nullptr && !ancillaryFds->empty()) { + if (ancillaryFds->size() > IPC_MAX_MSG_HANDLES) { + // This shouldn't happen because we check the FD count in RpcState. + ALOGE("Saw too many file descriptors in RpcTransportCtxTipcTrusty: " + "%zu (max is %u). Aborting session.", + ancillaryFds->size(), IPC_MAX_MSG_HANDLES); + return BAD_VALUE; + } + + for (size_t i = 0; i < ancillaryFds->size(); i++) { + msgHandles[i] = + std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i)); + } + + msg.num_handles = ancillaryFds->size(); + msg.handles = msgHandles; + } + + ssize_t rc = send_msg(mSocket.fd.get(), &msg); + if (rc == ERR_NOT_ENOUGH_BUFFER) { + // Peer is blocked, wait until it unblocks. + // TODO: when tipc supports a send-unblocked handler, + // save the message here in a queue and retry it asynchronously + // when the handler gets called by the library + uevent uevt; + do { + rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME); + if (rc < 0) { + return statusFromTrusty(rc); + } + if (uevt.event & IPC_HANDLE_POLL_HUP) { + return DEAD_OBJECT; + } + } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); + + // Retry the send, it should go through this time because + // sending is now unblocked + rc = send_msg(mSocket.fd.get(), &msg); + } + if (rc < 0) { + return statusFromTrusty(rc); + } + LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size, + "Sent the wrong number of bytes %zd!=%zu", rc, size); + + return OK; + } + + status_t interruptableReadFully( + FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { + if (niovs < 0) { + return BAD_VALUE; + } + + // If iovs has one or more empty vectors at the end and + // we somehow advance past all the preceding vectors and + // pass some or all of the empty ones to sendmsg/recvmsg, + // the call will return processSize == 0. In that case + // we should be returning OK but instead return DEAD_OBJECT. + // To avoid this problem, we make sure here that the last + // vector at iovs[niovs - 1] has a non-zero length. + while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { + niovs--; + } + if (niovs == 0) { + // The vectors are all empty, so we have nothing to read. + return OK; + } + + while (true) { + auto status = ensureMessage(true); + if (status != OK) { + return status; + } + + LOG_ALWAYS_FATAL_IF(mMessageInfo.num_handles > IPC_MAX_MSG_HANDLES, + "Received too many handles %" PRIu32, mMessageInfo.num_handles); + bool haveHandles = mMessageInfo.num_handles != 0; + handle_t msgHandles[IPC_MAX_MSG_HANDLES]; + + ipc_msg_t msg{ + .num_iov = static_cast<uint32_t>(niovs), + .iov = iovs, + .num_handles = mMessageInfo.num_handles, + .handles = haveHandles ? msgHandles : 0, + }; + ssize_t rc = read_msg(mSocket.fd.get(), mMessageInfo.id, mMessageOffset, &msg); + if (rc < 0) { + return statusFromTrusty(rc); + } + + size_t processSize = static_cast<size_t>(rc); + mMessageOffset += processSize; + LOG_ALWAYS_FATAL_IF(mMessageOffset > mMessageInfo.len, + "Message offset exceeds length %zu/%zu", mMessageOffset, + mMessageInfo.len); + + if (haveHandles) { + 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])); + } + + // Clear the saved number of handles so we don't accidentally + // read them multiple times + mMessageInfo.num_handles = 0; + haveHandles = false; + } else { + ALOGE("Received unexpected handles %" PRIu32, mMessageInfo.num_handles); + // It should be safe to continue here. We could abort, but then + // peers could DoS us by sending messages with handles in them. + // Close the handles since we are ignoring them. + for (size_t i = 0; i < mMessageInfo.num_handles; i++) { + ::close(msgHandles[i]); + } + } + } + + // Release the message if all of it has been read + if (mMessageOffset == mMessageInfo.len) { + releaseMessage(); + } + + while (processSize > 0 && niovs > 0) { + auto& iov = iovs[0]; + if (processSize < iov.iov_len) { + // Advance the base of the current iovec + iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; + iov.iov_len -= processSize; + break; + } + + // The current iovec was fully written + processSize -= iov.iov_len; + iovs++; + niovs--; + } + if (niovs == 0) { + LOG_ALWAYS_FATAL_IF(processSize > 0, + "Reached the end of iovecs " + "with %zd bytes remaining", + processSize); + return OK; + } + } + } + + bool isWaiting() override { return mSocket.isInPollingState(); } + +private: + status_t ensureMessage(bool wait) { + int rc; + if (mHaveMessage) { + LOG_ALWAYS_FATAL_IF(mMessageOffset >= mMessageInfo.len, "No data left in message"); + return OK; + } + + /* TODO: interruptible wait, maybe with a timeout??? */ + uevent uevt; + rc = ::wait(mSocket.fd.get(), &uevt, wait ? INFINITE_TIME : 0); + if (rc < 0) { + if (rc == ERR_TIMED_OUT && !wait) { + // If we timed out with wait==false, then there's no message + return OK; + } + return statusFromTrusty(rc); + } + if (!(uevt.event & IPC_HANDLE_POLL_MSG)) { + /* No message, terminate here and leave mHaveMessage false */ + if (uevt.event & IPC_HANDLE_POLL_HUP) { + // Peer closed the connection. We need to preserve the order + // between MSG and HUP from FdTrigger.cpp, which means that + // getting MSG&HUP should return OK instead of DEAD_OBJECT. + return DEAD_OBJECT; + } + return OK; + } + + rc = get_msg(mSocket.fd.get(), &mMessageInfo); + if (rc < 0) { + return statusFromTrusty(rc); + } + + mHaveMessage = true; + mMessageOffset = 0; + return OK; + } + + void releaseMessage() { + if (mHaveMessage) { + put_msg(mSocket.fd.get(), mMessageInfo.id); + mHaveMessage = false; + } + } + + android::RpcTransportFd mSocket; + + bool mHaveMessage = false; + ipc_msg_info mMessageInfo; + size_t mMessageOffset; +}; + +// RpcTransportCtx for Trusty. +class RpcTransportCtxTipcTrusty : public RpcTransportCtx { +public: + std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, + FdTrigger*) const override { + return std::make_unique<RpcTransportTipcTrusty>(std::move(socket)); + } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } +}; + +} // namespace + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const { + return std::make_unique<RpcTransportCtxTipcTrusty>(); +} + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newClientCtx() const { + return std::make_unique<RpcTransportCtxTipcTrusty>(); +} + +const char* RpcTransportCtxFactoryTipcTrusty::toCString() const { + return "trusty"; +} + +std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcTrusty::make() { + return std::unique_ptr<RpcTransportCtxFactoryTipcTrusty>( + new RpcTransportCtxFactoryTipcTrusty()); +} + +} // namespace android diff --git a/libs/binder/trusty/TrustyStatus.cpp b/libs/binder/trusty/TrustyStatus.cpp new file mode 100644 index 0000000000..b1caf612bc --- /dev/null +++ b/libs/binder/trusty/TrustyStatus.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TrustyStatus.h" +#include "../RpcState.h" + +namespace android { + +status_t statusFromTrusty(int rc) { + LOG_RPC_DETAIL("Trusty error: %d", rc); + switch (rc) { + case NO_ERROR: + return OK; + case ERR_NOT_FOUND: + return NAME_NOT_FOUND; + case ERR_NOT_READY: + // We get this error if we try to perform an IPC operation when the + // channel is not ready + return INVALID_OPERATION; + case ERR_NO_MSG: + return WOULD_BLOCK; + case ERR_NO_MEMORY: + return NO_MEMORY; + case ERR_INVALID_ARGS: + return BAD_VALUE; + case ERR_NOT_ENOUGH_BUFFER: + return WOULD_BLOCK; + case ERR_TIMED_OUT: + return TIMED_OUT; + case ERR_ALREADY_EXISTS: + return ALREADY_EXISTS; + case ERR_CHANNEL_CLOSED: + return DEAD_OBJECT; + case ERR_NOT_ALLOWED: + return INVALID_OPERATION; + case ERR_NOT_SUPPORTED: + return INVALID_OPERATION; + case ERR_TOO_BIG: + return BAD_INDEX; + case ERR_CMD_UNKNOWN: + return UNKNOWN_TRANSACTION; + case ERR_BAD_STATE: + return INVALID_OPERATION; + case ERR_BAD_LEN: + return NOT_ENOUGH_DATA; + case ERR_BAD_HANDLE: + return BAD_VALUE; + case ERR_ACCESS_DENIED: + return PERMISSION_DENIED; + default: + return UNKNOWN_ERROR; + } +} + +int statusToTrusty(status_t status) { + switch (status) { + case OK: + return NO_ERROR; + case NO_MEMORY: + return ERR_NO_MEMORY; + case INVALID_OPERATION: + case BAD_VALUE: + case BAD_TYPE: + return ERR_NOT_VALID; + case NAME_NOT_FOUND: + return ERR_NOT_FOUND; + case PERMISSION_DENIED: + return ERR_ACCESS_DENIED; + case NO_INIT: + return ERR_NOT_CONFIGURED; + case ALREADY_EXISTS: + return ERR_ALREADY_EXISTS; + case DEAD_OBJECT: + return ERR_CHANNEL_CLOSED; + case BAD_INDEX: + return ERR_TOO_BIG; + case NOT_ENOUGH_DATA: + return ERR_BAD_LEN; + case WOULD_BLOCK: + return ERR_NO_MSG; + case TIMED_OUT: + return ERR_TIMED_OUT; + case UNKNOWN_TRANSACTION: + return ERR_CMD_UNKNOWN; + case FDS_NOT_ALLOWED: + return ERR_NOT_SUPPORTED; + case UNEXPECTED_NULL: + return ERR_NOT_VALID; + default: + return ERR_GENERIC; + } +} + +} // namespace android diff --git a/libs/binder/trusty/TrustyStatus.h b/libs/binder/trusty/TrustyStatus.h new file mode 100644 index 0000000000..fcb43f8451 --- /dev/null +++ b/libs/binder/trusty/TrustyStatus.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <uapi/err.h> +#include <utils/Errors.h> + +namespace android { + +status_t statusFromTrusty(int rc); +int statusToTrusty(status_t status); + +} // namespace android diff --git a/libs/binder/trusty/binderRpcTest/aidl/rules.mk b/libs/binder/trusty/binderRpcTest/aidl/rules.mk new file mode 100644 index 0000000000..1afd32487a --- /dev/null +++ b/libs/binder/trusty/binderRpcTest/aidl/rules.mk @@ -0,0 +1,30 @@ +# 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests + +MODULE := $(LOCAL_DIR) + +MODULE_AIDLS := \ + $(LIBBINDER_TESTS_DIR)/BinderRpcTestClientInfo.aidl \ + $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerConfig.aidl \ + $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerInfo.aidl \ + $(LIBBINDER_TESTS_DIR)/IBinderRpcCallback.aidl \ + $(LIBBINDER_TESTS_DIR)/IBinderRpcSession.aidl \ + $(LIBBINDER_TESTS_DIR)/IBinderRpcTest.aidl \ + $(LIBBINDER_TESTS_DIR)/ParcelableCertificateData.aidl \ + +include make/aidl.mk diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json new file mode 100644 index 0000000000..d8b080f0d4 --- /dev/null +++ b/libs/binder/trusty/binderRpcTest/manifest.json @@ -0,0 +1,6 @@ +{ + "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b", + "app_name": "binderRpcTest", + "min_heap": 163840, + "min_stack": 16384 +} diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk new file mode 100644 index 0000000000..ae3949246d --- /dev/null +++ b/libs/binder/trusty/binderRpcTest/rules.mk @@ -0,0 +1,35 @@ +# 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests + +MODULE := $(LOCAL_DIR) + +MANIFEST := $(LOCAL_DIR)/manifest.json + +MODULE_SRCS += \ + $(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \ + $(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \ + $(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \ + +MODULE_LIBRARY_DEPS += \ + $(LOCAL_DIR)/aidl \ + frameworks/native/libs/binder/trusty \ + frameworks/native/libs/binder/trusty/ndk \ + trusty/user/base/lib/googletest \ + trusty/user/base/lib/libstdc++-trusty \ + +include make/trusted_app.mk diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json new file mode 100644 index 0000000000..1c4f7ee0d1 --- /dev/null +++ b/libs/binder/trusty/binderRpcTest/service/manifest.json @@ -0,0 +1,10 @@ +{ + "uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94", + "app_name": "binderRpcTestService", + "min_heap": 65536, + "min_stack": 16384, + "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 new file mode 100644 index 0000000000..5d1a51dca2 --- /dev/null +++ b/libs/binder/trusty/binderRpcTest/service/rules.mk @@ -0,0 +1,33 @@ +# 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests + +MODULE := $(LOCAL_DIR) + +MANIFEST := $(LOCAL_DIR)/manifest.json + +MODULE_SRCS := \ + $(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \ + $(LIBBINDER_TESTS_DIR)/binderRpcTestServiceTrusty.cpp \ + +MODULE_LIBRARY_DEPS := \ + frameworks/native/libs/binder/trusty \ + frameworks/native/libs/binder/trusty/binderRpcTest/aidl \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/tipc \ + +include make/trusted_app.mk diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests new file mode 100644 index 0000000000..d0a1fbca49 --- /dev/null +++ b/libs/binder/trusty/build-config-usertests @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file lists userspace tests + +[ + porttest("com.android.trusty.binderRpcTest"), +] diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h new file mode 100644 index 0000000000..6678eb8fec --- /dev/null +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/expected.h> +#include <android-base/macros.h> +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransport.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <map> +#include <vector> + +#include <lib/tipc/tipc_srv.h> + +namespace android { + +/** + * This is the Trusty-specific RPC server code. + */ +class RpcServerTrusty final : public virtual RefBase { +public: + // C++ equivalent to tipc_port_acl that uses safe data structures instead of + // raw pointers, except for |extraData| which doesn't have a good + // equivalent. + struct PortAcl { + uint32_t flags; + std::vector<const uuid> uuids; + const void* extraData; + }; + + /** + * Creates an RPC server listening on the given port and adds it to the + * Trusty handle set at |handleSet|. + * + * 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<sp<RpcServerTrusty>, int> make( + tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, + size_t msgMaxSize, + std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr); + + void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); } + void setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes) { + mRpcServer->setSupportedFileDescriptorTransportModes(modes); + } + void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); } + void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); } + void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) { + mRpcServer->setPerSessionRootObject(std::move(object)); + } + sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); } + + /** + * For debugging! + */ + std::vector<sp<RpcSession>> listSessions() { return mRpcServer->listSessions(); } + +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); + + friend sp<RpcServerTrusty>; + explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, + std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize); + + // The Rpc-specific context maintained for every open TIPC channel. + struct ChannelContext { + sp<RpcSession> session; + sp<RpcSession::RpcConnection> connection; + }; + + static int handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p); + static int handleMessage(const tipc_port* port, handle_t chan, void* ctx); + static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx); + static void handleChannelCleanup(void* ctx); + + static constexpr tipc_srv_ops kTipcOps = { + .on_connect = &handleConnect, + .on_message = &handleMessage, + .on_disconnect = &handleDisconnect, + .on_channel_cleanup = &handleChannelCleanup, + }; + + sp<RpcServer> mRpcServer; + std::string mPortName; + std::shared_ptr<const PortAcl> mPortAcl; + std::vector<const uuid*> mUuidPtrs; + tipc_port_acl mTipcPortAcl; + tipc_port mTipcPort; +}; + +} // namespace android diff --git a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h new file mode 100644 index 0000000000..8eae8c2ec1 --- /dev/null +++ b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Wraps the transport layer of RPC. Implementation uses plain sockets. +// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx. + +#pragma once + +#include <memory> + +#include <binder/RpcTransport.h> + +namespace android { + +// RpcTransportCtxFactory with TLS disabled. +class RpcTransportCtxFactoryTipcTrusty : public RpcTransportCtxFactory { +public: + static std::unique_ptr<RpcTransportCtxFactory> make(); + + std::unique_ptr<RpcTransportCtx> newServerCtx() const override; + std::unique_ptr<RpcTransportCtx> newClientCtx() const override; + const char* toCString() const override; + +private: + RpcTransportCtxFactoryTipcTrusty() = default; +}; + +} // namespace android diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h new file mode 100644 index 0000000000..de84617343 --- /dev/null +++ b/libs/binder/trusty/include/log/log.h @@ -0,0 +1,128 @@ +/* + * 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 <stdlib.h> +#include <trusty_log.h> + +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/lib/tipc/tipc.h b/libs/binder/trusty/include_mock/lib/tipc/tipc.h new file mode 100644 index 0000000000..ead9f9cdaf --- /dev/null +++ b/libs/binder/trusty/include_mock/lib/tipc/tipc.h @@ -0,0 +1,33 @@ +/* + * 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 + +#if defined(__cplusplus) +extern "C" { +#endif + +struct tipc_hset; + +struct tipc_hset* tipc_hset_create(void) { + return nullptr; +} +int tipc_run_event_loop(struct tipc_hset*) { + return 0; +} + +#if defined(__cplusplus) +} +#endif diff --git a/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h b/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h new file mode 100644 index 0000000000..2747314508 --- /dev/null +++ b/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h @@ -0,0 +1,51 @@ +/* + * 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. + */ +#pragma once + +#include <stddef.h> +#include <trusty_ipc.h> +#include <uapi/trusty_uuid.h> + +struct tipc_port_acl { + uint32_t flags; + uint32_t uuid_num; + const struct uuid** uuids; + const void* extra_data; +}; + +struct tipc_port { + const char* name; + uint32_t msg_max_size; + uint32_t msg_queue_len; + const struct tipc_port_acl* acl; + const void* priv; +}; + +struct tipc_srv_ops { + int (*on_connect)(const struct tipc_port* port, handle_t chan, const struct uuid* peer, + void** ctx_p); + + int (*on_message)(const struct tipc_port* port, handle_t chan, void* ctx); + + void (*on_disconnect)(const struct tipc_port* port, handle_t chan, void* ctx); + + void (*on_channel_cleanup)(void* ctx); +}; + +static inline int tipc_add_service(struct tipc_hset*, const struct tipc_port*, uint32_t, uint32_t, + const struct tipc_srv_ops*) { + return 0; +} diff --git a/libs/binder/trusty/include_mock/lk/err_ptr.h b/libs/binder/trusty/include_mock/lk/err_ptr.h new file mode 100644 index 0000000000..ab3fbbacf6 --- /dev/null +++ b/libs/binder/trusty/include_mock/lk/err_ptr.h @@ -0,0 +1,19 @@ +/* + * 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 IS_ERR(x) (!(x)) +#define PTR_ERR(x) (!!(x)) diff --git a/libs/binder/trusty/include_mock/openssl/rand.h b/libs/binder/trusty/include_mock/openssl/rand.h new file mode 100644 index 0000000000..07dcc1cb6d --- /dev/null +++ b/libs/binder/trusty/include_mock/openssl/rand.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +static inline int RAND_bytes(unsigned char*, int) { + return 0; +} diff --git a/libs/binder/trusty/include_mock/trusty-gtest.h b/libs/binder/trusty/include_mock/trusty-gtest.h new file mode 100644 index 0000000000..046b403553 --- /dev/null +++ b/libs/binder/trusty/include_mock/trusty-gtest.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#define PORT_GTEST(suite, port) \ + int main(void) { \ + return 0; \ + } diff --git a/libs/binder/trusty/include_mock/trusty_ipc.h b/libs/binder/trusty/include_mock/trusty_ipc.h new file mode 100644 index 0000000000..db044c2c04 --- /dev/null +++ b/libs/binder/trusty/include_mock/trusty_ipc.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <uapi/trusty_uuid.h> + +#define INFINITE_TIME 1 +#define IPC_MAX_MSG_HANDLES 8 + +#define IPC_PORT_ALLOW_TA_CONNECT 0x1 +#define IPC_PORT_ALLOW_NS_CONNECT 0x2 + +#define IPC_CONNECT_WAIT_FOR_PORT 0x1 + +#define IPC_HANDLE_POLL_HUP 0x1 +#define IPC_HANDLE_POLL_MSG 0x2 +#define IPC_HANDLE_POLL_SEND_UNBLOCKED 0x4 + +typedef int handle_t; + +typedef struct ipc_msg { + uint32_t num_iov; + iovec* iov; + uint32_t num_handles; + handle_t* handles; +} ipc_msg_t; + +typedef struct ipc_msg_info { + size_t len; + uint32_t id; + uint32_t num_handles; +} ipc_msg_info_t; + +typedef struct uevent { + uint32_t event; +} uevent_t; + +static inline handle_t port_create(const char*, uint32_t, uint32_t, uint32_t) { + return 0; +} +static inline handle_t connect(const char*, uint32_t) { + return 0; +} +static inline handle_t accept(handle_t, uuid_t*) { + return 0; +} +static inline int set_cookie(handle_t, void*) { + return 0; +} +static inline handle_t handle_set_create(void) { + return 0; +} +static inline int handle_set_ctrl(handle_t, uint32_t, struct uevent*) { + return 0; +} +static inline int wait(handle_t, uevent_t*, uint32_t) { + return 0; +} +static inline int wait_any(uevent_t*, uint32_t) { + return 0; +} +static inline int get_msg(handle_t, ipc_msg_info_t*) { + return 0; +} +static inline ssize_t read_msg(handle_t, uint32_t, uint32_t, ipc_msg_t*) { + return 0; +} +static inline int put_msg(handle_t, uint32_t) { + return 0; +} +static inline ssize_t send_msg(handle_t, ipc_msg_t*) { + return 0; +} diff --git a/libs/binder/trusty/include_mock/trusty_log.h b/libs/binder/trusty/include_mock/trusty_log.h new file mode 100644 index 0000000000..d51e75280c --- /dev/null +++ b/libs/binder/trusty/include_mock/trusty_log.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stdio.h> + +// Mock definitions for the Trusty logging macros. These are not +// meant to be run, just compiled successfully. +#define TLOGD(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGI(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGW(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGE(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGC(fmt, ...) printf(fmt, ##__VA_ARGS__) diff --git a/libs/binder/trusty/include_mock/uapi/err.h b/libs/binder/trusty/include_mock/uapi/err.h new file mode 100644 index 0000000000..c7e117e419 --- /dev/null +++ b/libs/binder/trusty/include_mock/uapi/err.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +enum { + NO_ERROR, + ERR_ACCESS_DENIED, + ERR_ALREADY_EXISTS, + ERR_BAD_HANDLE, + ERR_BAD_LEN, + ERR_BAD_STATE, + ERR_CHANNEL_CLOSED, + ERR_CMD_UNKNOWN, + ERR_GENERIC, + ERR_INVALID_ARGS, + ERR_NO_MEMORY, + ERR_NO_MSG, + ERR_NOT_ALLOWED, + ERR_NOT_CONFIGURED, + ERR_NOT_ENOUGH_BUFFER, + ERR_NOT_FOUND, + ERR_NOT_READY, + ERR_NOT_SUPPORTED, + ERR_NOT_VALID, + ERR_TIMED_OUT, + ERR_TOO_BIG, +}; diff --git a/libs/binder/trusty/include_mock/uapi/trusty_uuid.h b/libs/binder/trusty/include_mock/uapi/trusty_uuid.h new file mode 100644 index 0000000000..f636826bec --- /dev/null +++ b/libs/binder/trusty/include_mock/uapi/trusty_uuid.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +typedef struct uuid { + int placeholder; +} uuid_t; diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk new file mode 100644 index 0000000000..ab7a50d3f7 --- /dev/null +++ b/libs/binder/trusty/kernel/rules.mk @@ -0,0 +1,83 @@ +# 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +LIBBINDER_DIR := frameworks/native/libs/binder +LIBBASE_DIR := system/libbase +LIBCUTILS_DIR := system/core/libcutils +LIBUTILS_DIR := system/core/libutils +FMTLIB_DIR := external/fmtlib + +MODULE_SRCS := \ + $(LOCAL_DIR)/../logging.cpp \ + $(LOCAL_DIR)/../TrustyStatus.cpp \ + $(LIBBINDER_DIR)/Binder.cpp \ + $(LIBBINDER_DIR)/BpBinder.cpp \ + $(LIBBINDER_DIR)/FdTrigger.cpp \ + $(LIBBINDER_DIR)/IInterface.cpp \ + $(LIBBINDER_DIR)/IResultReceiver.cpp \ + $(LIBBINDER_DIR)/Parcel.cpp \ + $(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 \ + +MODULE_DEFINES += \ + LK_DEBUGLEVEL_NO_ALIASES=1 \ + +MODULE_INCLUDES += \ + $(LOCAL_DIR)/.. \ + +GLOBAL_INCLUDES += \ + $(LOCAL_DIR)/include \ + $(LOCAL_DIR)/../include \ + $(LIBBINDER_DIR)/include \ + $(LIBBINDER_DIR)/ndk/include_cpp \ + $(LIBBASE_DIR)/include \ + $(LIBCUTILS_DIR)/include \ + $(LIBUTILS_DIR)/include \ + $(FMTLIB_DIR)/include \ + +GLOBAL_COMPILEFLAGS += \ + -DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \ + -DBINDER_NO_KERNEL_IPC \ + -DBINDER_RPC_SINGLE_THREADED \ + -D__ANDROID_VNDK__ \ + +MODULE_DEPS += \ + trusty/kernel/lib/libcxx-trusty \ + trusty/kernel/lib/libcxxabi-trusty \ + +include make/module.mk diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp new file mode 100644 index 0000000000..b4243af891 --- /dev/null +++ b/libs/binder/trusty/logging.cpp @@ -0,0 +1,166 @@ +/* + * 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 <trusty_log.h> +#include <iostream> +#include <string> + +#include <android-base/macros.h> +#include <android-base/strings.h> + +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 <typename F, typename... Args> +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 ?: "<unknown>", 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/ndk/include/sys/cdefs.h b/libs/binder/trusty/ndk/include/sys/cdefs.h new file mode 100644 index 0000000000..6a48d2b776 --- /dev/null +++ b/libs/binder/trusty/ndk/include/sys/cdefs.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <lk/compiler.h> + +/* Alias the bionic macros to the ones from lk/compiler.h */ +#define __BEGIN_DECLS __BEGIN_CDECLS +#define __END_DECLS __END_CDECLS + +#define __INTRODUCED_IN(x) /* nothing on Trusty */ diff --git a/libs/binder/trusty/ndk/rules.mk b/libs/binder/trusty/ndk/rules.mk new file mode 100644 index 0000000000..03fd006fd2 --- /dev/null +++ b/libs/binder/trusty/ndk/rules.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +LIBBINDER_NDK_DIR := frameworks/native/libs/binder/ndk + +MODULE_SRCS := \ + $(LIBBINDER_NDK_DIR)/ibinder.cpp \ + $(LIBBINDER_NDK_DIR)/libbinder.cpp \ + $(LIBBINDER_NDK_DIR)/parcel.cpp \ + $(LIBBINDER_NDK_DIR)/status.cpp \ + +MODULE_EXPORT_INCLUDES += \ + $(LOCAL_DIR)/include \ + $(LIBBINDER_NDK_DIR)/include_cpp \ + $(LIBBINDER_NDK_DIR)/include_ndk \ + $(LIBBINDER_NDK_DIR)/include_platform \ + +MODULE_LIBRARY_DEPS += \ + trusty/user/base/lib/libstdc++-trusty \ + frameworks/native/libs/binder/trusty \ + +include make/library.mk diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk new file mode 100644 index 0000000000..42db29a2ef --- /dev/null +++ b/libs/binder/trusty/rules.mk @@ -0,0 +1,92 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +LIBBINDER_DIR := frameworks/native/libs/binder +LIBBASE_DIR := system/libbase +LIBCUTILS_DIR := system/core/libcutils +LIBUTILS_DIR := system/core/libutils +FMTLIB_DIR := external/fmtlib + +MODULE_SRCS := \ + $(LOCAL_DIR)/logging.cpp \ + $(LOCAL_DIR)/OS.cpp \ + $(LOCAL_DIR)/RpcServerTrusty.cpp \ + $(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \ + $(LOCAL_DIR)/TrustyStatus.cpp \ + $(LOCAL_DIR)/socket.cpp \ + $(LIBBINDER_DIR)/Binder.cpp \ + $(LIBBINDER_DIR)/BpBinder.cpp \ + $(LIBBINDER_DIR)/FdTrigger.cpp \ + $(LIBBINDER_DIR)/IInterface.cpp \ + $(LIBBINDER_DIR)/IResultReceiver.cpp \ + $(LIBBINDER_DIR)/Parcel.cpp \ + $(LIBBINDER_DIR)/ParcelFileDescriptor.cpp \ + $(LIBBINDER_DIR)/RpcServer.cpp \ + $(LIBBINDER_DIR)/RpcSession.cpp \ + $(LIBBINDER_DIR)/RpcState.cpp \ + $(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 \ + +MODULE_EXPORT_INCLUDES += \ + $(LOCAL_DIR)/include \ + $(LIBBINDER_DIR)/include \ + $(LIBBASE_DIR)/include \ + $(LIBCUTILS_DIR)/include \ + $(LIBUTILS_DIR)/include \ + $(FMTLIB_DIR)/include \ + +# The android/binder_to_string.h header is shared between libbinder and +# libbinder_ndk and included by auto-generated AIDL C++ files +MODULE_EXPORT_INCLUDES += \ + $(LIBBINDER_DIR)/ndk/include_cpp \ + +MODULE_EXPORT_COMPILEFLAGS += \ + -DBINDER_RPC_SINGLE_THREADED \ + -D__ANDROID_VNDK__ \ + +# libbinder has some deprecated declarations that we want to produce warnings +# not errors +MODULE_EXPORT_COMPILEFLAGS += \ + -Wno-error=deprecated-declarations \ + +MODULE_LIBRARY_DEPS += \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/tipc \ + external/boringssl \ + +include make/library.mk diff --git a/libs/binder/trusty/socket.cpp b/libs/binder/trusty/socket.cpp new file mode 100644 index 0000000000..02df8afb92 --- /dev/null +++ b/libs/binder/trusty/socket.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <log/log.h> + +// On some versions of clang, RpcServer.cpp refuses to link without accept4 +__attribute__((weak)) extern "C" int accept4(int, void*, void*, int) { + LOG_ALWAYS_FATAL("accept4 called on Trusty"); + return 0; +} + +// Placeholder for poll used by FdTrigger +__attribute__((weak)) extern "C" int poll(void*, long, int) { + LOG_ALWAYS_FATAL("poll called on Trusty"); + return 0; +} diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk new file mode 100644 index 0000000000..13001219d4 --- /dev/null +++ b/libs/binder/trusty/usertests-inc.mk @@ -0,0 +1,19 @@ +# 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. +# + +TRUSTY_USER_TESTS += \ + frameworks/native/libs/binder/trusty/binderRpcTest \ + frameworks/native/libs/binder/trusty/binderRpcTest/service \ + diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h index dfd5a7cc3e..6ce8edfc7c 100644 --- a/libs/binderdebug/include/binderdebug/BinderDebug.h +++ b/libs/binderdebug/include/binderdebug/BinderDebug.h @@ -15,6 +15,8 @@ */ #pragma once +#include <utils/Errors.h> + #include <map> #include <vector> diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp index 44e2fd19b1..df1f35d9a7 100644 --- a/libs/binderthreadstate/test.cpp +++ b/libs/binderthreadstate/test.cpp @@ -68,8 +68,22 @@ static void callHidl(size_t id, int32_t idx) { static void callAidl(size_t id, int32_t idx) { sp<IAidlStuff> stuff; - CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff)); - CHECK(stuff->call(idx).isOk()); + CHECK_EQ(OK, android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff)); + auto ret = stuff->call(idx); + CHECK(ret.isOk()) << ret; +} + +static std::string getStackPointerDebugInfo() { + const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer(); + const void* binderSp = android::IPCThreadState::self()->getServingStackPointer(); + + std::stringstream ss; + ss << "(hwbinder sp: " << hwbinderSp << " binder sp: " << binderSp << ")"; + return ss.str(); +} + +static inline std::ostream& operator<<(std::ostream& o, const BinderCallType& s) { + return o << static_cast<std::underlying_type_t<BinderCallType>>(s); } class HidlServer : public IHidlStuff { @@ -79,21 +93,25 @@ public: size_t otherId; Return<void> callLocal() { - CHECK(BinderCallType::NONE == getCurrentServingCall()); + CHECK_EQ(BinderCallType::NONE, getCurrentServingCall()); return android::hardware::Status::ok(); } Return<void> call(int32_t idx) { + bool doCallHidl = thisId == kP1Id && idx % 4 < 2; + LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx - << " with tid: " << gettid(); - CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + << " with tid: " << gettid() << " calling " << (doCallHidl ? "HIDL" : "AIDL"); + CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall()) + << " before call " << getStackPointerDebugInfo(); if (idx > 0) { - if (thisId == kP1Id && idx % 4 < 2) { + if (doCallHidl) { callHidl(otherId, idx - 1); } else { callAidl(otherId, idx - 1); } } - CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall()) + << " after call " << getStackPointerDebugInfo(); return android::hardware::Status::ok(); } }; @@ -104,21 +122,24 @@ public: size_t otherId; Status callLocal() { - CHECK(BinderCallType::NONE == getCurrentServingCall()); + CHECK_EQ(BinderCallType::NONE, getCurrentServingCall()); return Status::ok(); } Status call(int32_t idx) { + bool doCallHidl = thisId == kP2Id && idx % 4 < 2; LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx - << " with tid: " << gettid(); - CHECK(BinderCallType::BINDER == getCurrentServingCall()); + << " with tid: " << gettid() << " calling " << (doCallHidl ? "HIDL" : "AIDL"); + CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall()) + << " before call " << getStackPointerDebugInfo(); if (idx > 0) { - if (thisId == kP2Id && idx % 4 < 2) { + if (doCallHidl) { callHidl(otherId, idx - 1); } else { callAidl(otherId, idx - 1); } } - CHECK(BinderCallType::BINDER == getCurrentServingCall()); + CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall()) + << " after call " << getStackPointerDebugInfo(); return Status::ok(); } }; @@ -161,13 +182,14 @@ int server(size_t thisId, size_t otherId) { // AIDL android::ProcessState::self()->setThreadPoolMaxThreadCount(1); sp<AidlServer> aidlServer = new AidlServer(thisId, otherId); - CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); + CHECK_EQ(OK, + defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); android::ProcessState::self()->startThreadPool(); // HIDL android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/); sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId); - CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str())); + CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str())); android::hardware::joinRpcThreadpool(); return EXIT_FAILURE; diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 7e9bb7d468..706704ad34 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -131,24 +131,24 @@ static bool initGlobals() { } gTisTotalMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_total_time_in_state_map")}; if (gTisTotalMapFd < 0) return false; - gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_time_in_state_map")}; if (gTisMapFd < 0) return false; gConcurrentMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; if (gConcurrentMapFd < 0) return false; gUidLastUpdateMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_last_update_map")}; if (gUidLastUpdateMapFd < 0) return false; - gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_time_in_state_pid_time_in_state_map")}; + gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_timeInState_pid_time_in_state_map")}; if (gPidTisMapFd < 0) return false; - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); + unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_map")); if (trackedPidMapFd < 0) return false; gInitialized = true; @@ -156,7 +156,7 @@ static bool initGlobals() { } static int retrieveProgramFd(const std::string &eventType, const std::string &eventName) { - std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", + std::string path = StringPrintf(BPF_FS_PATH "prog_timeInState_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); return retrieveProgram(path.c_str()); } @@ -200,7 +200,7 @@ bool startTrackingUidTimes() { if (!initGlobals()) return false; if (gTracking) return true; - unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_policy_map")); + unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_cpu_policy_map")); if (cpuPolicyFd < 0) return false; for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) { @@ -209,7 +209,7 @@ bool startTrackingUidTimes() { } } - unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_freq_to_idx_map")); if (freqToIdxFd < 0) return false; freq_idx_key_t key; for (uint32_t i = 0; i < gNPolicies; ++i) { @@ -224,23 +224,23 @@ bool startTrackingUidTimes() { } } - unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_last_update_map")); + unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_cpu_last_update_map")); if (cpuLastUpdateFd < 0) return false; std::vector<uint64_t> zeros(get_nprocs_conf(), 0); uint32_t zero = 0; if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false; - unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_nr_active_map")); + unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_nr_active_map")); if (nrActiveFd < 0) return false; if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false; - unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_nr_active_map")); + unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_policy_nr_active_map")); if (policyNrActiveFd < 0) return false; for (uint32_t i = 0; i < gNPolicies; ++i) { if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false; } - unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map")); + unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_policy_freq_idx_map")); if (policyFreqIdxFd < 0) return false; for (uint32_t i = 0; i < gNPolicies; ++i) { auto freqIdx = getPolicyFreqIdx(i); @@ -300,11 +300,11 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui } std::vector<tis_val_t> vals(gNCpus); - time_key_t key = {.uid = uid}; for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { - key.bucket = i; + const time_key_t key = {.uid = uid, .bucket = i}; if (findMapEntry(gTisMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {}; + time_key_t tmpKey; + if (errno != ENOENT || getFirstMapKey(gTisMapFd, &tmpKey)) return {}; continue; } @@ -412,10 +412,11 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)}; for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0); std::vector<concurrent_val_t> vals(gNCpus); - time_key_t key = {.uid = uid}; - for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + for (uint32_t i = 0; i <= (gNCpus - 1) / CPUS_PER_ENTRY; ++i) { + const time_key_t key = {.uid = uid, .bucket = i}; if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {}; + time_key_t tmpKey; + if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &tmpKey)) return {}; continue; } auto offset = key.bucket * CPUS_PER_ENTRY; @@ -559,10 +560,10 @@ bool startTrackingProcessCpuTimes(pid_t pid) { if (!gInitialized && !initGlobals()) return false; unique_fd trackedPidHashMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_hash_map")); + mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_hash_map")); if (trackedPidHashMapFd < 0) return false; - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); + unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_map")); if (trackedPidMapFd < 0) return false; for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) { @@ -589,7 +590,7 @@ bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { if (!gInitialized && !initGlobals()) return false; unique_fd taskAggregationMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_task_aggregation_map")); + mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_task_aggregation_map")); if (taskAggregationMapFd < 0) return false; return writeToMapEntry(taskAggregationMapFd, &pid, &aggregationKey, BPF_ANY) == 0; diff --git a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp new file mode 100644 index 0000000000..2399acd156 --- /dev/null +++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * 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. + * + ***************************************************************************** + */ +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_fuzz { + name: "cputimeinstate_fuzzer", + srcs: [ + "cputimeinstate_fuzzer.cpp", + ], + static_libs: [ + "libtimeinstate", + ], + shared_libs: [ + "libbpf_bcc", + "libbase", + "libbpf_minimal", + ], +} diff --git a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp new file mode 100644 index 0000000000..f835997187 --- /dev/null +++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include <android-base/unique_fd.h> +#include <cputimeinstate.h> + +using namespace android::bpf; + +static const uint16_t MAX_VEC_SIZE = 500; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + uint32_t uid = fdp.ConsumeIntegral<uint32_t>(); + uint64_t lastUpdate = fdp.ConsumeIntegral<uint64_t>(); + uint16_t aggregationKey = fdp.ConsumeIntegral<uint16_t>(); + pid_t pid = fdp.ConsumeIntegral<pid_t>(); + std::vector<uint16_t> aggregationKeys; + uint16_t aggregationKeysSize = fdp.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE); + for (uint16_t i = 0; i < aggregationKeysSize; i++) { + aggregationKeys.push_back(fdp.ConsumeIntegral<uint16_t>()); + } + + // To randomize the API calls + while (fdp.remaining_bytes() > 0) { + auto func = fdp.PickValueInArray<const std::function<void()>>({ + [&]() { getUidCpuFreqTimes(uid); }, + [&]() { getUidsUpdatedCpuFreqTimes(&lastUpdate); }, + [&]() { getUidConcurrentTimes(uid);}, + [&]() { getUidsUpdatedConcurrentTimes(&lastUpdate); }, + [&]() { startAggregatingTaskCpuTimes(pid, aggregationKey); }, + [&]() { getAggregatedTaskCpuFreqTimes(pid, aggregationKeys); }, + }); + + func(); + } + + return 0; +} diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 45a6d47bb4..6ccc6cafb6 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -462,7 +462,7 @@ TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { ++uid; } android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; ASSERT_GE(fd, 0); uint32_t nCpus = get_nprocs_conf(); uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY; @@ -504,7 +504,7 @@ TEST_F(TimeInStateTest, RemoveUid) { { // Add a map entry for our fake UID by copying a real map entry android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_time_in_state_map")}; ASSERT_GE(fd, 0); time_key_t k; ASSERT_FALSE(getFirstMapKey(fd, &k)); @@ -515,7 +515,7 @@ TEST_F(TimeInStateTest, RemoveUid) { ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); android::base::unique_fd fd2{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; k.uid = copiedUid; k.bucket = 0; std::vector<concurrent_val_t> cvals(get_nprocs_conf()); diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 3551a8fd0a..067ce17c33 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -85,8 +85,20 @@ static const char* hidl_hal_interfaces_to_dump[] { /* list of hal interface to dump containing process during native dumps */ static const std::vector<std::string> aidl_interfaces_to_dump { + "android.hardware.automotive.audiocontrol.IAudioControl", + "android.hardware.automotive.evs.IEvsEnumerator", + "android.hardware.biometrics.face.IBiometricsFace", + "android.hardware.biometrics.fingerprint.IBiometricsFingerprint", "android.hardware.camera.provider.ICameraProvider", + "android.hardware.drm.IDrmFactory", + "android.hardware.graphics.allocator.IAllocator", + "android.hardware.graphics.composer3.IComposer", + "android.hardware.health.IHealth", "android.hardware.input.processor.IInputProcessor", + "android.hardware.neuralnetworks.IDevice", + "android.hardware.power.IPower", + "android.hardware.power.stats.IPowerStats", + "android.hardware.sensors.ISensors", }; /* list of extra hal interfaces to dump containing process during native dumps */ @@ -196,5 +208,5 @@ bool IsZygote(int pid) { cmdline = std::string(cmdline.c_str()); return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" || - cmdline == "usap64"; + cmdline == "usap64" || cmdline == "webview_zygote"; } diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index 47c0657bbd..29924ff500 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -28,6 +28,7 @@ cc_defaults { cc_library { name: "libfakeservicemanager", defaults: ["fakeservicemanager_defaults"], + export_include_dirs: ["include/fakeservicemanager"], } cc_test_host { @@ -37,4 +38,5 @@ cc_test_host { "test_sm.cpp", ], static_libs: ["libgmock"], + local_include_dirs: ["include/fakeservicemanager"], } diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp index 6c6d7f3641..1109ad8594 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -36,6 +36,9 @@ sp<IBinder> ServiceManager::checkService( const String16& name) const { status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service, bool /*allowIsolated*/, int /*dumpsysFlags*/) { + if (service == nullptr) { + return UNEXPECTED_NULL; + } mNameToService[name] = service; return NO_ERROR; } @@ -78,6 +81,11 @@ std::optional<String16> ServiceManager::updatableViaApex(const String16& name) { return std::nullopt; } +Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) { + (void)apexName; + return {}; +} + std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo( const String16& name) { (void)name; @@ -98,4 +106,8 @@ std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugIn std::vector<IServiceManager::ServiceDebugInfo> ret; return ret; } + +void ServiceManager::clear() { + mNameToService.clear(); +} } // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h index e0af5d4ba8..ba6bb7d95b 100644 --- a/libs/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h @@ -52,6 +52,8 @@ public: std::optional<String16> updatableViaApex(const String16& name) override; + Vector<String16> getUpdatableNames(const String16& apexName) override; + std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override; status_t registerForNotifications(const String16& name, @@ -62,6 +64,9 @@ public: std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override; + // Clear all of the registered services + void clear(); + private: std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp index 71e5abe126..8682c1c795 100644 --- a/libs/fakeservicemanager/test_sm.cpp +++ b/libs/fakeservicemanager/test_sm.cpp @@ -50,6 +50,12 @@ TEST(AddService, HappyHappy) { IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); } +TEST(AddService, SadNullBinder) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL); +} + TEST(AddService, HappyOverExistingService) { auto sm = new ServiceManager(); EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, @@ -58,6 +64,15 @@ TEST(AddService, HappyOverExistingService) { IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); } +TEST(AddService, HappyClearAddedService) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + EXPECT_NE(sm->getService(String16("foo")), nullptr); + sm->clear(); + EXPECT_EQ(sm->getService(String16("foo")), nullptr); +} + TEST(GetService, HappyHappy) { auto sm = new ServiceManager(); sp<IBinder> service = getBinder(); diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index bd21fbaf0e..f5af425fca 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -51,14 +51,14 @@ cc_library { ], shared_libs: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", "liblog", ], export_shared_lib_headers: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", ], diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index e86881a880..5f5f85a2ad 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -377,26 +377,61 @@ bool GraphicsEnv::shouldUseAngle() { return (mUseAngle == YES) ? true : false; } +bool GraphicsEnv::angleIsSystemDriver() { + // Make sure we are init'ed + if (mAngleAppName.empty()) { + ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE."); + return false; + } + + return (mAngleIsSystemDriver == YES) ? true : false; +} + +bool GraphicsEnv::shouldForceLegacyDriver() { + // Make sure we are init'ed + if (mAngleAppName.empty()) { + ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE."); + return false; + } + + return (mAngleIsSystemDriver == YES && mUseAngle == NO) ? true : false; +} + +std::string GraphicsEnv::getLegacySuffix() { + return mLegacyDriverSuffix; +} + void GraphicsEnv::updateUseAngle() { mUseAngle = NO; const char* ANGLE_PREFER_ANGLE = "angle"; + const char* ANGLE_PREFER_LEGACY = "legacy"; + // The following is a deprecated version of "legacy" const char* ANGLE_PREFER_NATIVE = "native"; mUseAngle = NO; if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { - ALOGV("User set \"Developer Options\" to force the use of ANGLE"); + ALOGI("Using ANGLE, the %s GLES driver for package '%s'", + mAngleIsSystemDriver == YES ? "system" : "optional", mAngleAppName.c_str()); mUseAngle = YES; - } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { - ALOGV("User set \"Developer Options\" to force the use of Native"); + } else if (mAngleDeveloperOptIn == ANGLE_PREFER_LEGACY || + mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { + ALOGI("Using the (%s) Legacy GLES driver for package '%s'", + mAngleIsSystemDriver == YES ? "optional" : "system", mAngleAppName.c_str()); } else { ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str()); } } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, - const std::string developerOptIn, + const bool angleIsSystemDriver, const std::string developerOptIn, const std::vector<std::string> eglFeatures) { + // Set whether ANGLE is the system driver: + mAngleIsSystemDriver = angleIsSystemDriver ? YES : NO; + + // Note: Given the current logic and lack of the old rules file processing, + // there seems to be little chance that mUseAngle != UNKNOWN. Leave this + // for now, even though it seems outdated. if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), @@ -417,6 +452,25 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName updateUseAngle(); } +void GraphicsEnv::setLegacyDriverInfo(const std::string appName, const bool angleIsSystemDriver, + const std::string legacyDriverName) { + ALOGV("setting legacy app name to '%s'", appName.c_str()); + mAngleAppName = appName; + + // Force the use of the legacy driver instead of ANGLE + const char* ANGLE_PREFER_LEGACY = "legacy"; + mAngleDeveloperOptIn = ANGLE_PREFER_LEGACY; + ALOGV("setting ANGLE application opt-in to 'legacy'"); + + // Set whether ANGLE is the system driver: + mAngleIsSystemDriver = angleIsSystemDriver ? YES : NO; + + mLegacyDriverSuffix = legacyDriverName; + + // Update the current status of whether we should use ANGLE or not + updateUseAngle(); +} + void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) { if (mLayerPaths.empty()) { mLayerPaths = layerPaths; diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS index 8c284647f2..347c4e0db1 100644 --- a/libs/graphicsenv/OWNERS +++ b/libs/graphicsenv/OWNERS @@ -1,4 +1,10 @@ +abdolrashidi@google.com +cclao@google.com chrisforbes@google.com cnorthrop@google.com +ianelliott@google.com +lfy@google.com lpy@google.com -timvp@google.com +romanl@google.com +vantablack@google.com +yuxinhu@google.com diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 098e4a6507..73d3196948 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -91,17 +91,28 @@ public: bool shouldUseAngle(std::string appName); // Check if this app process should use ANGLE. bool shouldUseAngle(); + // If ANGLE is the system GLES driver + bool angleIsSystemDriver(); + // If should use legacy driver instead of a system ANGLE driver + bool shouldForceLegacyDriver(); // Set a search path for loading ANGLE libraries. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (libraries must be stored uncompressed and page aligned); such elements // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a - void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, + void setAngleInfo(const std::string path, const std::string appName, + const bool angleIsSystemDriver, std::string devOptIn, const std::vector<std::string> eglFeatures); + // Set the state so that the legacy driver will be used, and in case ANGLE + // is the system driver, provide the name of the legacy driver. + void setLegacyDriverInfo(const std::string appName, const bool angleIsSystemDriver, + const std::string legacyDriverName); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app name for ANGLE debug message. std::string& getAngleAppName(); + // Get the legacy driver's suffix name. + std::string getLegacySuffix(); const std::vector<std::string>& getAngleEglFeatures(); @@ -156,6 +167,10 @@ private: std::string mAngleDeveloperOptIn; // ANGLE EGL features; std::vector<std::string> mAngleEglFeatures; + // ANGLE is System Driver flag. + UseAngle mAngleIsSystemDriver = UNKNOWN; + // Legacy driver name to use when ANGLE is the system driver. + std::string mLegacyDriverSuffix; // Use ANGLE flag. UseAngle mUseAngle = UNKNOWN; // Vulkan debug layers libs. diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index d634c58c53..2ac11746c3 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -170,14 +170,13 @@ cc_library_shared { ], srcs: [ - ":framework_native_aidl", + ":framework_native_aidl_binder", + ":framework_native_aidl_gui", ":inputconstants_aidl", ":libgui_bufferqueue_sources", "BitTube.cpp", "BLASTBufferQueue.cpp", - "BufferHubConsumer.cpp", - "BufferHubProducer.cpp", "BufferItemConsumer.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", @@ -215,9 +214,6 @@ cc_library_shared { shared_libs: [ "libbinder", - "libbufferhub", - "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. - "libpdx_default_transport", ], export_shared_lib_headers: [ @@ -228,24 +224,6 @@ cc_library_shared { "libgui_aidl_headers", ], - // bufferhub is not used when building libgui for vendors - target: { - vendor: { - cflags: [ - "-DNO_BUFFERHUB", - ], - exclude_srcs: [ - "BufferHubConsumer.cpp", - "BufferHubProducer.cpp", - ], - exclude_shared_libs: [ - "libbufferhub", - "libbufferhubqueue", - "libpdx_default_transport", - ], - }, - }, - aidl: { export_aidl_headers: true, }, @@ -275,7 +253,6 @@ cc_library_static { min_sdk_version: "29", cflags: [ - "-DNO_BUFFERHUB", "-DNO_BINDER", ], @@ -316,7 +293,6 @@ filegroup { cc_defaults { name: "libgui_bufferqueue-defaults", - clang: true, cflags: [ "-Wall", "-Werror", @@ -337,7 +313,7 @@ cc_defaults { }, whole_static_libs: [ - "LibGuiProperties", + "libLibGuiProperties", ], shared_libs: [ @@ -345,7 +321,7 @@ cc_defaults { "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", "android.hardware.graphics.common@1.2", - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hidl.token@1.0-utils", "libbase", "libcutils", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index b8ea0808ab..ee0eaa1f8f 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -429,8 +429,8 @@ void BLASTBufferQueue::releaseBufferCallbackLocked( mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount; } - const auto numPendingBuffersToHold = - isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0; + const uint32_t numPendingBuffersToHold = + isEGL ? std::max(0, mMaxAcquiredBuffers - (int32_t)mCurrentMaxAcquiredBufferCount) : 0; auto rb = ReleasedBuffer{id, releaseFence}; if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) { diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp deleted file mode 100644 index b5cdeb280a..0000000000 --- a/libs/gui/BufferHubConsumer.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2018 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 <gui/BufferHubConsumer.h> - -namespace android { - -using namespace dvr; - -/* static */ -sp<BufferHubConsumer> BufferHubConsumer::Create(const std::shared_ptr<ConsumerQueue>& queue) { - sp<BufferHubConsumer> consumer = new BufferHubConsumer; - consumer->mQueue = queue; - return consumer; -} - -/* static */ sp<BufferHubConsumer> BufferHubConsumer::Create(ConsumerQueueParcelable parcelable) { - if (!parcelable.IsValid()) { - ALOGE("BufferHubConsumer::Create: Invalid consumer parcelable."); - return nullptr; - } - - sp<BufferHubConsumer> consumer = new BufferHubConsumer; - consumer->mQueue = ConsumerQueue::Import(parcelable.TakeChannelHandle()); - return consumer; -} - -status_t BufferHubConsumer::acquireBuffer(BufferItem* /*buffer*/, nsecs_t /*presentWhen*/, - uint64_t /*maxFrameNumber*/) { - ALOGE("BufferHubConsumer::acquireBuffer: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::detachBuffer(int /*slot*/) { - ALOGE("BufferHubConsumer::detachBuffer: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::attachBuffer(int* /*outSlot*/, const sp<GraphicBuffer>& /*buffer*/) { - ALOGE("BufferHubConsumer::attachBuffer: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::releaseBuffer(int /*buf*/, uint64_t /*frameNumber*/, - EGLDisplay /*display*/, EGLSyncKHR /*fence*/, - const sp<Fence>& /*releaseFence*/) { - ALOGE("BufferHubConsumer::releaseBuffer: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::consumerConnect(const sp<IConsumerListener>& /*consumer*/, - bool /*controlledByApp*/) { - ALOGE("BufferHubConsumer::consumerConnect: not implemented."); - - // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to - // make IGraphicBufferConsumer_test happy. - return NO_ERROR; -} - -status_t BufferHubConsumer::consumerDisconnect() { - ALOGE("BufferHubConsumer::consumerDisconnect: not implemented."); - - // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to - // make IGraphicBufferConsumer_test happy. - return NO_ERROR; -} - -status_t BufferHubConsumer::getReleasedBuffers(uint64_t* /*slotMask*/) { - ALOGE("BufferHubConsumer::getReleasedBuffers: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) { - ALOGE("BufferHubConsumer::setDefaultBufferSize: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setMaxBufferCount(int /*bufferCount*/) { - ALOGE("BufferHubConsumer::setMaxBufferCount: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setMaxAcquiredBufferCount(int /*maxAcquiredBuffers*/) { - ALOGE("BufferHubConsumer::setMaxAcquiredBufferCount: not implemented."); - - // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to - // make IGraphicBufferConsumer_test happy. - return NO_ERROR; -} - -status_t BufferHubConsumer::setConsumerName(const String8& /*name*/) { - ALOGE("BufferHubConsumer::setConsumerName: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setDefaultBufferFormat(PixelFormat /*defaultFormat*/) { - ALOGE("BufferHubConsumer::setDefaultBufferFormat: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setDefaultBufferDataSpace(android_dataspace /*defaultDataSpace*/) { - ALOGE("BufferHubConsumer::setDefaultBufferDataSpace: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setConsumerUsageBits(uint64_t /*usage*/) { - ALOGE("BufferHubConsumer::setConsumerUsageBits: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setConsumerIsProtected(bool /*isProtected*/) { - ALOGE("BufferHubConsumer::setConsumerIsProtected: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::setTransformHint(uint32_t /*hint*/) { - ALOGE("BufferHubConsumer::setTransformHint: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::getSidebandStream(sp<NativeHandle>* /*outStream*/) const { - ALOGE("BufferHubConsumer::getSidebandStream: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::getOccupancyHistory( - bool /*forceFlush*/, std::vector<OccupancyTracker::Segment>* /*outHistory*/) { - ALOGE("BufferHubConsumer::getOccupancyHistory: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::discardFreeBuffers() { - ALOGE("BufferHubConsumer::discardFreeBuffers: not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const { - ALOGE("BufferHubConsumer::dumpState: not implemented."); - return INVALID_OPERATION; -} - -IBinder* BufferHubConsumer::onAsBinder() { - ALOGE("BufferHubConsumer::onAsBinder: BufferHubConsumer should never be used as an Binder " - "object."); - return nullptr; -} - -} // namespace android diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp deleted file mode 100644 index 1f71e23b4d..0000000000 --- a/libs/gui/BufferHubProducer.cpp +++ /dev/null @@ -1,868 +0,0 @@ -/* - * Copyright 2018 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 <dvr/dvr_api.h> -#include <gui/BufferHubProducer.h> -#include <inttypes.h> -#include <log/log.h> -#include <system/window.h> - -namespace android { - -using namespace dvr; - -/* static */ -sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<ProducerQueue>& queue) { - sp<BufferHubProducer> producer = new BufferHubProducer; - producer->queue_ = queue; - return producer; -} - -/* static */ -sp<BufferHubProducer> BufferHubProducer::Create(ProducerQueueParcelable parcelable) { - if (!parcelable.IsValid()) { - ALOGE("BufferHubProducer::Create: Invalid producer parcelable."); - return nullptr; - } - - sp<BufferHubProducer> producer = new BufferHubProducer; - producer->queue_ = ProducerQueue::Import(parcelable.TakeChannelHandle()); - return producer; -} - -status_t BufferHubProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - ALOGV("requestBuffer: slot=%d", slot); - - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ == kNoConnectedApi) { - ALOGE("requestBuffer: BufferHubProducer has no connected producer"); - return NO_INIT; - } - - if (slot < 0 || slot >= max_buffer_count_) { - ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_); - return BAD_VALUE; - } else if (!buffers_[slot].mBufferState.isDequeued()) { - ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", slot, - buffers_[slot].mBufferState.string()); - return BAD_VALUE; - } else if (buffers_[slot].mGraphicBuffer != nullptr) { - ALOGE("requestBuffer: slot %d is not empty.", slot); - return BAD_VALUE; - } else if (buffers_[slot].mProducerBuffer == nullptr) { - ALOGE("requestBuffer: slot %d is not dequeued.", slot); - return BAD_VALUE; - } - - const auto& producer_buffer = buffers_[slot].mProducerBuffer; - sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer(); - - buffers_[slot].mGraphicBuffer = graphic_buffer; - buffers_[slot].mRequestBufferCalled = true; - - *buf = graphic_buffer; - return NO_ERROR; -} - -status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers) { - ALOGV("setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers); - - std::unique_lock<std::mutex> lock(mutex_); - - if (max_dequeued_buffers <= 0 || - max_dequeued_buffers > - int(BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) { - ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers, - BufferHubQueue::kMaxQueueCapacity); - return BAD_VALUE; - } - - // The new dequeued_buffers count should not be violated by the number - // of currently dequeued buffers. - int dequeued_count = 0; - for (const auto& buf : buffers_) { - if (buf.mBufferState.isDequeued()) { - dequeued_count++; - } - } - if (dequeued_count > max_dequeued_buffers) { - ALOGE("setMaxDequeuedBufferCount: the requested dequeued_buffers" - "count (%d) exceeds the current dequeued buffer count (%d)", - max_dequeued_buffers, dequeued_count); - return BAD_VALUE; - } - - max_dequeued_buffer_count_ = max_dequeued_buffers; - return NO_ERROR; -} - -status_t BufferHubProducer::setAsyncMode(bool async) { - if (async) { - // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer - // automatically and behaves differently from IGraphicBufferConsumer. Thus, - // android::BufferQueue's async mode (a.k.a. allocating an additional buffer - // to prevent dequeueBuffer from being blocking) technically does not apply - // here. - // - // In Daydream, non-blocking producer side dequeue is guaranteed by careful - // buffer consumer implementations. In another word, BufferHubQueue based - // dequeueBuffer should never block whether setAsyncMode(true) is set or - // not. - // - // See: IGraphicBufferProducer::setAsyncMode and - // BufferQueueProducer::setAsyncMode for more about original implementation. - ALOGW("BufferHubProducer::setAsyncMode: BufferHubQueue should always be " - "asynchronous. This call makes no effact."); - return NO_ERROR; - } - return NO_ERROR; -} - -status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, - uint32_t height, PixelFormat format, uint64_t usage, - uint64_t* /*outBufferAge*/, - FrameEventHistoryDelta* /* out_timestamps */) { - ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage); - - status_t ret; - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ == kNoConnectedApi) { - ALOGE("dequeueBuffer: BufferQueue has no connected producer"); - return NO_INIT; - } - - const uint32_t kLayerCount = 1; - if (int32_t(queue_->capacity()) < max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) { - // Lazy allocation. When the capacity of |queue_| has not reached - // |max_dequeued_buffer_count_|, allocate new buffer. - // TODO(jwcai) To save memory, the really reasonable thing to do is to go - // over existing slots and find first existing one to dequeue. - ret = AllocateBuffer(width, height, kLayerCount, format, usage); - if (ret < 0) return ret; - } - - size_t slot = 0; - std::shared_ptr<ProducerBuffer> producer_buffer; - - for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) { - LocalHandle fence; - auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence); - if (!buffer_status) return NO_MEMORY; - - producer_buffer = buffer_status.take(); - if (!producer_buffer) return NO_MEMORY; - - if (width == producer_buffer->width() && height == producer_buffer->height() && - uint32_t(format) == producer_buffer->format()) { - // The producer queue returns a producer buffer matches the request. - break; - } - - // Needs reallocation. - // TODO(jwcai) Consider use VLOG instead if we find this log is not useful. - ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different " - "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need " - "re-allocattion.", - width, height, format, slot, producer_buffer->width(), producer_buffer->height(), - producer_buffer->format()); - // Mark the slot as reallocating, so that later we can set - // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued. - buffers_[slot].mIsReallocating = true; - - // Remove the old buffer once the allocation before allocating its - // replacement. - RemoveBuffer(slot); - - // Allocate a new producer buffer with new buffer configs. Note that if - // there are already multiple buffers in the queue, the next one returned - // from |queue_->Dequeue| may not be the new buffer we just reallocated. - // Retry up to BufferHubQueue::kMaxQueueCapacity times. - ret = AllocateBuffer(width, height, kLayerCount, format, usage); - if (ret < 0) return ret; - } - - // With the BufferHub backed solution. Buffer slot returned from - // |queue_->Dequeue| is guaranteed to avaiable for producer's use. - // It's either in free state (if the buffer has never been used before) or - // in queued state (if the buffer has been dequeued and queued back to - // BufferHubQueue). - LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() && - !buffers_[slot].mBufferState.isQueued()), - "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot, - buffers_[slot].mBufferState.string()); - - buffers_[slot].mBufferState.freeQueued(); - buffers_[slot].mBufferState.dequeue(); - ALOGV("dequeueBuffer: slot=%zu", slot); - - // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we - // just need to exopose that through |BufferHubQueue| once we need fence. - *out_fence = Fence::NO_FENCE; - *out_slot = int(slot); - ret = NO_ERROR; - - if (buffers_[slot].mIsReallocating) { - ret |= BUFFER_NEEDS_REALLOCATION; - buffers_[slot].mIsReallocating = false; - } - - return ret; -} - -status_t BufferHubProducer::detachBuffer(int slot) { - ALOGV("detachBuffer: slot=%d", slot); - std::unique_lock<std::mutex> lock(mutex_); - - return DetachBufferLocked(static_cast<size_t>(slot)); -} - -status_t BufferHubProducer::DetachBufferLocked(size_t slot) { - if (connected_api_ == kNoConnectedApi) { - ALOGE("detachBuffer: BufferHubProducer is not connected."); - return NO_INIT; - } - - if (slot >= static_cast<size_t>(max_buffer_count_)) { - ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_); - return BAD_VALUE; - } else if (!buffers_[slot].mBufferState.isDequeued()) { - ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot, - buffers_[slot].mBufferState.string()); - return BAD_VALUE; - } else if (!buffers_[slot].mRequestBufferCalled) { - ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot); - return BAD_VALUE; - } - std::shared_ptr<ProducerBuffer> producer_buffer = queue_->GetBuffer(slot); - if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) { - ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot); - return BAD_VALUE; - } - sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer(); - if (graphic_buffer == nullptr) { - ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot); - return BAD_VALUE; - } - - // Remove the ProducerBuffer from the ProducerQueue. - status_t error = RemoveBuffer(slot); - if (error != NO_ERROR) { - ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error); - return error; - } - - // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject - // the handle into the GraphicBuffer object at the requested slot. - auto status_or_handle = producer_buffer->Detach(); - if (!status_or_handle.ok()) { - ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot, - status_or_handle.error()); - return BAD_VALUE; - } - - // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can - // be directly backed by BufferHub. - return INVALID_OPERATION; -} - -status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) { - ALOGV("detachNextBuffer."); - - if (out_buffer == nullptr || out_fence == nullptr) { - ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer, - out_fence); - return BAD_VALUE; - } - - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ == kNoConnectedApi) { - ALOGE("detachNextBuffer: BufferHubProducer is not connected."); - return NO_INIT; - } - - // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in - // sequence, except for two things: - // - // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the - // function just returns whatever ProducerBuffer is available from the ProducerQueue and no - // buffer allocation or re-allocation will happen. - // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return - // an error instead. - size_t slot = 0; - LocalHandle fence; - - // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error - // immediately if ProducerQueue::Dequeue() fails. - auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence); - if (!status_or_buffer.ok()) { - ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error()); - return NO_MEMORY; - } - - std::shared_ptr<ProducerBuffer> producer_buffer = status_or_buffer.take(); - if (producer_buffer == nullptr) { - ALOGE("detachNextBuffer: Dequeued buffer is null."); - return NO_MEMORY; - } - - // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to - // be available for producer's use. It's either in free state (if the buffer has never been used - // before) or in queued state (if the buffer has been dequeued and queued back to - // BufferHubQueue). - if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) { - ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot, - buffers_[slot].mBufferState.string()); - return BAD_VALUE; - } - if (buffers_[slot].mProducerBuffer == nullptr) { - ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot); - return BAD_VALUE; - } - if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) { - ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: " - "%d, expected: %d.", - slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id()); - return BAD_VALUE; - } - - ALOGV("detachNextBuffer: slot=%zu", slot); - buffers_[slot].mBufferState.freeQueued(); - buffers_[slot].mBufferState.dequeue(); - - // Second, request the buffer. - sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer(); - buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer(); - - // Finally, detach the buffer and then return. - status_t error = DetachBufferLocked(slot); - if (error == NO_ERROR) { - *out_fence = new Fence(fence.Release()); - *out_buffer = graphic_buffer; - } - return error; -} - -status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) { - // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only - // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer. - ALOGV("queueBuffer: buffer=%p", buffer.get()); - - if (out_slot == nullptr) { - ALOGE("attachBuffer: out_slot cannot be NULL."); - return BAD_VALUE; - } - if (buffer == nullptr) { - ALOGE("attachBuffer: invalid GraphicBuffer."); - return BAD_VALUE; - } - - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ == kNoConnectedApi) { - ALOGE("attachBuffer: BufferQueue has no connected producer"); - return NO_INIT; - } - - // Before attaching the buffer, caller is supposed to call - // IGraphicBufferProducer::setGenerationNumber to inform the - // BufferHubProducer the next generation number. - if (buffer->getGenerationNumber() != generation_number_) { - ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.", - buffer->getGenerationNumber(), generation_number_); - return BAD_VALUE; - } - - // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can - // be directly backed by BufferHub. - return INVALID_OPERATION; -} - -status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input, - QueueBufferOutput* output) { - ALOGV("queueBuffer: slot %d", slot); - - if (output == nullptr) { - return BAD_VALUE; - } - - int64_t timestamp; - bool is_auto_timestamp; - android_dataspace dataspace; - Rect crop(Rect::EMPTY_RECT); - int scaling_mode; - uint32_t transform; - sp<Fence> fence; - - input.deflate(×tamp, &is_auto_timestamp, &dataspace, &crop, &scaling_mode, &transform, - &fence); - - // Check input scaling mode is valid. - switch (scaling_mode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: - case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: - break; - default: - ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode); - return BAD_VALUE; - } - - // Check input fence is valid. - if (fence == nullptr) { - ALOGE("queueBuffer: fence is NULL"); - return BAD_VALUE; - } - - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ == kNoConnectedApi) { - ALOGE("queueBuffer: BufferQueue has no connected producer"); - return NO_INIT; - } - - if (slot < 0 || slot >= max_buffer_count_) { - ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_); - return BAD_VALUE; - } else if (!buffers_[slot].mBufferState.isDequeued()) { - ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", slot, - buffers_[slot].mBufferState.string()); - return BAD_VALUE; - } else if ((!buffers_[slot].mRequestBufferCalled || buffers_[slot].mGraphicBuffer == nullptr)) { - ALOGE("queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, " - "mGraphicBuffer=%p)", - slot, buffers_[slot].mRequestBufferCalled, buffers_[slot].mGraphicBuffer.get()); - return BAD_VALUE; - } - - // Post the producer buffer with timestamp in the metadata. - const auto& producer_buffer = buffers_[slot].mProducerBuffer; - - // Check input crop is not out of boundary of current buffer. - Rect buffer_rect(producer_buffer->width(), producer_buffer->height()); - Rect cropped_rect(Rect::EMPTY_RECT); - crop.intersect(buffer_rect, &cropped_rect); - if (cropped_rect != crop) { - ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot); - return BAD_VALUE; - } - - LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1); - - DvrNativeBufferMetadata meta_data; - meta_data.timestamp = timestamp; - meta_data.is_auto_timestamp = int32_t(is_auto_timestamp); - meta_data.dataspace = int32_t(dataspace); - meta_data.crop_left = crop.left; - meta_data.crop_top = crop.top; - meta_data.crop_right = crop.right; - meta_data.crop_bottom = crop.bottom; - meta_data.scaling_mode = int32_t(scaling_mode); - meta_data.transform = int32_t(transform); - - producer_buffer->PostAsync(&meta_data, fence_fd); - buffers_[slot].mBufferState.queue(); - - output->width = producer_buffer->width(); - output->height = producer_buffer->height(); - output->transformHint = 0; // default value, we don't use it yet. - - // |numPendingBuffers| counts of the number of buffers that has been enqueued - // by the producer but not yet acquired by the consumer. Due to the nature - // of BufferHubQueue design, this is hard to trace from the producer's client - // side, but it's safe to assume it's zero. - output->numPendingBuffers = 0; - - // Note that we are not setting nextFrameNumber here as it seems to be only - // used by surface flinger. See more at b/22802885, ag/791760. - output->nextFrameNumber = 0; - - return NO_ERROR; -} - -status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) { - ALOGV(__FUNCTION__); - - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ == kNoConnectedApi) { - ALOGE("cancelBuffer: BufferQueue has no connected producer"); - return NO_INIT; - } - - if (slot < 0 || slot >= max_buffer_count_) { - ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_); - return BAD_VALUE; - } else if (!buffers_[slot].mBufferState.isDequeued()) { - ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", slot, - buffers_[slot].mBufferState.string()); - return BAD_VALUE; - } else if (fence == nullptr) { - ALOGE("cancelBuffer: fence is NULL"); - return BAD_VALUE; - } - - auto producer_buffer = buffers_[slot].mProducerBuffer; - queue_->Enqueue(producer_buffer, size_t(slot), 0U); - buffers_[slot].mBufferState.cancel(); - buffers_[slot].mFence = fence; - ALOGV("cancelBuffer: slot %d", slot); - - return NO_ERROR; -} - -status_t BufferHubProducer::query(int what, int* out_value) { - ALOGV(__FUNCTION__); - - std::unique_lock<std::mutex> lock(mutex_); - - if (out_value == nullptr) { - ALOGE("query: out_value was NULL"); - return BAD_VALUE; - } - - int value = 0; - switch (what) { - case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - // TODO(b/36187402) This should be the maximum number of buffers that this - // producer queue's consumer can acquire. Set to be at least one. Need to - // find a way to set from the consumer side. - value = kDefaultUndequeuedBuffers; - break; - case NATIVE_WINDOW_BUFFER_AGE: - value = 0; - break; - case NATIVE_WINDOW_WIDTH: - value = int32_t(queue_->default_width()); - break; - case NATIVE_WINDOW_HEIGHT: - value = int32_t(queue_->default_height()); - break; - case NATIVE_WINDOW_FORMAT: - value = int32_t(queue_->default_format()); - break; - case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: - // BufferHubQueue is always operating in async mode, thus semantically - // consumer can never be running behind. See BufferQueueCore.cpp core - // for more information about the original meaning of this flag. - value = 0; - break; - case NATIVE_WINDOW_CONSUMER_USAGE_BITS: - // TODO(jwcai) This is currently not implement as we don't need - // IGraphicBufferConsumer parity. - value = 0; - break; - case NATIVE_WINDOW_DEFAULT_DATASPACE: - // TODO(jwcai) Return the default value android::BufferQueue is using as - // there is no way dvr::ConsumerQueue can set it. - value = 0; // HAL_DATASPACE_UNKNOWN - break; - case NATIVE_WINDOW_STICKY_TRANSFORM: - // TODO(jwcai) Return the default value android::BufferQueue is using as - // there is no way dvr::ConsumerQueue can set it. - value = 0; - break; - case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: - // In Daydream's implementation, the consumer end (i.e. VR Compostior) - // knows how to handle protected buffers. - value = 1; - break; - default: - return BAD_VALUE; - } - - ALOGV("query: key=%d, v=%d", what, value); - *out_value = value; - return NO_ERROR; -} - -status_t BufferHubProducer::connect(const sp<IProducerListener>& /* listener */, int api, - bool /* producer_controlled_by_app */, - QueueBufferOutput* output) { - // Consumer interaction are actually handled by buffer hub, and we need - // to maintain consumer operations here. We only need to perform basic input - // parameter checks here. - ALOGV(__FUNCTION__); - - if (output == nullptr) { - return BAD_VALUE; - } - - std::unique_lock<std::mutex> lock(mutex_); - - if (connected_api_ != kNoConnectedApi) { - return BAD_VALUE; - } - - if (!queue_->is_connected()) { - ALOGE("BufferHubProducer::connect: This BufferHubProducer is not " - "connected to bufferhud. Has it been taken out as a parcelable?"); - return BAD_VALUE; - } - - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - connected_api_ = api; - - output->width = queue_->default_width(); - output->height = queue_->default_height(); - - // default values, we don't use them yet. - output->transformHint = 0; - output->numPendingBuffers = 0; - output->nextFrameNumber = 0; - output->bufferReplaced = false; - - break; - default: - ALOGE("BufferHubProducer::connect: unknow API %d", api); - return BAD_VALUE; - } - - return NO_ERROR; -} - -status_t BufferHubProducer::disconnect(int api, DisconnectMode /*mode*/) { - // Consumer interaction are actually handled by buffer hub, and we need - // to maintain consumer operations here. We only need to perform basic input - // parameter checks here. - ALOGV(__FUNCTION__); - - std::unique_lock<std::mutex> lock(mutex_); - - if (kNoConnectedApi == connected_api_) { - return NO_INIT; - } else if (api != connected_api_) { - return BAD_VALUE; - } - - FreeAllBuffers(); - connected_api_ = kNoConnectedApi; - return NO_ERROR; -} - -status_t BufferHubProducer::setSidebandStream(const sp<NativeHandle>& stream) { - if (stream != nullptr) { - // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's - // metadata. - ALOGE("SidebandStream is not currently supported."); - return INVALID_OPERATION; - } - return NO_ERROR; -} - -void BufferHubProducer::allocateBuffers(uint32_t /* width */, uint32_t /* height */, - PixelFormat /* format */, uint64_t /* usage */) { - // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number - // of buffers permitted by the current BufferQueue configuration (aka - // |max_buffer_count_|). - ALOGE("BufferHubProducer::allocateBuffers not implemented."); -} - -status_t BufferHubProducer::allowAllocation(bool /* allow */) { - ALOGE("BufferHubProducer::allowAllocation not implemented."); - return INVALID_OPERATION; -} - -status_t BufferHubProducer::setGenerationNumber(uint32_t generation_number) { - ALOGV(__FUNCTION__); - - std::unique_lock<std::mutex> lock(mutex_); - generation_number_ = generation_number; - return NO_ERROR; -} - -String8 BufferHubProducer::getConsumerName() const { - // BufferHub based implementation could have one to many producer/consumer - // relationship, thus |getConsumerName| from the producer side does not - // make any sense. - ALOGE("BufferHubProducer::getConsumerName not supported."); - return String8("BufferHubQueue::StubConsumer"); -} - -status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) { - if (shared_buffer_mode) { - ALOGE("BufferHubProducer::setSharedBufferMode(true) is not supported."); - // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow. - return INVALID_OPERATION; - } - // Setting to default should just work as a no-op. - return NO_ERROR; -} - -status_t BufferHubProducer::setAutoRefresh(bool auto_refresh) { - if (auto_refresh) { - ALOGE("BufferHubProducer::setAutoRefresh(true) is not supported."); - return INVALID_OPERATION; - } - // Setting to default should just work as a no-op. - return NO_ERROR; -} - -status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) { - ALOGV(__FUNCTION__); - - std::unique_lock<std::mutex> lock(mutex_); - dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000)); - return NO_ERROR; -} - -status_t BufferHubProducer::getLastQueuedBuffer(sp<GraphicBuffer>* /* out_buffer */, - sp<Fence>* /* out_fence */, - float /*out_transform_matrix*/[16]) { - ALOGE("BufferHubProducer::getLastQueuedBuffer not implemented."); - return INVALID_OPERATION; -} - -void BufferHubProducer::getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) { - ALOGE("BufferHubProducer::getFrameTimestamps not implemented."); -} - -status_t BufferHubProducer::getUniqueId(uint64_t* out_id) const { - ALOGV(__FUNCTION__); - - *out_id = unique_id_; - return NO_ERROR; -} - -status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const { - ALOGV(__FUNCTION__); - - // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS - *out_usage = 0; - return NO_ERROR; -} - -status_t BufferHubProducer::TakeAsParcelable(ProducerQueueParcelable* out_parcelable) { - if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE; - - if (connected_api_ != kNoConnectedApi) { - ALOGE("BufferHubProducer::TakeAsParcelable: BufferHubProducer has " - "connected client. Must disconnect first."); - return BAD_VALUE; - } - - if (!queue_->is_connected()) { - ALOGE("BufferHubProducer::TakeAsParcelable: This BufferHubProducer " - "is not connected to bufferhud. Has it been taken out as a " - "parcelable?"); - return BAD_VALUE; - } - - auto status = queue_->TakeAsParcelable(); - if (!status) { - ALOGE("BufferHubProducer::TakeAsParcelable: Failed to take out " - "ProducuerQueueParcelable from the producer queue, error: %s.", - status.GetErrorMessage().c_str()); - return BAD_VALUE; - } - - *out_parcelable = status.take(); - return NO_ERROR; -} - -status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, - PixelFormat format, uint64_t usage) { - auto status = queue_->AllocateBuffer(width, height, layer_count, uint32_t(format), usage); - if (!status) { - ALOGE("BufferHubProducer::AllocateBuffer: Failed to allocate buffer: %s", - status.GetErrorMessage().c_str()); - return NO_MEMORY; - } - - size_t slot = status.get(); - auto producer_buffer = queue_->GetBuffer(slot); - - LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr, - "Failed to get the producer buffer at slot: %zu", slot); - - buffers_[slot].mProducerBuffer = producer_buffer; - - return NO_ERROR; -} - -status_t BufferHubProducer::RemoveBuffer(size_t slot) { - auto status = queue_->RemoveBuffer(slot); - if (!status) { - ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.", - slot, status.GetErrorMessage().c_str()); - return INVALID_OPERATION; - } - - // Reset in memory objects related the the buffer. - buffers_[slot].mProducerBuffer = nullptr; - buffers_[slot].mBufferState.detachProducer(); - buffers_[slot].mFence = Fence::NO_FENCE; - buffers_[slot].mGraphicBuffer = nullptr; - buffers_[slot].mRequestBufferCalled = false; - return NO_ERROR; -} - -status_t BufferHubProducer::FreeAllBuffers() { - for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { - // Reset in memory objects related the the buffer. - buffers_[slot].mProducerBuffer = nullptr; - buffers_[slot].mBufferState.reset(); - buffers_[slot].mFence = Fence::NO_FENCE; - buffers_[slot].mGraphicBuffer = nullptr; - buffers_[slot].mRequestBufferCalled = false; - } - - auto status = queue_->FreeAllBuffers(); - if (!status) { - ALOGE("BufferHubProducer::FreeAllBuffers: Failed to free all buffers on " - "the queue: %s", - status.GetErrorMessage().c_str()); - } - - if (queue_->capacity() != 0 || queue_->count() != 0) { - LOG_ALWAYS_FATAL("BufferHubProducer::FreeAllBuffers: Not all buffers are freed."); - } - - return NO_ERROR; -} - -status_t BufferHubProducer::exportToParcel(Parcel* parcel) { - status_t res = TakeAsParcelable(&pending_producer_parcelable_); - if (res != NO_ERROR) return res; - - if (!pending_producer_parcelable_.IsValid()) { - ALOGE("BufferHubProducer::exportToParcel: Invalid parcelable object."); - return BAD_VALUE; - } - - res = parcel->writeUint32(USE_BUFFER_HUB); - if (res != NO_ERROR) { - ALOGE("BufferHubProducer::exportToParcel: Cannot write magic, res=%d.", res); - return res; - } - - return pending_producer_parcelable_.writeToParcel(parcel); -} - -IBinder* BufferHubProducer::onAsBinder() { - ALOGE("BufferHubProducer::onAsBinder: BufferHubProducer should never be used as an Binder " - "object."); - return nullptr; -} - -} // namespace android diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index c1d92a2a7f..66cad03fec 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -18,11 +18,6 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 -#ifndef NO_BUFFERHUB -#include <gui/BufferHubConsumer.h> -#include <gui/BufferHubProducer.h> -#endif - #include <gui/BufferQueue.h> #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> @@ -127,32 +122,4 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, *outConsumer = consumer; } -#ifndef NO_BUFFERHUB -void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, - sp<IGraphicBufferConsumer>* outConsumer) { - LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); - - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - - dvr::ProducerQueueConfigBuilder configBuilder; - std::shared_ptr<dvr::ProducerQueue> producerQueue = - dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{}); - LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue."); - - std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue(); - LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue."); - - producer = BufferHubProducer::Create(producerQueue); - consumer = BufferHubConsumer::Create(consumerQueue); - - LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); - LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); - - *outProducer = producer; - *outConsumer = consumer; -} -#endif - }; // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 797069c798..918ff2dd25 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -27,10 +27,6 @@ #include <binder/Parcel.h> #include <binder/IInterface.h> -#ifndef NO_BUFFERHUB -#include <gui/BufferHubProducer.h> -#endif - #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> #include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> #include <gui/BufferQueueDefs.h> @@ -1012,17 +1008,7 @@ sp<IGraphicBufferProducer> IGraphicBufferProducer::createFromParcel(const Parcel } case USE_BUFFER_HUB: { ALOGE("createFromParcel: BufferHub not implemented."); -#ifndef NO_BUFFERHUB - dvr::ProducerQueueParcelable producerParcelable; - res = producerParcelable.readFromParcel(parcel); - if (res != NO_ERROR) { - ALOGE("createFromParcel: Failed to read from parcel, error=%d", res); - return nullptr; - } - return BufferHubProducer::Create(std::move(producerParcelable)); -#else return nullptr; -#endif } default: { ALOGE("createFromParcel: Unexpected mgaic: 0x%x.", outMagic); diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 45c958ebe4..05b55337ab 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -2,9 +2,9 @@ adyabr@google.com alecmouri@google.com chaviw@google.com chrisforbes@google.com -jessehall@google.com +jreck@google.com lpy@google.com -mathias@google.com +pdwilliams@google.com racarr@google.com vishnun@google.com diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 4c8079265a..8f45425929 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1107,9 +1107,12 @@ void Surface::applyGrallocMetadataLocked( ATRACE_CALL(); auto& mapper = GraphicBufferMapper::get(); mapper.setDataspace(buffer->handle, static_cast<ui::Dataspace>(queueBufferInput.dataSpace)); - mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086()); - mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613()); - mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus()); + if (mHdrMetadataIsSet & HdrMetadata::SMPTE2086) + mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086()); + if (mHdrMetadataIsSet & HdrMetadata::CTA861_3) + mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613()); + if (mHdrMetadataIsSet & HdrMetadata::HDR10PLUS) + mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus()); } void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, @@ -1944,6 +1947,7 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { mReqHeight = 0; mReqUsage = 0; mCrop.clear(); + mDataSpace = Dataspace::UNKNOWN; mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mTransform = 0; mStickyTransform = 0; @@ -2250,6 +2254,7 @@ int Surface::setBuffersDataSpace(Dataspace dataSpace) int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) { ALOGV("Surface::setBuffersSmpte2086Metadata"); Mutex::Autolock lock(mMutex); + mHdrMetadataIsSet |= HdrMetadata::SMPTE2086; if (metadata) { mHdrMetadata.smpte2086 = *metadata; mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086; @@ -2262,6 +2267,7 @@ int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metad int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) { ALOGV("Surface::setBuffersCta8613Metadata"); Mutex::Autolock lock(mMutex); + mHdrMetadataIsSet |= HdrMetadata::CTA861_3; if (metadata) { mHdrMetadata.cta8613 = *metadata; mHdrMetadata.validTypes |= HdrMetadata::CTA861_3; @@ -2274,6 +2280,7 @@ int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) { ALOGV("Surface::setBuffersBlobMetadata"); Mutex::Autolock lock(mMutex); + mHdrMetadataIsSet |= HdrMetadata::HDR10PLUS; if (size > 0) { mHdrMetadata.hdr10plus.assign(metadata, metadata + size); mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS; diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h deleted file mode 100644 index d38077014b..0000000000 --- a/libs/gui/include/gui/BufferHubConsumer.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2018 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. - */ - -#ifndef ANDROID_GUI_BUFFERHUBCONSUMER_H_ -#define ANDROID_GUI_BUFFERHUBCONSUMER_H_ - -#include <gui/IGraphicBufferConsumer.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/buffer_hub_queue_parcelable.h> - -namespace android { - -class BufferHubConsumer : public IGraphicBufferConsumer { -public: - // Creates a BufferHubConsumer instance by importing an existing producer queue. - static sp<BufferHubConsumer> Create(const std::shared_ptr<dvr::ConsumerQueue>& queue); - - // Creates a BufferHubConsumer instance by importing an existing producer - // parcelable. Note that this call takes the ownership of the parcelable - // object and is guaranteed to succeed if parcelable object is valid. - static sp<BufferHubConsumer> Create(dvr::ConsumerQueueParcelable parcelable); - - // See |IGraphicBufferConsumer::acquireBuffer| - status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, - uint64_t maxFrameNumber = 0) override; - - // See |IGraphicBufferConsumer::detachBuffer| - status_t detachBuffer(int slot) override; - - // See |IGraphicBufferConsumer::attachBuffer| - status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override; - - // See |IGraphicBufferConsumer::releaseBuffer| - status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, - const sp<Fence>& releaseFence) override; - - // See |IGraphicBufferConsumer::consumerConnect| - status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override; - - // See |IGraphicBufferConsumer::consumerDisconnect| - status_t consumerDisconnect() override; - - // See |IGraphicBufferConsumer::getReleasedBuffers| - status_t getReleasedBuffers(uint64_t* slotMask) override; - - // See |IGraphicBufferConsumer::setDefaultBufferSize| - status_t setDefaultBufferSize(uint32_t w, uint32_t h) override; - - // See |IGraphicBufferConsumer::setMaxBufferCount| - status_t setMaxBufferCount(int bufferCount) override; - - // See |IGraphicBufferConsumer::setMaxAcquiredBufferCount| - status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override; - - // See |IGraphicBufferConsumer::setConsumerName| - status_t setConsumerName(const String8& name) override; - - // See |IGraphicBufferConsumer::setDefaultBufferFormat| - status_t setDefaultBufferFormat(PixelFormat defaultFormat) override; - - // See |IGraphicBufferConsumer::setDefaultBufferDataSpace| - status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override; - - // See |IGraphicBufferConsumer::setConsumerUsageBits| - status_t setConsumerUsageBits(uint64_t usage) override; - - // See |IGraphicBufferConsumer::setConsumerIsProtected| - status_t setConsumerIsProtected(bool isProtected) override; - - // See |IGraphicBufferConsumer::setTransformHint| - status_t setTransformHint(uint32_t hint) override; - - // See |IGraphicBufferConsumer::getSidebandStream| - status_t getSidebandStream(sp<NativeHandle>* outStream) const override; - - // See |IGraphicBufferConsumer::getOccupancyHistory| - status_t getOccupancyHistory(bool forceFlush, - std::vector<OccupancyTracker::Segment>* outHistory) override; - - // See |IGraphicBufferConsumer::discardFreeBuffers| - status_t discardFreeBuffers() override; - - // See |IGraphicBufferConsumer::dumpState| - status_t dumpState(const String8& prefix, String8* outResult) const override; - - // BufferHubConsumer provides its own logic to cast to a binder object. - IBinder* onAsBinder() override; - -private: - // Private constructor to force use of |Create|. - BufferHubConsumer() = default; - - // Concrete implementation backed by BufferHubBuffer. - std::shared_ptr<dvr::ConsumerQueue> mQueue; -}; - -} // namespace android - -#endif // ANDROID_GUI_BUFFERHUBCONSUMER_H_ diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h deleted file mode 100644 index 0e925ceb3b..0000000000 --- a/libs/gui/include/gui/BufferHubProducer.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2018 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. - */ - -#ifndef ANDROID_GUI_BUFFERHUBPRODUCER_H_ -#define ANDROID_GUI_BUFFERHUBPRODUCER_H_ - -#include <gui/BufferSlot.h> -#include <gui/IGraphicBufferProducer.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/buffer_hub_queue_parcelable.h> - -namespace android { - -class BufferHubProducer : public IGraphicBufferProducer { -public: - static constexpr int kNoConnectedApi = -1; - - // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer - // side logic doesn't limit the number of buffer it can acquire - // simultaneously. We need a way for consumer logic to configure and enforce - // that. - static constexpr int kDefaultUndequeuedBuffers = 1; - - // Creates a BufferHubProducer instance by importing an existing prodcuer - // queue. - static sp<BufferHubProducer> Create(const std::shared_ptr<dvr::ProducerQueue>& producer); - - // Creates a BufferHubProducer instance by importing an existing prodcuer - // parcelable. Note that this call takes the ownership of the parcelable - // object and is guaranteed to succeed if parcelable object is valid. - static sp<BufferHubProducer> Create(dvr::ProducerQueueParcelable parcelable); - - // See |IGraphicBufferProducer::requestBuffer| - status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override; - - // For the BufferHub based implementation. All buffers in the queue are - // allowed to be dequeued from the consumer side. It call always returns - // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting - // |max_dequeued_buffers| here can be considered the same as setting queue - // capacity. - // - // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info - status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override; - - // See |IGraphicBufferProducer::setAsyncMode| - status_t setAsyncMode(bool async) override; - - // See |IGraphicBufferProducer::dequeueBuffer| - status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage, uint64_t* outBufferAge, - FrameEventHistoryDelta* outTimestamps) override; - - // See |IGraphicBufferProducer::detachBuffer| - status_t detachBuffer(int slot) override; - - // See |IGraphicBufferProducer::detachNextBuffer| - status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) override; - - // See |IGraphicBufferProducer::attachBuffer| - status_t attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) override; - - // See |IGraphicBufferProducer::queueBuffer| - status_t queueBuffer(int slot, const QueueBufferInput& input, - QueueBufferOutput* output) override; - - // See |IGraphicBufferProducer::cancelBuffer| - status_t cancelBuffer(int slot, const sp<Fence>& fence) override; - - // See |IGraphicBufferProducer::query| - status_t query(int what, int* out_value) override; - - // See |IGraphicBufferProducer::connect| - status_t connect(const sp<IProducerListener>& listener, int api, - bool producer_controlled_by_app, QueueBufferOutput* output) override; - - // See |IGraphicBufferProducer::disconnect| - status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override; - - // See |IGraphicBufferProducer::setSidebandStream| - status_t setSidebandStream(const sp<NativeHandle>& stream) override; - - // See |IGraphicBufferProducer::allocateBuffers| - void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, - uint64_t usage) override; - - // See |IGraphicBufferProducer::allowAllocation| - status_t allowAllocation(bool allow) override; - - // See |IGraphicBufferProducer::setGenerationNumber| - status_t setGenerationNumber(uint32_t generation_number) override; - - // See |IGraphicBufferProducer::getConsumerName| - String8 getConsumerName() const override; - - // See |IGraphicBufferProducer::setSharedBufferMode| - status_t setSharedBufferMode(bool shared_buffer_mode) override; - - // See |IGraphicBufferProducer::setAutoRefresh| - status_t setAutoRefresh(bool auto_refresh) override; - - // See |IGraphicBufferProducer::setDequeueTimeout| - status_t setDequeueTimeout(nsecs_t timeout) override; - - // See |IGraphicBufferProducer::getLastQueuedBuffer| - status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence, - float out_transform_matrix[16]) override; - - // See |IGraphicBufferProducer::getFrameTimestamps| - void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override; - - // See |IGraphicBufferProducer::getUniqueId| - status_t getUniqueId(uint64_t* out_id) const override; - - // See |IGraphicBufferProducer::getConsumerUsage| - status_t getConsumerUsage(uint64_t* out_usage) const override; - - // Takes out the current producer as a binder parcelable object. Note that the - // producer must be disconnected to be exportable. After successful export, - // the producer queue can no longer be connected again. Returns NO_ERROR when - // takeout is successful and out_parcelable will hold the new parcelable - // object. Also note that out_parcelable cannot be NULL and must points to an - // invalid parcelable. - status_t TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable); - - IBinder* onAsBinder() override; - -protected: - // See |IGraphicBufferProducer::exportToParcel| - status_t exportToParcel(Parcel* parcel) override; - -private: - using LocalHandle = pdx::LocalHandle; - - // Private constructor to force use of |Create|. - BufferHubProducer() {} - - static uint64_t genUniqueId() { - static std::atomic<uint32_t> counter{0}; - static uint64_t id = static_cast<uint64_t>(getpid()) << 32; - return id | counter++; - } - - // Allocate new buffer through BufferHub and add it into |queue_| for - // bookkeeping. - status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, - PixelFormat format, uint64_t usage); - - // Remove a buffer via BufferHubRPC. - status_t RemoveBuffer(size_t slot); - - // Free all buffers which are owned by the prodcuer. Note that if graphic - // buffers are acquired by the consumer, we can't . - status_t FreeAllBuffers(); - - // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been - // locked already. - status_t DetachBufferLocked(size_t slot); - - // Concreate implementation backed by BufferHubBuffer. - std::shared_ptr<dvr::ProducerQueue> queue_; - - // Mutex for thread safety. - std::mutex mutex_; - - // Connect client API, should be one of the NATIVE_WINDOW_API_* flags. - int connected_api_{kNoConnectedApi}; - - // |max_buffer_count_| sets the capacity of the underlying buffer queue. - int32_t max_buffer_count_{dvr::BufferHubQueue::kMaxQueueCapacity}; - - // |max_dequeued_buffer_count_| set the maximum number of buffers that can - // be dequeued at the same momment. - int32_t max_dequeued_buffer_count_{1}; - - // Sets how long dequeueBuffer or attachBuffer will block if a buffer or - // slot is not yet available. The timeout is stored in milliseconds. - int dequeue_timeout_ms_{dvr::BufferHubQueue::kNoTimeOut}; - - // |generation_number_| stores the current generation number of the attached - // producer. Any attempt to attach a buffer with a different generation - // number will fail. - // TOOD(b/38137191) Currently not used as we don't support - // IGraphicBufferProducer::detachBuffer. - uint32_t generation_number_{0}; - - // |buffers_| stores the buffers that have been dequeued from - // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets - // filled in with the result of |Dequeue|. - // TODO(jwcai) The buffer allocated to a slot will also be replaced if the - // requested buffer usage or geometry differs from that of the buffer - // allocated to a slot. - struct BufferHubSlot : public BufferSlot { - BufferHubSlot() : mProducerBuffer(nullptr), mIsReallocating(false) {} - // BufferSlot comes from android framework, using m prefix to comply with - // the name convention with the reset of data fields from BufferSlot. - std::shared_ptr<dvr::ProducerBuffer> mProducerBuffer; - bool mIsReallocating; - }; - BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity]; - - // A uniqueId used by IGraphicBufferProducer interface. - const uint64_t unique_id_{genUniqueId()}; - - // A pending parcelable object which keeps the bufferhub channel alive. - dvr::ProducerQueueParcelable pending_producer_parcelable_; -}; - -} // namespace android - -#endif // ANDROID_GUI_BUFFERHUBPRODUCER_H_ diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h index 91f80d2f17..690587f0e6 100644 --- a/libs/gui/include/gui/BufferQueue.h +++ b/libs/gui/include/gui/BufferQueue.h @@ -82,12 +82,6 @@ public: sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger = false); -#ifndef NO_BUFFERHUB - // Creates an IGraphicBufferProducer and IGraphicBufferConsumer pair backed by BufferHub. - static void createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, - sp<IGraphicBufferConsumer>* outConsumer); -#endif - BufferQueue() = delete; // Create through createBufferQueue }; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 8d4e15bb40..4a552b6643 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -111,6 +111,24 @@ public: return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr; } + static sp<IGraphicBufferProducer> getIGraphicBufferProducer(ANativeWindow* window) { + int val; + if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 && + val == NATIVE_WINDOW_SURFACE) { + return ((Surface*) window)->mGraphicBufferProducer; + } + return nullptr; + } + + static sp<IBinder> getSurfaceControlHandle(ANativeWindow* window) { + int val; + if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 && + val == NATIVE_WINDOW_SURFACE) { + return ((Surface*) window)->mSurfaceControlHandle; + } + return nullptr; + } + /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. * * A sideband stream is a device-specific mechanism for passing buffers @@ -462,6 +480,11 @@ protected: // queue operation. There is no HDR metadata by default. HdrMetadata mHdrMetadata; + // mHdrMetadataIsSet is a bitfield to track which HDR metadata has been set. + // Prevent Surface from resetting HDR metadata that was set on a bufer when + // HDR metadata is not set on this Surface. + uint32_t mHdrMetadataIsSet{0}; + // mCrop is the crop rectangle that will be used for the next buffer // that gets queued. It is set by calling setCrop. Rect mCrop; diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index e58543a245..fa54c7d1f6 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -15,7 +15,6 @@ cc_test { name: "libgui_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -75,7 +74,6 @@ cc_test { name: "libgui_multilib_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -94,44 +92,9 @@ cc_test { header_libs: ["libsurfaceflinger_headers"], } -// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) -// This test has a main method, and requires a separate binary to be built. -// To add move tests like this, just add additional cc_test statements, -// as opposed to adding more source files to this one. -cc_test { - name: "SurfaceParcelable_test", - test_suites: ["device-tests"], - - clang: true, - cflags: [ - "-Wall", - "-Werror", - ], - - srcs: [ - "SurfaceParcelable_test.cpp", - ], - - shared_libs: [ - "liblog", - "libbinder", - "libcutils", - "libgui", - "libui", - "libutils", - "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui. - "libpdx_default_transport", - ], - - header_libs: [ - "libdvr_headers", - ], -} - cc_test { name: "SamplingDemo", - clang: true, cflags: [ "-Wall", "-Werror", diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 2af2fe1f91..3427731fff 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -42,10 +42,6 @@ #define TEST_CONTROLLED_BY_APP false #define TEST_PRODUCER_USAGE_BITS (0) -#ifndef USE_BUFFER_HUB_AS_BUFFER_QUEUE -#define USE_BUFFER_HUB_AS_BUFFER_QUEUE 0 -#endif - namespace android { namespace { @@ -77,7 +73,6 @@ namespace { // Enums to control which IGraphicBufferProducer backend to test. enum IGraphicBufferProducerTestCode { USE_BUFFER_QUEUE_PRODUCER = 0, - USE_BUFFER_HUB_PRODUCER, }; }; // namespace anonymous @@ -99,10 +94,6 @@ protected: BufferQueue::createBufferQueue(&mProducer, &mConsumer); break; } - case USE_BUFFER_HUB_PRODUCER: { - BufferQueue::createBufferHubQueue(&mProducer, &mConsumer); - break; - } default: { // Should never reach here. LOG_ALWAYS_FATAL("Invalid test params: %u", GetParam()); @@ -880,11 +871,6 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) { } TEST_P(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) { - if (GetParam() == USE_BUFFER_HUB_PRODUCER) { - // TODO(b/36724099): Add support for BufferHubProducer::setAsyncMode(true) - return; - } - ASSERT_OK(mConsumer->setMaxAcquiredBufferCount(1)) << "maxAcquire: " << 1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_OK(mProducer->setAsyncMode(true)) << "async mode: " << true; @@ -1117,14 +1103,7 @@ TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { } } -#if USE_BUFFER_HUB_AS_BUFFER_QUEUE -INSTANTIATE_TEST_CASE_P(IGraphicBufferProducerBackends, IGraphicBufferProducerTest, - ::testing::Values(USE_BUFFER_QUEUE_PRODUCER, USE_BUFFER_HUB_PRODUCER)); -#else -// TODO(b/70046255): Remove the #ifdef here and always tests both backends once BufferHubQueue can -// pass all existing libgui tests. INSTANTIATE_TEST_CASE_P(IGraphicBufferProducerBackends, IGraphicBufferProducerTest, ::testing::Values(USE_BUFFER_QUEUE_PRODUCER)); -#endif } // namespace android diff --git a/libs/gui/tests/SurfaceParcelable_test.cpp b/libs/gui/tests/SurfaceParcelable_test.cpp deleted file mode 100644 index 686dc82f3e..0000000000 --- a/libs/gui/tests/SurfaceParcelable_test.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2018 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 "SurfaceParcelable_test" - -#include <gtest/gtest.h> - -#include <binder/IServiceManager.h> -#include <binder/ProcessState.h> -#include <gui/BufferHubProducer.h> -#include <gui/BufferQueue.h> -#include <gui/view/Surface.h> -#include <utils/Log.h> - -namespace android { - -static const String16 kTestServiceName = String16("SurfaceParcelableTestService"); -static const String16 kSurfaceName = String16("TEST_SURFACE"); -static const uint32_t kBufferWidth = 100; -static const uint32_t kBufferHeight = 1; -static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; - -enum SurfaceParcelableTestServiceCode { - CREATE_BUFFER_QUEUE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, - CREATE_BUFFER_HUB_SURFACE, -}; - -class SurfaceParcelableTestService : public BBinder { -public: - SurfaceParcelableTestService() { - // BufferQueue - BufferQueue::createBufferQueue(&mBufferQueueProducer, &mBufferQueueConsumer); - - // BufferHub - dvr::ProducerQueueConfigBuilder configBuilder; - mProducerQueue = dvr::ProducerQueue::Create(configBuilder.SetDefaultWidth(kBufferWidth) - .SetDefaultHeight(kBufferHeight) - .SetDefaultFormat(kBufferFormat) - .Build(), - dvr::UsagePolicy{}); - mBufferHubProducer = BufferHubProducer::Create(mProducerQueue); - } - - ~SurfaceParcelableTestService() = default; - - virtual status_t onTransact(uint32_t code, const Parcel& /*data*/, Parcel* reply, - uint32_t /*flags*/ = 0) { - switch (code) { - case CREATE_BUFFER_QUEUE_SURFACE: { - view::Surface surfaceShim; - surfaceShim.name = kSurfaceName; - surfaceShim.graphicBufferProducer = mBufferQueueProducer; - return surfaceShim.writeToParcel(reply); - } - case CREATE_BUFFER_HUB_SURFACE: { - view::Surface surfaceShim; - surfaceShim.name = kSurfaceName; - surfaceShim.graphicBufferProducer = mBufferHubProducer; - return surfaceShim.writeToParcel(reply); - } - default: - return UNKNOWN_TRANSACTION; - }; - } - -protected: - sp<IGraphicBufferProducer> mBufferQueueProducer; - sp<IGraphicBufferConsumer> mBufferQueueConsumer; - - std::shared_ptr<dvr::ProducerQueue> mProducerQueue; - sp<IGraphicBufferProducer> mBufferHubProducer; -}; - -static int runBinderServer() { - ProcessState::self()->startThreadPool(); - - sp<IServiceManager> sm = defaultServiceManager(); - sp<SurfaceParcelableTestService> service = new SurfaceParcelableTestService; - sm->addService(kTestServiceName, service, false); - - ALOGI("Binder server running..."); - - while (true) { - int stat, retval; - retval = wait(&stat); - if (retval == -1 && errno == ECHILD) { - break; - } - } - - ALOGI("Binder server exiting..."); - return 0; -} - -class SurfaceParcelableTest : public ::testing::TestWithParam<uint32_t> { -protected: - virtual void SetUp() { - mService = defaultServiceManager()->getService(kTestServiceName); - if (mService == nullptr) { - ALOGE("Failed to connect to the test service."); - return; - } - - ALOGI("Binder service is ready for client."); - } - - status_t GetSurface(view::Surface* surfaceShim) { - ALOGI("...Test: %d", GetParam()); - - uint32_t opCode = GetParam(); - Parcel data; - Parcel reply; - status_t error = mService->transact(opCode, data, &reply); - if (error != NO_ERROR) { - ALOGE("Failed to get surface over binder, error=%d.", error); - return error; - } - - error = surfaceShim->readFromParcel(&reply); - if (error != NO_ERROR) { - ALOGE("Failed to get surface from parcel, error=%d.", error); - return error; - } - - return NO_ERROR; - } - -private: - sp<IBinder> mService; -}; - -TEST_P(SurfaceParcelableTest, SendOverBinder) { - view::Surface surfaceShim; - EXPECT_EQ(GetSurface(&surfaceShim), NO_ERROR); - EXPECT_EQ(surfaceShim.name, kSurfaceName); - EXPECT_FALSE(surfaceShim.graphicBufferProducer == nullptr); -} - -INSTANTIATE_TEST_CASE_P(SurfaceBackends, SurfaceParcelableTest, - ::testing::Values(CREATE_BUFFER_QUEUE_SURFACE, CREATE_BUFFER_HUB_SURFACE)); - -} // namespace android - -int main(int argc, char** argv) { - pid_t pid = fork(); - if (pid == 0) { - android::ProcessState::self()->startThreadPool(); - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); - - } else { - ALOGI("Test process pid: %d.", pid); - return android::runBinderServer(); - } -} diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 1335e4dfd0..b2fec7917b 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -58,8 +58,6 @@ cc_library { "VirtualKeyMap.cpp", ], - clang: true, - header_libs: ["jni_headers"], export_header_lib_headers: ["jni_headers"], diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 155cb040fb..2b7483d27d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -949,6 +949,8 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << ", actionButton=" << std::to_string(event.getActionButton()); } const size_t pointerCount = event.getPointerCount(); + LOG_ALWAYS_FATAL_IF(pointerCount > MAX_POINTERS, "Too many pointers : pointerCount = %zu", + pointerCount); for (size_t i = 0; i < pointerCount; i++) { out << ", id[" << i << "]=" << event.getPointerId(i); float x = event.getX(i); diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 170e748ca6..d6b4579a94 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -25,8 +25,10 @@ #include <utils/Errors.h> #include <utils/Timers.h> #include <utils/Tokenizer.h> +#if defined(__ANDROID__) #include <vintf/RuntimeInfo.h> #include <vintf/VintfObject.h> +#endif #include <cstdlib> #include <string_view> @@ -79,6 +81,7 @@ static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_ sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()}; bool kernelConfigsArePresent(const std::set<std::string>& configs) { +#if defined(__ANDROID__) std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo = android::vintf::VintfObject::GetInstance()->getRuntimeInfo( vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); @@ -99,6 +102,10 @@ bool kernelConfigsArePresent(const std::set<std::string>& configs) { } } return true; +#else + (void)configs; // Suppress 'unused variable' warning + return true; +#endif } } // namespace @@ -199,78 +206,68 @@ base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor( const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &mKeysByUsageCode.valueAt(index); + auto it = mKeysByUsageCode.find(usageCode); + if (it != mKeysByUsageCode.end()) { + return &it->second; } } if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &mKeysByScanCode.valueAt(index); + auto it = mKeysByScanCode.find(scanCode); + if (it != mKeysByScanCode.end()) { + return &it->second; } } return nullptr; } -status_t KeyLayoutMap::findScanCodesForKey( - int32_t keyCode, std::vector<int32_t>* outScanCodes) const { - const size_t N = mKeysByScanCode.size(); - for (size_t i=0; i<N; i++) { - if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { - outScanCodes->push_back(mKeysByScanCode.keyAt(i)); +std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { + std::vector<int32_t> scanCodes; + for (const auto& [scanCode, key] : mKeysByScanCode) { + if (keyCode == key.keyCode) { + scanCodes.push_back(scanCode); } } - return NO_ERROR; + return scanCodes; } -status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { - ssize_t index = mAxes.indexOfKey(scanCode); - if (index < 0) { +std::optional<AxisInfo> KeyLayoutMap::mapAxis(int32_t scanCode) const { + auto it = mAxes.find(scanCode); + if (it == mAxes.end()) { ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); - return NAME_NOT_FOUND; + return std::nullopt; } - *outAxisInfo = mAxes.valueAt(index); - + const AxisInfo& axisInfo = it->second; ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " "splitValue=%d, flatOverride=%d.", - scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); - - return NO_ERROR; + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + return axisInfo; } -status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { - const size_t N = mLedsByScanCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { - *outScanCode = mLedsByScanCode.keyAt(i); - ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, - *outScanCode); - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findScanCodeForLed(int32_t ledCode) const { + for (const auto& [scanCode, led] : mLedsByScanCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, scanCode=%d.", __func__, ledCode, scanCode); + return scanCode; } } - ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } -status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { - const size_t N = mLedsByUsageCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { - *outUsageCode = mLedsByUsageCode.keyAt(i); - ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, *outUsageCode); - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findUsageCodeForLed(int32_t ledCode) const { + for (const auto& [usageCode, led] : mLedsByUsageCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, usageCode); + return usageCode; } } ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); - - return NAME_NOT_FOUND; + return std::nullopt; } - // --- KeyLayoutMap::Parser --- KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : @@ -345,8 +342,9 @@ status_t KeyLayoutMap::Parser::parseKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Key>& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -387,7 +385,7 @@ status_t KeyLayoutMap::Parser::parseKey() { Key key; key.keyCode = keyCode; key.flags = flags; - map.add(code, key); + map.insert({code, key}); return NO_ERROR; } @@ -400,7 +398,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { scanCodeToken.string()); return BAD_VALUE; } - if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) { ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; @@ -486,8 +484,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { "splitValue=%d, flatOverride=%d.", scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, axisInfo.flatOverride); - - mMap->mAxes.add(scanCode, axisInfo); + mMap->mAxes.insert({scanCode, axisInfo}); return NO_ERROR; } @@ -507,8 +504,9 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } - KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Led>& map = + mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -528,7 +526,7 @@ status_t KeyLayoutMap::Parser::parseLed() { Led led; led.ledCode = ledCode; - map.add(code, led); + map.insert({code, led}); return NO_ERROR; } diff --git a/libs/math/OWNERS b/libs/math/OWNERS index 72d33bc09e..82ae422893 100644 --- a/libs/math/OWNERS +++ b/libs/math/OWNERS @@ -1,3 +1,5 @@ mathias@google.com randolphs@google.com romainguy@google.com +sumir@google.com +jreck@google.com diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index ed728dcb45..4659b96b11 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -33,7 +33,7 @@ license { cc_library_headers { name: "libnativedisplay_headers", - export_include_dirs: ["include",], + export_include_dirs: ["include"], } cc_library_shared { @@ -43,8 +43,6 @@ cc_library_shared { "include-private", ], - clang: true, - cflags: [ "-Wall", "-Werror", diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 4a1784ea0b..b2e069c4da 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "AHardwareBuffer" +#include <android/hardware_buffer.h> +#include <android/hardware_buffer_aidl.h> #include <vndk/hardware_buffer.h> #include <errno.h> @@ -32,6 +34,9 @@ #include <android/hardware/graphics/common/1.1/types.h> #include <aidl/android/hardware/graphics/common/PixelFormat.h> +// TODO: Better way to handle this +#include "../binder/ndk/parcel_internal.h" + static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints using namespace android; @@ -412,6 +417,25 @@ int AHardwareBuffer_getId(const AHardwareBuffer* buffer, uint64_t* outId) { return OK; } +binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) { + if (!parcel || !outBuffer) return STATUS_BAD_VALUE; + auto buffer = sp<GraphicBuffer>::make(); + status_t status = parcel->get()->read(*buffer); + if (status != STATUS_OK) return status; + *outBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get()); + AHardwareBuffer_acquire(*outBuffer); + return STATUS_OK; +} + +binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer, + AParcel* _Nonnull parcel) { + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return STATUS_BAD_VALUE; + if (!parcel) return STATUS_BAD_VALUE; + return parcel->get()->write(*gb); +} + // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- @@ -593,15 +617,27 @@ bool AHardwareBuffer_isValidPixelFormat(uint32_t format) { static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) == AHARDWAREBUFFER_FORMAT_R8_UNORM, "HAL and AHardwareBuffer pixel format don't match"); + static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) == + AHARDWAREBUFFER_FORMAT_R16_UINT, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) == + AHARDWAREBUFFER_FORMAT_R16G16_UINT, + "HAL and AHardwareBuffer pixel format don't match"); + static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) == + AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); switch (format) { case AHARDWAREBUFFER_FORMAT_R8_UNORM: + case AHARDWAREBUFFER_FORMAT_R16_UINT: + case AHARDWAREBUFFER_FORMAT_R16G16_UINT: case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM: case AHARDWAREBUFFER_FORMAT_BLOB: case AHARDWAREBUFFER_FORMAT_D16_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM: @@ -653,6 +689,7 @@ uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) { return 1; case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: case AHARDWAREBUFFER_FORMAT_D16_UNORM: + case AHARDWAREBUFFER_FORMAT_R16_UINT: return 2; case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM: @@ -662,8 +699,10 @@ uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) { case AHARDWAREBUFFER_FORMAT_D32_FLOAT: case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT: + case AHARDWAREBUFFER_FORMAT_R16G16_UINT: return 4; case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM: return 8; default: return 0; diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 18a4b2d3e8..3762e66e7f 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -20,10 +20,15 @@ // from nativewindow/includes/system/window.h // (not to be confused with the compatibility-only window.h from system/core/includes) #include <system/window.h> +#include <android/native_window_aidl.h> #include <private/android/AHardwareBufferHelpers.h> +#include <log/log.h> #include <ui/GraphicBuffer.h> +#include <gui/Surface.h> +#include <gui/view/Surface.h> +#include <android/binder_libbinder.h> using namespace android; @@ -59,6 +64,13 @@ static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) { return false; } } +static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) { + return Surface::getIGraphicBufferProducer(window); +} + +static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) { + return Surface::getSurfaceControlHandle(window); +} /************************************************************************************************** * NDK @@ -334,6 +346,42 @@ int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation return native_window_set_auto_prerotation(window, autoPrerotation); } +binder_status_t ANativeWindow_readFromParcel( + const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) { + const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + + // Use a android::view::Surface to unparcel the window + std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>(); + status_t ret = shimSurface->readFromParcel(nativeParcel); + if (ret != OK) { + ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__); + return STATUS_BAD_VALUE; + } + sp<Surface> surface = sp<Surface>::make( + shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle); + ANativeWindow* anw = surface.get(); + ANativeWindow_acquire(anw); + *outWindow = anw; + return STATUS_OK; +} + +binder_status_t ANativeWindow_writeToParcel( + ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) { + int value; + int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value); + if (err != OK || value != NATIVE_WINDOW_SURFACE) { + ALOGE("Error: ANativeWindow is not backed by Surface"); + return STATUS_BAD_VALUE; + } + // Use a android::view::Surface to parcelize the window + std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>(); + shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window); + shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window); + + Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + return shimSurface->writeToParcel(nativeParcel); +} + /************************************************************************************************** * apex-stable **************************************************************************************************/ diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index d30efa1851..bc0bfc52d5 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -61,6 +61,9 @@ ndk_library { // Android O first_version: "26", + export_header_libs: [ + "libnativewindow_ndk_headers", + ], } cc_library { @@ -71,14 +74,15 @@ cc_library { override_export_include_dirs: [ "include", ], + export_llndk_headers: [ + "libarect_headers", + ], }, export_include_dirs: [ "include", "include-private", ], - clang: true, - cflags: [ "-Wall", "-Werror", @@ -98,25 +102,27 @@ cc_library { "liblog", "libutils", "libui", + "libbinder", + "libbinder_ndk", "android.hardware.graphics.common@1.1", ], static_libs: [ "libarect", "libgrallocusage", + "libgui_aidl_static", ], header_libs: [ + "libgui_headers", + "libarect_headers", "libnativebase_headers", "libnativewindow_headers", ], // headers we include in our public headers - export_static_lib_headers: [ - "libarect", - ], - export_header_lib_headers: [ + "libarect_headers", "libnativebase_headers", ], diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index c35507b6f1..85a524988d 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -173,6 +173,27 @@ enum AHardwareBuffer_Format { * OpenGL ES: GR_GL_R8 */ AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R16_UINT + * OpenGL ES: GR_GL_R16UI + */ + AHARDWAREBUFFER_FORMAT_R16_UINT = 0x39, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R16G16_UINT + * OpenGL ES: GR_GL_RG16UI + */ + AHARDWAREBUFFER_FORMAT_R16G16_UINT = 0x3a, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 + * OpenGL ES: N/A + */ + AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM = 0x3b, }; /** @@ -290,6 +311,16 @@ enum AHardwareBuffer_UsageFlags { */ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26, + /** + * Usage: The buffer is used for front-buffer rendering. When + * front-buffering rendering is specified, different usages may adjust their + * behavior as a result. For example, when used as GPU_COLOR_OUTPUT the buffer + * will behave similar to a single-buffered window. When used with + * COMPOSER_OVERLAY, the system will try to prioritize the buffer receiving + * an overlay plane & avoid caching it in intermediate composition buffers. + */ + AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL << 32, + AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28, AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29, AHARDWAREBUFFER_USAGE_VENDOR_2 = 1ULL << 30, diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h new file mode 100644 index 0000000000..9fea21e34e --- /dev/null +++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h @@ -0,0 +1,151 @@ +/* + * 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. + */ + +/** + * @file hardware_buffer_aidl.h + * @brief HardwareBuffer NDK AIDL glue code + */ + +/** + * @addtogroup AHardwareBuffer + * + * Parcelable support for AHardwareBuffer. Can be used with libbinder_ndk + * + * @{ + */ + +#ifndef ANDROID_HARDWARE_BUFFER_AIDL_H +#define ANDROID_HARDWARE_BUFFER_AIDL_H + +#include <android/binder_parcel.h> +#include <android/hardware_buffer.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Read an AHardwareBuffer from a AParcel. The output buffer will have an + * initial reference acquired and will need to be released with + * AHardwareBuffer_release. + * + * Available since API level 34. + * + * \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 AHardwareBuffer type + * STATUS_NO_MEMORY if an allocation fails + */ +binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(34); + +/** + * Write an AHardwareBuffer to an AParcel. + * + * Available since API level 34. + * + * \return STATUS_OK on success. + * STATUS_BAD_VALUE if either buffer or parcel is null, or if the AHardwareBuffer* + * fails to serialize (eg, internally corrupted) + * STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is + * unable to allocate more + * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs + */ +binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer, + AParcel* _Nonnull parcel) __INTRODUCED_IN(34); + +__END_DECLS + +// Only enable the AIDL glue helper if this is C++ +#ifdef __cplusplus + +namespace aidl::android::hardware { + +/** + * Wrapper class that enables interop with AIDL NDK generation + * Takes ownership of the AHardwareBuffer* given to it in reset() and will automatically + * destroy it in the destructor, similar to a smart pointer container + */ +class HardwareBuffer { +public: + HardwareBuffer() noexcept {} + HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {} + + ~HardwareBuffer() { + reset(); + } + + binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { + reset(); + return AHardwareBuffer_readFromParcel(parcel, &mBuffer); + } + + binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { + if (!mBuffer) { + return STATUS_BAD_VALUE; + } + return AHardwareBuffer_writeToParcel(mBuffer, parcel); + } + + /** + * Destroys any currently owned AHardwareBuffer* and takes ownership of the given + * AHardwareBuffer* + * + * @param buffer The buffer to take ownership of + */ + void reset(AHardwareBuffer* _Nullable buffer = nullptr) noexcept { + if (mBuffer) { + AHardwareBuffer_release(mBuffer); + mBuffer = nullptr; + } + mBuffer = buffer; + } + + inline AHardwareBuffer* _Nullable operator-> () const { return mBuffer; } + inline AHardwareBuffer* _Nullable get() const { return mBuffer; } + inline explicit operator bool () const { return mBuffer != nullptr; } + + HardwareBuffer& operator=(HardwareBuffer&& other) noexcept { + reset(other.release()); + return *this; + } + + /** + * Stops managing any contained AHardwareBuffer*, returning it to the caller. Ownership + * is released. + * @return AHardwareBuffer* or null if this was empty + */ + [[nodiscard]] AHardwareBuffer* _Nullable release() noexcept { + AHardwareBuffer* _Nullable ret = mBuffer; + mBuffer = nullptr; + return ret; + } + +private: + HardwareBuffer(const HardwareBuffer& other) = delete; + HardwareBuffer& operator=(const HardwareBuffer& other) = delete; + + AHardwareBuffer* _Nullable mBuffer = nullptr; +}; + +} // aidl::android::hardware + +#endif // __cplusplus + +#endif // ANDROID_HARDWARE_BUFFER_AIDL_H + +/** @} */ diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index f0e1c4d749..a593cd47d3 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -333,7 +333,7 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa __INTRODUCED_IN(31); #ifdef __cplusplus -}; +} #endif #endif // ANDROID_NATIVE_WINDOW_H diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h new file mode 100644 index 0000000000..a252245a10 --- /dev/null +++ b/libs/nativewindow/include/android/native_window_aidl.h @@ -0,0 +1,161 @@ +/* + * 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. + */ + +/** + * @file native_window_aidl.h + * @brief NativeWindow NDK AIDL glue code + */ + +/** + * @addtogroup ANativeWindow + * + * Parcelable support for ANativeWindow. Can be used with libbinder_ndk + * + * @{ + */ + +#ifndef ANDROID_NATIVE_WINDOW_AIDL_H +#define ANDROID_NATIVE_WINDOW_AIDL_H + +#include <android/binder_parcel.h> +#include <android/native_window.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Read an ANativeWindow from a AParcel. The output buffer will have an + * initial reference acquired and will need to be released with + * ANativeWindow_release. + * + * Available since API level 34. + * + * \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 ANativeWindow type + * STATUS_NO_MEMORY if an allocation fails + */ +binder_status_t ANativeWindow_readFromParcel(const AParcel* _Nonnull parcel, + ANativeWindow* _Nullable* _Nonnull outWindow) __INTRODUCED_IN(__ANDROID_API_U__); + +/** + * Write an ANativeWindow to an AParcel. + * + * Available since API level 34. + * + * \return STATUS_OK on success. + * STATUS_BAD_VALUE if either buffer or parcel is null, or if the ANativeWindow* + * fails to serialize (eg, internally corrupted) + * STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is + * unable to allocate more + * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs + */ +binder_status_t ANativeWindow_writeToParcel(ANativeWindow* _Nonnull window, + AParcel* _Nonnull parcel) __INTRODUCED_IN(__ANDROID_API_U__); + +__END_DECLS + +// Only enable the AIDL glue helper if this is C++ +#ifdef __cplusplus + +namespace aidl::android::hardware { + +/** + * Wrapper class that enables interop with AIDL NDK generation + * Takes ownership of the ANativeWindow* given to it in reset() and will automatically + * destroy it in the destructor, similar to a smart pointer container + */ +class NativeWindow { +public: + NativeWindow() noexcept {} + explicit NativeWindow(ANativeWindow* _Nullable window) { + reset(window); + } + + explicit NativeWindow(NativeWindow&& other) noexcept { + mWindow = other.release(); // steal ownership from r-value + } + + ~NativeWindow() { + reset(); + } + + binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { + reset(); + return ANativeWindow_readFromParcel(parcel, &mWindow); + } + + binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { + if (!mWindow) { + return STATUS_BAD_VALUE; + } + return ANativeWindow_writeToParcel(mWindow, parcel); + } + + /** + * Destroys any currently owned ANativeWindow* and takes ownership of the given + * ANativeWindow* + * + * @param buffer The buffer to take ownership of + */ + void reset(ANativeWindow* _Nullable window = nullptr) noexcept { + if (mWindow) { + ANativeWindow_release(mWindow); + mWindow = nullptr; + } + if (window != nullptr) { + ANativeWindow_acquire(window); + } + mWindow = window; + } + inline ANativeWindow* _Nullable operator-> () const { return mWindow; } + inline ANativeWindow* _Nullable get() const { return mWindow; } + inline explicit operator bool () const { return mWindow != nullptr; } + + NativeWindow& operator=(NativeWindow&& other) noexcept { + mWindow = other.release(); // steal ownership from r-value + return *this; + } + + /** + * Stops managing any contained ANativeWindow*, returning it to the caller. Ownership + * is released. + * @return ANativeWindow* or null if this was empty + */ + [[nodiscard]] ANativeWindow* _Nullable release() noexcept { + ANativeWindow* _Nullable ret = mWindow; + mWindow = nullptr; + return ret; + } +private: + ANativeWindow* _Nullable mWindow = nullptr; + NativeWindow(const NativeWindow &other) = delete; + NativeWindow& operator=(const NativeWindow &other) = delete; +}; + +} // aidl::android::hardware + // +namespace aidl::android::view { + using Surface = aidl::android::hardware::NativeWindow; +} + +#endif // __cplusplus + +#endif // ANDROID_NATIVE_WINDOW_AIDL_H + +/** @} */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 988132cd1c..d9ac568187 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,10 +2,10 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_createFromHandle; # llndk # apex + AHardwareBuffer_createFromHandle; # llndk # systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 - AHardwareBuffer_getNativeHandle; # llndk # apex + AHardwareBuffer_getNativeHandle; # llndk # systemapi AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 @@ -14,6 +14,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_release; AHardwareBuffer_sendHandleToUnixSocket; AHardwareBuffer_unlock; + AHardwareBuffer_readFromParcel; # introduced=34 + AHardwareBuffer_writeToParcel; # introduced=34 ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk @@ -23,18 +25,18 @@ LIBNATIVEWINDOW { ANativeWindow_getBuffersDataSpace; # introduced=28 ANativeWindow_getFormat; ANativeWindow_getHeight; - ANativeWindow_getLastDequeueDuration; # apex # introduced=30 - ANativeWindow_getLastDequeueStartTime; # apex # introduced=30 - ANativeWindow_getLastQueueDuration; # apex # introduced=30 + ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30 + ANativeWindow_getLastDequeueStartTime; # systemapi # introduced=30 + ANativeWindow_getLastQueueDuration; # systemapi # introduced=30 ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # llndk ANativeWindow_queryf; # llndk ANativeWindow_queueBuffer; # llndk - ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30 - ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30 - ANativeWindow_setPerformInterceptor; # apex # introduced=30 - ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30 + ANativeWindow_setCancelBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setDequeueBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setPerformInterceptor; # systemapi # introduced=30 + ANativeWindow_setQueueBufferInterceptor; # systemapi # introduced=30 ANativeWindow_release; ANativeWindow_setAutoPrerotation; # llndk ANativeWindow_setAutoRefresh; # llndk @@ -45,7 +47,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; - ANativeWindow_setDequeueTimeout; # apex # introduced=30 + ANativeWindow_setDequeueTimeout; # systemapi # introduced=30 ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk @@ -53,6 +55,8 @@ LIBNATIVEWINDOW { ANativeWindow_setUsage; # llndk ANativeWindow_tryAllocateBuffers; # introduced=30 ANativeWindow_unlockAndPost; + ANativeWindow_readFromParcel; # introduced=UpsideDownCake + ANativeWindow_writeToParcel; # introduced=UpsideDownCake local: *; }; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index cb92df388b..f6f57dde7d 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -111,7 +111,6 @@ cc_library_static { name: "librenderengine", defaults: ["librenderengine_defaults"], double_loadable: true, - clang: true, cflags: [ "-fvisibility=hidden", "-Werror=format", diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index edd453a936..b6b9cc4099 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,10 +21,10 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_shared { +cc_library { name: "libsensor", - clang: true, + host_supported: true, cflags: [ "-Wall", "-Werror", @@ -53,5 +53,9 @@ cc_library_shared { export_include_dirs: ["include"], - export_shared_lib_headers: ["libbinder", "libpermission", "libhardware"], + export_shared_lib_headers: [ + "libbinder", + "libpermission", + "libhardware", + ], } diff --git a/libs/sensor/fuzz/bittube_fuzzer/Android.bp b/libs/sensor/fuzz/bittube_fuzzer/Android.bp new file mode 100644 index 0000000000..7af22cc7c1 --- /dev/null +++ b/libs/sensor/fuzz/bittube_fuzzer/Android.bp @@ -0,0 +1,51 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +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_fuzz { + name: "bittube_fuzzer", + srcs: [ + "bittube_fuzzer.cpp", + ], + static_libs: [ + ], + shared_libs: [ + "libsensor", + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + "libpermission", + ], + export_shared_lib_headers: [ + "libbinder", + "libpermission", + "libhardware", + ], + header_libs: [ + ], +} diff --git a/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp new file mode 100644 index 0000000000..6a61d3615f --- /dev/null +++ b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp @@ -0,0 +1,37 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +#include <fuzzer/FuzzedDataProvider.h> + +#include <sensor/BitTube.h> +#include <binder/Parcel.h> +using namespace android; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + sp<BitTube> bittube(new BitTube(size)); + Parcel parcel[5]; + bittube->writeToParcel(parcel); + sp<BitTube> tube(new BitTube(size)); + bittube->sendObjects<uint8_t>(tube, data, size); + uint8_t recvData[size]; + for (int i = 0; i < size; i++) recvData[i] = fdp.ConsumeIntegral<uint8_t>(); + bittube->recvObjects<uint8_t>(tube, recvData, size); + + return 0; +} diff --git a/libs/sensor/fuzz/sensor_fuzzer/Android.bp b/libs/sensor/fuzz/sensor_fuzzer/Android.bp new file mode 100644 index 0000000000..685c54953c --- /dev/null +++ b/libs/sensor/fuzz/sensor_fuzzer/Android.bp @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * 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. + * + ***************************************************************************** + */ +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_fuzz { + name: "sensor_fuzzer", + srcs: [ + "sensor_fuzzer.cpp", + ], + shared_libs: [ + "libsensor", + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + "libpermission", + ], + export_shared_lib_headers: [ + "libbinder", + "libpermission", + "libhardware", + ], +} diff --git a/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp new file mode 100644 index 0000000000..0e110b7e6f --- /dev/null +++ b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +#include <fuzzer/FuzzedDataProvider.h> + +#include <sensor/Sensor.h> +using namespace android; + +const int MAX_STR_LEN = 32; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + struct sensor_t sensor_type; + std::string name = fdp.ConsumeBytesAsString(MAX_STR_LEN); + sensor_type.name = name.c_str(); + std::string vendor = fdp.ConsumeBytesAsString(MAX_STR_LEN); + sensor_type.vendor = vendor.c_str(); + sensor_type.stringType = ""; + sensor_type.requiredPermission = ""; + sensor_type.version = fdp.ConsumeIntegral<int>(); + sensor_type.handle = fdp.ConsumeIntegral<int>(); + sensor_type.type = fdp.ConsumeIntegral<int>(); + sensor_type.maxRange = fdp.ConsumeFloatingPoint<float>(); + sensor_type.resolution = fdp.ConsumeFloatingPoint<float>(); + sensor_type.power = fdp.ConsumeFloatingPoint<float>(); + sensor_type.minDelay = fdp.ConsumeIntegral<int32_t>(); + sensor_type.fifoReservedEventCount = fdp.ConsumeIntegral<uint32_t>(); + sensor_type.fifoMaxEventCount = fdp.ConsumeIntegral<uint32_t>(); + int halVersion = fdp.ConsumeIntegral<int>(); + Sensor sensor1(&sensor_type, halVersion); + uint8_t buffer[size]; + for (int i = 0; i < size; i++) buffer[i] = data[i]; + sensor1.flatten(buffer, size); + std::vector<uint8_t> buffer1(sensor1.getFlattenedSize()); + auto ab = sensor1.unflatten(buffer1.data(), buffer1.size()); + return 0; +} + diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index 8fdb003a5d..ac4be44a4a 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp @@ -24,9 +24,10 @@ package { cc_test { name: "libsensor_test", - clang: true, - - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], srcs: [ "Sensor_test.cpp", diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp index 6b936de0ec..847747948a 100644 --- a/libs/shaders/Android.bp +++ b/libs/shaders/Android.bp @@ -28,7 +28,7 @@ cc_library_static { local_include_dirs: ["include"], shared_libs: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.composer3-V1-ndk", "android.hardware.graphics.common@1.2", "libnativewindow", diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp index cf671bcb7a..718d37bc38 100644 --- a/libs/shaders/tests/Android.bp +++ b/libs/shaders/tests/Android.bp @@ -31,7 +31,7 @@ cc_test { "libtonemap_headers", ], shared_libs: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.composer3-V1-ndk", "android.hardware.graphics.common@1.2", "libnativewindow", diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index 37c98240c5..eca051d215 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -28,7 +28,7 @@ cc_library_static { local_include_dirs: ["include"], shared_libs: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.composer3-V1-ndk", "liblog", "libnativewindow", diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp index 58851b41be..0002d3a935 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -31,7 +31,7 @@ cc_test { "libtonemap_headers", ], shared_libs: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.composer3-V1-ndk", "libnativewindow", ], diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index d138495036..98d9b94322 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -36,7 +36,6 @@ cc_library_headers { cc_defaults { name: "libui-defaults", - clang: true, cflags: [ "-Wall", "-Werror", @@ -111,7 +110,6 @@ cc_library_shared { }, double_loadable: true, - clang: true, cflags: [ "-Wall", "-Werror", @@ -157,6 +155,8 @@ cc_library_shared { ], defaults: [ + "android.hardware.graphics.allocator-ndk_shared", + "android.hardware.graphics.common-ndk_shared", "libui-defaults", // Uncomment the following line to enable VALIDATE_REGIONS traces //defaults: ["libui-validate-regions-defaults"], @@ -166,8 +166,6 @@ cc_library_shared { "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "android.hardware.graphics.allocator@4.0", - "android.hardware.graphics.allocator-V1-ndk", - "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", @@ -185,7 +183,6 @@ cc_library_shared { export_shared_lib_headers: [ "android.hardware.graphics.common@1.2", - "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.mapper@4.0", "libgralloctypes", ], diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index f6ab7b2a5e..53372c9866 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -22,6 +22,8 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_enums.h> #include <android/binder_manager.h> +#include <cutils/android_filesystem_config.h> +#include <cutils/multiuser.h> #include <gralloctypes/Gralloc4.h> #include <hidl/ServiceManagement.h> #include <hwbinder/IPCThreadState.h> @@ -1195,8 +1197,15 @@ Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(map mAllocator = IAllocator::getService(); if (__builtin_available(android 31, *)) { if (hasIAllocatorAidl()) { - mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder( - AServiceManager_waitForService(kAidlAllocatorServiceName.c_str()))); + // TODO(b/269517338): Perform the isolated checking for this in service manager instead. + uid_t aid = multiuser_get_app_id(getuid()); + if (aid >= AID_ISOLATED_START && aid <= AID_ISOLATED_END) { + mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder( + AServiceManager_getService(kAidlAllocatorServiceName.c_str()))); + } else { + mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder( + AServiceManager_waitForService(kAidlAllocatorServiceName.c_str()))); + } ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service"); } } diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 3732fee7f2..429760ffe0 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -465,7 +465,7 @@ status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& if (flattenWordCount == 13) { usage = (uint64_t(buf[12]) << 32) | uint32_t(buf[6]); } else { - usage = uint64_t(usage_deprecated); + usage = uint64_t(ANDROID_NATIVE_UNSIGNED_CAST(usage_deprecated)); } native_handle* h = native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts)); diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index a98e697eb1..f5824233e9 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -109,6 +109,11 @@ status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, return NO_ERROR; } +status_t GraphicBufferMapper::importBufferNoValidate(const native_handle_t* rawHandle, + buffer_handle_t* outHandle) { + return mMapper->importBuffer(rawHandle, outHandle); +} + void GraphicBufferMapper::getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts) { diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 507fa355a4..37a2e1cc9f 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -59,6 +59,8 @@ public: PixelFormat format, uint64_t usage, uint32_t stride, buffer_handle_t* outHandle); + status_t importBufferNoValidate(const native_handle_t* rawHandle, buffer_handle_t* outHandle); + status_t freeBuffer(buffer_handle_t handle); void getTransportSize(buffer_handle_t handle, diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h index f422ce439e..cf5c2e8c12 100644 --- a/libs/ui/include/ui/PixelFormat.h +++ b/libs/ui/include/ui/PixelFormat.h @@ -53,16 +53,19 @@ enum { // real pixel formats supported for rendering ----------------------------- - PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA - PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 - PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB - PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB - PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA - PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB - PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB - PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA - PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA - PIXEL_FORMAT_R_8 = 0x38, + PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA + PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 + PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB + PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB + PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA + PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB + PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB + PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA + PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA + PIXEL_FORMAT_R_8 = 0x38, + PIXEL_FORMAT_R_16_UINT = 0x39, + PIXEL_FORMAT_RG_1616_UINT = 0x3a, + PIXEL_FORMAT_RGBA_10101010 = 0x3b, }; typedef int32_t PixelFormat; diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp index 0a4873c8d3..3fb33b4a12 100644 --- a/libs/ui/tests/colorspace_test.cpp +++ b/libs/ui/tests/colorspace_test.cpp @@ -111,6 +111,7 @@ TEST_F(ColorSpaceTest, TransferFunctions) { EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f); EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f); + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float v = 0.0f; v <= 0.5f; v += 1e-3f) { ASSERT_TRUE(v >= sRGB.getEOTF()(v)); ASSERT_TRUE(v <= sRGB.getOETF()(v)); @@ -118,6 +119,7 @@ TEST_F(ColorSpaceTest, TransferFunctions) { float previousEOTF = std::numeric_limits<float>::lowest(); float previousOETF = std::numeric_limits<float>::lowest(); + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v)); previousEOTF = sRGB.getEOTF()(v); @@ -131,6 +133,7 @@ TEST_F(ColorSpaceTest, TransferFunctions) { {0.3127f, 0.3290f} // linear transfer functions ); + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { ASSERT_EQ(v, sRGB2.getEOTF()(v)); ASSERT_EQ(v, sRGB2.getOETF()(v)); diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp index c28c303c0c..5d6070cf95 100644 --- a/libs/ui/tools/Android.bp +++ b/libs/ui/tools/Android.bp @@ -25,7 +25,7 @@ package { cc_defaults { name: "libui_tools_default", - clang_cflags: [ + cflags: [ "-Wall", "-Wextra", "-Werror", diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp index 83c250a24f..2af51a722c 100644 --- a/libs/vibrator/Android.bp +++ b/libs/vibrator/Android.bp @@ -21,10 +21,30 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +cc_defaults { + name: "libvibrator_defaults", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wno-unused-parameter", + ], + + export_include_dirs: ["include"], + + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, +} + cc_library { name: "libvibrator", - vendor_available: true, - double_loadable: true, + defaults: ["libvibrator_defaults"], shared_libs: [ "libbinder", @@ -32,6 +52,10 @@ cc_library { "libutils", ], + whole_static_libs: [ + "libvibratorutils", + ], + header_libs: [ "libaudio_system_headers", ], @@ -44,23 +68,27 @@ cc_library { srcs: [ ":libvibrator_aidl", - "*.cpp", + "ExternalVibration.cpp", ], +} - cflags: [ - "-Wall", - "-Werror", - "-Wno-missing-field-initializers", - "-Wno-unused-variable", - "-Wno-unused-parameter", +cc_library { + name: "libvibratorutils", + defaults: ["libvibrator_defaults"], + + vendor_available: true, + double_loadable: true, + + shared_libs: [ + "libutils", ], - export_include_dirs: ["include"], + srcs: [ + "ExternalVibrationUtils.cpp", + ], - host_supported: true, - target: { - darwin: { - enabled: false, - }, - }, + visibility: [ + "//frameworks/native/libs/vibrator", + "//frameworks/av/media/libeffects/hapticgenerator", + ], } diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index f6fc19e05d..ec906458a3 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -15,11 +15,22 @@ */ #include <vibrator/ExternalVibration.h> +#include <vibrator/ExternalVibrationUtils.h> +#include <android/os/IExternalVibratorService.h> #include <binder/Parcel.h> #include <log/log.h> #include <utils/Errors.h> + +// To guarantee if HapticScale enum has the same value as IExternalVibratorService +static_assert(static_cast<int>(android::os::HapticScale::MUTE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_MUTE)); +static_assert(static_cast<int>(android::os::HapticScale::VERY_LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_LOW)); +static_assert(static_cast<int>(android::os::HapticScale::LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_LOW)); +static_assert(static_cast<int>(android::os::HapticScale::NONE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_NONE)); +static_assert(static_cast<int>(android::os::HapticScale::HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_HIGH)); +static_assert(static_cast<int>(android::os::HapticScale::VERY_HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_HIGH)); + void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) { out->writeInt32(attrs.usage); out->writeInt32(attrs.content_type); diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h index c6eb3d102b..760dbce149 100644 --- a/libs/vibrator/include/vibrator/ExternalVibration.h +++ b/libs/vibrator/include/vibrator/ExternalVibration.h @@ -33,7 +33,6 @@ public : ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs, sp<IExternalVibrationController> controller); virtual ~ExternalVibration() = default; - ExternalVibration(const ExternalVibration&) = default; bool operator==(const ExternalVibration&) const; diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index 84357fcb35..c588bfdedd 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -17,17 +17,17 @@ #ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H #define ANDROID_EXTERNAL_VIBRATION_UTILS_H -#include <android/os/IExternalVibratorService.h> - namespace android::os { +// Copied from frameworks/base/core/java/android/os/IExternalVibratorService.aidl +// The values are checked in ExternalVibration.cpp enum class HapticScale { - MUTE = IExternalVibratorService::SCALE_MUTE, - VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW, - LOW = IExternalVibratorService::SCALE_LOW, - NONE = IExternalVibratorService::SCALE_NONE, - HIGH = IExternalVibratorService::SCALE_HIGH, - VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH, + MUTE = -100, + VERY_LOW = -2, + LOW = -1, + NONE = 0, + HIGH = 1, + VERY_HIGH = 2, }; bool isValidHapticScale(HapticScale scale); diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp index fa449aeb55..e72ca74ba7 100644 --- a/libs/vr/libbroadcastring/Android.bp +++ b/libs/vr/libbroadcastring/Android.bp @@ -26,7 +26,6 @@ cc_library_static { cc_test { name: "broadcast_ring_tests", - clang: true, cflags: [ "-Wall", "-Wextra", diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp deleted file mode 100644 index 0bda7987a0..0000000000 --- a/libs/vr/libbufferhubqueue/Android.bp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2016 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"], -} - -sourceFiles = [ - "buffer_hub_queue_client.cpp", - "buffer_hub_queue_parcelable.cpp", -] - -includeFiles = [ - "include", -] - -staticLibraries = [ - "libbufferhub", -] - -sharedLibraries = [ - "libbinder", - "libcutils", - "liblog", - "libui", - "libutils", - "libpdx_default_transport", -] - -headerLibraries = [ - "libdvr_headers", - "libnativebase_headers", -] - -cc_library_shared { - name: "libbufferhubqueue", - cflags: [ - "-DLOG_TAG=\"libbufferhubqueue\"", - "-DTRACE=0", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-Wall", - "-Werror", - "-Wno-format", - "-Wno-unused-parameter", - "-Wno-unused-variable", - ], - srcs: sourceFiles, - export_include_dirs: includeFiles, - export_static_lib_headers: staticLibraries, - static_libs: staticLibraries, - shared_libs: sharedLibraries, - header_libs: headerLibraries, -} - -subdirs = ["benchmarks", "tests"] diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp deleted file mode 100644 index e33e03b37b..0000000000 --- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ - -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_benchmark { - srcs: ["buffer_transport_benchmark.cpp"], - shared_libs: [ - "libbase", - "libbinder", - "libcutils", - "libdvr.google", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libbufferhubqueue", - "libpdx_default_transport", - ], - cflags: [ - "-DLOG_TAG=\"buffer_transport_benchmark\"", - "-DTRACE=0", - "-O2", - "-Wall", - "-Werror", - ], - name: "buffer_transport_benchmark", -} diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp deleted file mode 100644 index b6813eb51d..0000000000 --- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp +++ /dev/null @@ -1,589 +0,0 @@ -#include <android-base/logging.h> -#include <android/native_window.h> -#include <benchmark/benchmark.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <dvr/dvr_api.h> -#include <gui/BufferItem.h> -#include <gui/BufferItemConsumer.h> -#include <gui/Surface.h> -#include <private/dvr/epoll_file_descriptor.h> -#include <utils/Trace.h> - -#include <chrono> -#include <functional> -#include <iostream> -#include <thread> -#include <vector> - -#include <dlfcn.h> -#include <poll.h> -#include <sys/epoll.h> -#include <sys/wait.h> - -// Use ALWAYS at the tag level. Control is performed manually during command -// line processing. -#ifdef ATRACE_TAG -#undef ATRACE_TAG -#endif -#define ATRACE_TAG ATRACE_TAG_ALWAYS - -using namespace android; -using ::benchmark::State; - -static const String16 kBinderService = String16("bufferTransport"); -static const uint32_t kBufferWidth = 100; -static const uint32_t kBufferHeight = 1; -static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; -static const uint64_t kBufferUsage = - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; -static const uint32_t kBufferLayer = 1; -static const int kMaxAcquiredImages = 1; -static const int kQueueDepth = 2; // We are double buffering for this test. -static const size_t kMaxQueueCounts = 128; -static const int kInvalidFence = -1; - -enum BufferTransportServiceCode { - CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION, -}; - -// A binder services that minics a compositor that consumes buffers. It provides -// one Binder interface to create a new Surface for buffer producer to write -// into; while itself will carry out no-op buffer consuming by acquiring then -// releasing the buffer immediately. -class BufferTransportService : public BBinder { - public: - BufferTransportService() = default; - ~BufferTransportService() = default; - - virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) { - (void)flags; - (void)data; - switch (code) { - case CREATE_BUFFER_QUEUE: { - auto new_queue = std::make_shared<BufferQueueHolder>(this); - reply->writeStrongBinder( - IGraphicBufferProducer::asBinder(new_queue->producer)); - buffer_queues_.push_back(new_queue); - return OK; - } - default: - return UNKNOWN_TRANSACTION; - }; - } - - private: - struct FrameListener : public ConsumerBase::FrameAvailableListener { - public: - FrameListener(BufferTransportService* /*service*/, - sp<BufferItemConsumer> buffer_item_consumer) - : buffer_item_consumer_(buffer_item_consumer) {} - - void onFrameAvailable(const BufferItem& /*item*/) override { - BufferItem buffer; - status_t ret = 0; - { - ATRACE_NAME("AcquireBuffer"); - ret = buffer_item_consumer_->acquireBuffer(&buffer, /*presentWhen=*/0, - /*waitForFence=*/false); - } - - if (ret != OK) { - LOG(ERROR) << "Failed to acquire next buffer."; - return; - } - - { - ATRACE_NAME("ReleaseBuffer"); - ret = buffer_item_consumer_->releaseBuffer(buffer); - } - - if (ret != OK) { - LOG(ERROR) << "Failed to release buffer."; - return; - } - } - - private: - sp<BufferItemConsumer> buffer_item_consumer_; - }; - - struct BufferQueueHolder { - explicit BufferQueueHolder(BufferTransportService* service) { - BufferQueue::createBufferQueue(&producer, &consumer); - - sp<BufferItemConsumer> buffer_item_consumer = - new BufferItemConsumer(consumer, kBufferUsage, kMaxAcquiredImages, - /*controlledByApp=*/true); - buffer_item_consumer->setName(String8("BinderBufferTransport")); - frame_listener_ = new FrameListener(service, buffer_item_consumer); - buffer_item_consumer->setFrameAvailableListener(frame_listener_); - } - - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - - private: - sp<FrameListener> frame_listener_; - }; - - std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_; -}; - -// A virtual interfaces that abstracts the common BufferQueue operations, so -// that the test suite can use the same test case to drive different types of -// transport backends. -class BufferTransport { - public: - virtual ~BufferTransport() {} - - virtual int Start() = 0; - virtual sp<Surface> CreateSurface() = 0; -}; - -// Binder-based buffer transport backend. -// -// On Start() a new process will be swapned to run a Binder server that -// actually consumes the buffer. -// On CreateSurface() a new Binder BufferQueue will be created, which the -// service holds the concrete binder node of the IGraphicBufferProducer while -// sending the binder proxy to the client. In another word, the producer side -// operations are carried out process while the consumer side operations are -// carried out within the BufferTransportService's own process. -class BinderBufferTransport : public BufferTransport { - public: - BinderBufferTransport() {} - - int Start() override { - sp<IServiceManager> sm = defaultServiceManager(); - service_ = sm->getService(kBinderService); - if (service_ == nullptr) { - LOG(ERROR) << "Failed to get the benchmark service."; - return -EIO; - } - - LOG(INFO) << "Binder server is ready for client."; - return 0; - } - - sp<Surface> CreateSurface() override { - Parcel data; - Parcel reply; - int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply); - if (error != OK) { - LOG(ERROR) << "Failed to get buffer queue over binder."; - return nullptr; - } - - sp<IBinder> binder; - error = reply.readNullableStrongBinder(&binder); - if (error != OK) { - LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder."; - return nullptr; - } - - auto producer = interface_cast<IGraphicBufferProducer>(binder); - if (producer == nullptr) { - LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder."; - return nullptr; - } - - sp<Surface> surface = new Surface(producer, /*controlledByApp=*/true); - - // Set buffer dimension. - ANativeWindow* window = static_cast<ANativeWindow*>(surface.get()); - ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight, - kBufferFormat); - - return surface; - } - - private: - sp<IBinder> service_; -}; - -class DvrApi { - public: - DvrApi() { - handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL); - CHECK(handle_); - - auto dvr_get_api = - reinterpret_cast<decltype(&dvrGetApi)>(dlsym(handle_, "dvrGetApi")); - int ret = dvr_get_api(&api_, sizeof(api_), /*version=*/1); - - CHECK(ret == 0); - } - - ~DvrApi() { dlclose(handle_); } - - const DvrApi_v1& Api() { return api_; } - - private: - void* handle_ = nullptr; - DvrApi_v1 api_; -}; - -// BufferHub/PDX-based buffer transport. -// -// On Start() a new thread will be swapned to run an epoll polling thread which -// minics the behavior of a compositor. Similar to Binder-based backend, the -// buffer available handler is also a no-op: Buffer gets acquired and released -// immediately. -// On CreateSurface() a pair of dvr::ProducerQueue and dvr::ConsumerQueue will -// be created. The epoll thread holds on the consumer queue and dequeues buffer -// from it; while the producer queue will be wrapped in a Surface and returned -// to test suite. -class BufferHubTransport : public BufferTransport { - public: - virtual ~BufferHubTransport() { - stopped_.store(true); - if (reader_thread_.joinable()) { - reader_thread_.join(); - } - } - - int Start() override { - int ret = epoll_fd_.Create(); - if (ret < 0) { - LOG(ERROR) << "Failed to create epoll fd: %s", strerror(-ret); - return -1; - } - - // Create the reader thread. - reader_thread_ = std::thread([this]() { - int ret = dvr_.Api().PerformanceSetSchedulerPolicy(0, "graphics"); - if (ret < 0) { - LOG(ERROR) << "Failed to set scheduler policy, ret=" << ret; - return; - } - - stopped_.store(false); - LOG(INFO) << "Reader Thread Running..."; - - while (!stopped_.load()) { - std::array<epoll_event, kMaxQueueCounts> events; - - // Don't sleep forever so that we will have a chance to wake up. - const int ret = epoll_fd_.Wait(events.data(), events.size(), - /*timeout=*/100); - if (ret < 0) { - LOG(ERROR) << "Error polling consumer queues."; - continue; - } - if (ret == 0) { - continue; - } - - const int num_events = ret; - for (int i = 0; i < num_events; i++) { - uint32_t index = events[i].data.u32; - dvr_.Api().ReadBufferQueueHandleEvents( - buffer_queues_[index]->GetReadQueue()); - } - } - - LOG(INFO) << "Reader Thread Exiting..."; - }); - - return 0; - } - - sp<Surface> CreateSurface() override { - auto new_queue = std::make_shared<BufferQueueHolder>(); - if (!new_queue->IsReady()) { - LOG(ERROR) << "Failed to create BufferHub-based BufferQueue."; - return nullptr; - } - - // Set buffer dimension. - ANativeWindow_setBuffersGeometry(new_queue->GetSurface(), kBufferWidth, - kBufferHeight, kBufferFormat); - - // Use the next position as buffer_queue index. - uint32_t index = buffer_queues_.size(); - epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u32 = index}}; - int queue_fd = - dvr_.Api().ReadBufferQueueGetEventFd(new_queue->GetReadQueue()); - const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, queue_fd, &event); - if (ret < 0) { - LOG(ERROR) << "Failed to track consumer queue: " << strerror(-ret) - << ", consumer queue fd: " << queue_fd; - return nullptr; - } - - buffer_queues_.push_back(new_queue); - ANativeWindow_acquire(new_queue->GetSurface()); - return static_cast<Surface*>(new_queue->GetSurface()); - } - - private: - struct BufferQueueHolder { - BufferQueueHolder() { - int ret = 0; - ret = dvr_.Api().WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kBufferLayer, - kBufferUsage, 0, sizeof(DvrNativeBufferMetadata), &write_queue_); - if (ret < 0) { - LOG(ERROR) << "Failed to create write buffer queue, ret=" << ret; - return; - } - - ret = dvr_.Api().WriteBufferQueueCreateReadQueue(write_queue_, - &read_queue_); - if (ret < 0) { - LOG(ERROR) << "Failed to create read buffer queue, ret=" << ret; - return; - } - - ret = dvr_.Api().ReadBufferQueueSetBufferAvailableCallback( - read_queue_, BufferAvailableCallback, this); - if (ret < 0) { - LOG(ERROR) << "Failed to create buffer available callback, ret=" << ret; - return; - } - - ret = - dvr_.Api().WriteBufferQueueGetANativeWindow(write_queue_, &surface_); - if (ret < 0) { - LOG(ERROR) << "Failed to create surface, ret=" << ret; - return; - } - } - - static void BufferAvailableCallback(void* context) { - BufferQueueHolder* thiz = static_cast<BufferQueueHolder*>(context); - thiz->HandleBufferAvailable(); - } - - DvrReadBufferQueue* GetReadQueue() { return read_queue_; } - - ANativeWindow* GetSurface() { return surface_; } - - bool IsReady() { - return write_queue_ != nullptr && read_queue_ != nullptr && - surface_ != nullptr; - } - - void HandleBufferAvailable() { - int ret = 0; - DvrNativeBufferMetadata meta; - DvrReadBuffer* buffer = nullptr; - DvrNativeBufferMetadata metadata; - int acquire_fence = kInvalidFence; - - { - ATRACE_NAME("AcquireBuffer"); - ret = dvr_.Api().ReadBufferQueueAcquireBuffer( - read_queue_, 0, &buffer, &metadata, &acquire_fence); - } - if (ret < 0) { - LOG(ERROR) << "Failed to acquire consumer buffer, error: " << ret; - return; - } - - if (buffer != nullptr) { - ATRACE_NAME("ReleaseBuffer"); - ret = dvr_.Api().ReadBufferQueueReleaseBuffer(read_queue_, buffer, - &meta, kInvalidFence); - } - if (ret < 0) { - LOG(ERROR) << "Failed to release consumer buffer, error: " << ret; - } - } - - private: - DvrWriteBufferQueue* write_queue_ = nullptr; - DvrReadBufferQueue* read_queue_ = nullptr; - ANativeWindow* surface_ = nullptr; - }; - - static DvrApi dvr_; - std::atomic<bool> stopped_; - std::thread reader_thread_; - - dvr::EpollFileDescriptor epoll_fd_; - std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_; -}; - -DvrApi BufferHubTransport::dvr_ = {}; - -enum TransportType { - kBinderBufferTransport, - kBufferHubTransport, -}; - -// Main test suite, which supports two transport backend: 1) BinderBufferQueue, -// 2) BufferHubQueue. The test case drives the producer end of both transport -// backend by queuing buffers into the buffer queue by using ANativeWindow API. -class BufferTransportBenchmark : public ::benchmark::Fixture { - public: - void SetUp(State& state) override { - if (state.thread_index == 0) { - const int transport = state.range(0); - switch (transport) { - case kBinderBufferTransport: - transport_.reset(new BinderBufferTransport); - break; - case kBufferHubTransport: - transport_.reset(new BufferHubTransport); - break; - default: - CHECK(false) << "Unknown test case."; - break; - } - - CHECK(transport_); - const int ret = transport_->Start(); - CHECK_EQ(ret, 0); - - LOG(INFO) << "Transport backend running, transport=" << transport << "."; - - // Create surfaces for each thread. - surfaces_.resize(state.threads); - for (int i = 0; i < state.threads; i++) { - // Common setup every thread needs. - surfaces_[i] = transport_->CreateSurface(); - CHECK(surfaces_[i]); - - LOG(INFO) << "Surface initialized on thread " << i << "."; - } - } - } - - void TearDown(State& state) override { - if (state.thread_index == 0) { - surfaces_.clear(); - transport_.reset(); - LOG(INFO) << "Tear down benchmark."; - } - } - - protected: - std::unique_ptr<BufferTransport> transport_; - std::vector<sp<Surface>> surfaces_; -}; - -BENCHMARK_DEFINE_F(BufferTransportBenchmark, Producers)(State& state) { - ANativeWindow* window = nullptr; - ANativeWindow_Buffer buffer; - int32_t error = 0; - double total_gain_buffer_us = 0; - double total_post_buffer_us = 0; - int iterations = 0; - - while (state.KeepRunning()) { - if (window == nullptr) { - CHECK(surfaces_[state.thread_index]); - window = static_cast<ANativeWindow*>(surfaces_[state.thread_index].get()); - - // Lock buffers a couple time from the queue, so that we have the buffer - // allocated. - for (int i = 0; i < kQueueDepth; i++) { - error = ANativeWindow_lock(window, &buffer, - /*inOutDirtyBounds=*/nullptr); - CHECK_EQ(error, 0); - error = ANativeWindow_unlockAndPost(window); - CHECK_EQ(error, 0); - } - } - - { - ATRACE_NAME("GainBuffer"); - auto t1 = std::chrono::high_resolution_clock::now(); - error = ANativeWindow_lock(window, &buffer, - /*inOutDirtyBounds=*/nullptr); - auto t2 = std::chrono::high_resolution_clock::now(); - std::chrono::duration<double, std::micro> delta_us = t2 - t1; - total_gain_buffer_us += delta_us.count(); - } - CHECK_EQ(error, 0); - - { - ATRACE_NAME("PostBuffer"); - auto t1 = std::chrono::high_resolution_clock::now(); - error = ANativeWindow_unlockAndPost(window); - auto t2 = std::chrono::high_resolution_clock::now(); - std::chrono::duration<double, std::micro> delta_us = t2 - t1; - total_post_buffer_us += delta_us.count(); - } - CHECK_EQ(error, 0); - - iterations++; - } - - state.counters["gain_buffer_us"] = ::benchmark::Counter( - total_gain_buffer_us / iterations, ::benchmark::Counter::kAvgThreads); - state.counters["post_buffer_us"] = ::benchmark::Counter( - total_post_buffer_us / iterations, ::benchmark::Counter::kAvgThreads); - state.counters["producer_us"] = ::benchmark::Counter( - (total_gain_buffer_us + total_post_buffer_us) / iterations, - ::benchmark::Counter::kAvgThreads); -} - -BENCHMARK_REGISTER_F(BufferTransportBenchmark, Producers) - ->Unit(::benchmark::kMicrosecond) - ->Ranges({{kBinderBufferTransport, kBufferHubTransport}}) - ->ThreadRange(1, 32); - -static void runBinderServer() { - ProcessState::self()->setThreadPoolMaxThreadCount(0); - ProcessState::self()->startThreadPool(); - - sp<IServiceManager> sm = defaultServiceManager(); - sp<BufferTransportService> service = new BufferTransportService; - sm->addService(kBinderService, service, false); - - LOG(INFO) << "Binder server running..."; - - while (true) { - int stat, retval; - retval = wait(&stat); - if (retval == -1 && errno == ECHILD) { - break; - } - } - - LOG(INFO) << "Service Exiting..."; -} - -// To run binder-based benchmark, use: -// adb shell buffer_transport_benchmark \ -// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/0/" -// -// To run bufferhub-based benchmark, use: -// adb shell buffer_transport_benchmark \ -// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/1/" -int main(int argc, char** argv) { - bool tracing_enabled = false; - - // Parse arguments in addition to "--benchmark_filter" paramters. - for (int i = 1; i < argc; i++) { - if (std::string(argv[i]) == "--help") { - std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl; - std::cout << "\t--trace: Enable systrace logging." << std::endl; - return 0; - } - if (std::string(argv[i]) == "--trace") { - tracing_enabled = true; - continue; - } - } - - // Setup ATRACE/systrace based on command line. - atrace_setup(); - atrace_set_tracing_enabled(tracing_enabled); - - pid_t pid = fork(); - if (pid == 0) { - // Child, i.e. the client side. - ProcessState::self()->startThreadPool(); - - ::benchmark::Initialize(&argc, argv); - ::benchmark::RunSpecifiedBenchmarks(); - } else { - LOG(INFO) << "Benchmark process pid: " << pid; - runBinderServer(); - } -} diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp deleted file mode 100644 index 2d3fa4aec0..0000000000 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ /dev/null @@ -1,823 +0,0 @@ -#include "include/private/dvr/buffer_hub_queue_client.h" - -#include <inttypes.h> -#include <log/log.h> -#include <poll.h> -#include <sys/epoll.h> - -#include <array> - -#include <pdx/default_transport/client_channel.h> -#include <pdx/default_transport/client_channel_factory.h> -#include <pdx/file_handle.h> -#include <pdx/trace.h> - -#define RETRY_EINTR(fnc_call) \ - ([&]() -> decltype(fnc_call) { \ - decltype(fnc_call) result; \ - do { \ - result = (fnc_call); \ - } while (result == -1 && errno == EINTR); \ - return result; \ - })() - -using android::pdx::ErrorStatus; -using android::pdx::LocalChannelHandle; -using android::pdx::LocalHandle; -using android::pdx::Status; - -namespace android { -namespace dvr { - -namespace { - -std::pair<int32_t, int32_t> Unstuff(uint64_t value) { - return {static_cast<int32_t>(value >> 32), - static_cast<int32_t>(value & ((1ull << 32) - 1))}; -} - -uint64_t Stuff(int32_t a, int32_t b) { - const uint32_t ua = static_cast<uint32_t>(a); - const uint32_t ub = static_cast<uint32_t>(b); - return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub); -} - -} // anonymous namespace - -BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle) - : Client{pdx::default_transport::ClientChannel::Create( - std::move(channel_handle))} { - Initialize(); -} - -BufferHubQueue::BufferHubQueue(const std::string& endpoint_path) - : Client{ - pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} { - Initialize(); -} - -void BufferHubQueue::Initialize() { - int ret = epoll_fd_.Create(); - if (ret < 0) { - ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s", - strerror(-ret)); - return; - } - - epoll_event event = { - .events = EPOLLIN | EPOLLET, - .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}}; - ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); - if (ret < 0) { - ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__, - strerror(-ret)); - } -} - -Status<void> BufferHubQueue::ImportQueue() { - auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>(); - if (!status) { - ALOGE("%s: Failed to import queue: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return ErrorStatus(status.error()); - } else { - SetupQueue(status.get()); - return {}; - } -} - -void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) { - is_async_ = queue_info.producer_config.is_async; - default_width_ = queue_info.producer_config.default_width; - default_height_ = queue_info.producer_config.default_height; - default_format_ = queue_info.producer_config.default_format; - user_metadata_size_ = queue_info.producer_config.user_metadata_size; - id_ = queue_info.id; -} - -std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() { - if (auto status = CreateConsumerQueueHandle(/*silent*/ false)) - return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); - else - return nullptr; -} - -std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() { - if (auto status = CreateConsumerQueueHandle(/*silent*/ true)) - return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); - else - return nullptr; -} - -Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle( - bool silent) { - auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent); - if (!status) { - ALOGE( - "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " - "%s", - status.GetErrorMessage().c_str()); - return ErrorStatus(status.error()); - } - - return status; -} - -pdx::Status<ConsumerQueueParcelable> -BufferHubQueue::CreateConsumerQueueParcelable(bool silent) { - auto status = CreateConsumerQueueHandle(silent); - if (!status) - return status.error_status(); - - // A temporary consumer queue client to pull its channel parcelable. - auto consumer_queue = - std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); - ConsumerQueueParcelable queue_parcelable( - consumer_queue->GetChannel()->TakeChannelParcelable()); - - if (!queue_parcelable.IsValid()) { - ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__); - return ErrorStatus(EINVAL); - } - - return {std::move(queue_parcelable)}; -} - -bool BufferHubQueue::WaitForBuffers(int timeout) { - ATRACE_NAME("BufferHubQueue::WaitForBuffers"); - std::array<epoll_event, kMaxEvents> events; - - // Loop at least once to check for hangups. - do { - ALOGD_IF( - TRACE, - "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu", - id(), count(), capacity()); - - // If there is already a buffer then just check for hangup without waiting. - const int ret = epoll_fd_.Wait(events.data(), events.size(), - count() == 0 ? timeout : 0); - - if (ret == 0) { - ALOGI_IF(TRACE, - "BufferHubQueue::WaitForBuffers: No events before timeout: " - "queue_id=%d", - id()); - return count() != 0; - } - - if (ret < 0 && ret != -EINTR) { - ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret)); - return false; - } - - const int num_events = ret; - - // A BufferQueue's epoll fd tracks N+1 events, where there are N events, - // one for each buffer in the queue, and one extra event for the queue - // client itself. - for (int i = 0; i < num_events; i++) { - int32_t event_fd; - int32_t index; - std::tie(event_fd, index) = Unstuff(events[i].data.u64); - - PDX_TRACE_FORMAT( - "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;" - "slot=%d|", - id(), num_events, i, event_fd, index); - - ALOGD_IF(TRACE, - "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d", - i, event_fd, index); - - if (is_buffer_event_index(index)) { - HandleBufferEvent(static_cast<size_t>(index), event_fd, - events[i].events); - } else if (is_queue_event_index(index)) { - HandleQueueEvent(events[i].events); - } else { - ALOGW( - "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d " - "index=%d", - event_fd, index); - } - } - } while (count() == 0 && capacity() > 0 && !hung_up()); - - return count() != 0; -} - -Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, - int poll_events) { - ATRACE_NAME("BufferHubQueue::HandleBufferEvent"); - if (!buffers_[slot]) { - ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); - return ErrorStatus(ENOENT); - } - - auto status = buffers_[slot]->GetEventMask(poll_events); - if (!status) { - ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - const int events = status.get(); - PDX_TRACE_FORMAT( - "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;" - "events=%d|", - id(), buffers_[slot]->id(), slot, event_fd, poll_events, events); - - if (events & EPOLLIN) { - return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()}); - } else if (events & EPOLLHUP) { - ALOGW( - "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu " - "event_fd=%d buffer_id=%d", - slot, buffers_[slot]->event_fd(), buffers_[slot]->id()); - return RemoveBuffer(slot); - } else { - ALOGW( - "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " - "events=%d", - slot, events); - } - - return {}; -} - -Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { - ATRACE_NAME("BufferHubQueue::HandleQueueEvent"); - auto status = GetEventMask(poll_event); - if (!status) { - ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - const int events = status.get(); - if (events & EPOLLIN) { - // Note that after buffer imports, if |count()| still returns 0, epoll - // wait will be tried again to acquire the newly imported buffer. - auto buffer_status = OnBufferAllocated(); - if (!buffer_status) { - ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, - buffer_status.GetErrorMessage().c_str()); - } - } else if (events & EPOLLHUP) { - ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__); - hung_up_ = true; - } else { - ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events); - } - - return {}; -} - -Status<void> BufferHubQueue::AddBuffer( - const std::shared_ptr<BufferHubBase>& buffer, size_t slot) { - ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(), - slot); - - if (is_full()) { - ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_); - return ErrorStatus(E2BIG); - } - - if (buffers_[slot]) { - // Replace the buffer if the slot is occupied. This could happen when the - // producer side replaced the slot with a newly allocated buffer. Remove the - // buffer before setting up with the new one. - auto remove_status = RemoveBuffer(slot); - if (!remove_status) - return remove_status.error_status(); - } - - for (const auto& event_source : buffer->GetEventSources()) { - epoll_event event = {.events = event_source.event_mask | EPOLLET, - .data = {.u64 = Stuff(buffer->event_fd(), slot)}}; - const int ret = - epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event); - if (ret < 0) { - ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__, - strerror(-ret)); - return ErrorStatus(-ret); - } - } - - buffers_[slot] = buffer; - capacity_++; - return {}; -} - -Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { - ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot); - - if (buffers_[slot]) { - for (const auto& event_source : buffers_[slot]->GetEventSources()) { - const int ret = - epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr); - if (ret < 0) { - ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__, - strerror(-ret)); - return ErrorStatus(-ret); - } - } - - // Trigger OnBufferRemoved callback if registered. - if (on_buffer_removed_) - on_buffer_removed_(buffers_[slot]); - - buffers_[slot] = nullptr; - capacity_--; - } - - return {}; -} - -Status<void> BufferHubQueue::Enqueue(Entry entry) { - if (!is_full()) { - // Find and remove the enqueued buffer from unavailable_buffers_slot if - // exist. - auto enqueued_buffer_iter = std::find_if( - unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(), - [&entry](size_t slot) -> bool { return slot == entry.slot; }); - if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) { - unavailable_buffers_slot_.erase(enqueued_buffer_iter); - } - - available_buffers_.push(std::move(entry)); - - // Trigger OnBufferAvailable callback if registered. - if (on_buffer_available_) - on_buffer_available_(); - - return {}; - } else { - ALOGE("%s: Buffer queue is full!", __FUNCTION__); - return ErrorStatus(E2BIG); - } -} - -Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout, - size_t* slot) { - ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout); - - PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count()); - - if (count() == 0) { - if (!WaitForBuffers(timeout)) - return ErrorStatus(ETIMEDOUT); - } - - auto& entry = available_buffers_.top(); - PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), - entry.slot); - - std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer); - *slot = entry.slot; - - available_buffers_.pop(); - unavailable_buffers_slot_.push_back(*slot); - - return {std::move(buffer)}; -} - -void BufferHubQueue::SetBufferAvailableCallback( - BufferAvailableCallback callback) { - on_buffer_available_ = callback; -} - -void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) { - on_buffer_removed_ = callback; -} - -pdx::Status<void> BufferHubQueue::FreeAllBuffers() { - // Clear all available buffers. - while (!available_buffers_.empty()) - available_buffers_.pop(); - - pdx::Status<void> last_error; // No error. - // Clear all buffers this producer queue is tracking. - for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { - if (buffers_[slot] != nullptr) { - auto status = RemoveBuffer(slot); - if (!status) { - ALOGE( - "ProducerQueue::FreeAllBuffers: Failed to remove buffer at " - "slot=%zu.", - slot); - last_error = status.error_status(); - } - } - } - - return last_error; -} - -ProducerQueue::ProducerQueue(LocalChannelHandle handle) - : BASE(std::move(handle)) { - auto status = ImportQueue(); - if (!status) { - ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - } -} - -ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, - const UsagePolicy& usage) - : BASE(BufferHubRPC::kClientPath) { - auto status = - InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage); - if (!status) { - ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - return; - } - - SetupQueue(status.get()); -} - -Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( - uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t buffer_count) { - if (buffer_count == 0) { - return {std::vector<size_t>()}; - } - - if (capacity() + buffer_count > kMaxQueueCapacity) { - ALOGE( - "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot " - "allocate %zu more buffer(s).", - capacity(), buffer_count); - return ErrorStatus(E2BIG); - } - - Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status = - InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>( - width, height, layer_count, format, usage, buffer_count); - if (!status) { - ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto buffer_handle_slots = status.take(); - LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count, - "BufferHubRPC::ProducerQueueAllocateBuffers should " - "return %zu buffer handle(s), but returned %zu instead.", - buffer_count, buffer_handle_slots.size()); - - std::vector<size_t> buffer_slots; - buffer_slots.reserve(buffer_count); - - // Bookkeeping for each buffer. - for (auto& hs : buffer_handle_slots) { - auto& buffer_handle = hs.first; - size_t buffer_slot = hs.second; - - // Note that import might (though very unlikely) fail. If so, buffer_handle - // will be closed and included in returned buffer_slots. - if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)), - buffer_slot)) { - ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu", - buffer_slot); - buffer_slots.push_back(buffer_slot); - } - } - - if (buffer_slots.size() != buffer_count) { - // Error out if the count of imported buffer(s) is not correct. - ALOGE( - "ProducerQueue::AllocateBuffers: requested to import %zu " - "buffers, but actually imported %zu buffers.", - buffer_count, buffer_slots.size()); - return ErrorStatus(ENOMEM); - } - - return {std::move(buffer_slots)}; -} - -Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, - uint32_t layer_count, - uint32_t format, uint64_t usage) { - // We only allocate one buffer at a time. - constexpr size_t buffer_count = 1; - auto status = - AllocateBuffers(width, height, layer_count, format, usage, buffer_count); - if (!status) { - ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - return {status.get()[0]}; -} - -Status<void> ProducerQueue::AddBuffer( - const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", - id(), buffer->id(), slot); - // For producer buffer, we need to enqueue the newly added buffer - // immediately. Producer queue starts with all buffers in available state. - auto status = BufferHubQueue::AddBuffer(buffer, slot); - if (!status) - return status; - - return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); -} - -Status<size_t> ProducerQueue::InsertBuffer( - const std::shared_ptr<ProducerBuffer>& buffer) { - if (buffer == nullptr || - !BufferHubDefs::isClientGained(buffer->buffer_state(), - buffer->client_state_mask())) { - ALOGE( - "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in " - "gained state."); - return ErrorStatus(EINVAL); - } - - auto status_or_slot = - InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>( - buffer->cid()); - if (!status_or_slot) { - ALOGE( - "ProducerQueue::InsertBuffer: Failed to insert producer buffer: " - "buffer_cid=%d, error: %s.", - buffer->cid(), status_or_slot.GetErrorMessage().c_str()); - return status_or_slot.error_status(); - } - - size_t slot = status_or_slot.get(); - - // Note that we are calling AddBuffer() from the base class to explicitly - // avoid Enqueue() the ProducerBuffer. - auto status = BufferHubQueue::AddBuffer(buffer, slot); - if (!status) { - ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - return {slot}; -} - -Status<void> ProducerQueue::RemoveBuffer(size_t slot) { - auto status = - InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot); - if (!status) { - ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - return BufferHubQueue::RemoveBuffer(slot); -} - -Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue( - int timeout, size_t* slot, LocalHandle* release_fence) { - DvrNativeBufferMetadata canonical_meta; - return Dequeue(timeout, slot, &canonical_meta, release_fence); -} - -pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* release_fence, bool gain_posted_buffer) { - ATRACE_NAME("ProducerQueue::Dequeue"); - if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { - ALOGE("%s: Invalid parameter.", __FUNCTION__); - return ErrorStatus(EINVAL); - } - - std::shared_ptr<ProducerBuffer> buffer; - Status<std::shared_ptr<BufferHubBase>> dequeue_status = - BufferHubQueue::Dequeue(timeout, slot); - if (dequeue_status.ok()) { - buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take()); - } else { - if (gain_posted_buffer) { - Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status = - ProducerQueue::DequeueUnacquiredBuffer(slot); - if (!dequeue_unacquired_status.ok()) { - ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__, - dequeue_unacquired_status.error()); - return dequeue_unacquired_status.error_status(); - } - buffer = dequeue_unacquired_status.take(); - } else { - return dequeue_status.error_status(); - } - } - const int ret = - buffer->GainAsync(out_meta, release_fence, gain_posted_buffer); - if (ret < 0 && ret != -EALREADY) - return ErrorStatus(-ret); - - return {std::move(buffer)}; -} - -Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer( - size_t* slot) { - if (unavailable_buffers_slot_.size() < 1) { - ALOGE( - "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in " - "acquired state if exist.", - __FUNCTION__); - return ErrorStatus(ENOMEM); - } - - // Find the first buffer that is not in acquired state from - // unavailable_buffers_slot_. - for (auto iter = unavailable_buffers_slot_.begin(); - iter != unavailable_buffers_slot_.end(); iter++) { - std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter); - if (buffer == nullptr) { - ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__, - static_cast<int>(*slot)); - return ErrorStatus(EIO); - } - if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) { - *slot = *iter; - unavailable_buffers_slot_.erase(iter); - unavailable_buffers_slot_.push_back(*slot); - ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d", - __FUNCTION__, static_cast<int>(*slot)); - return {std::move(buffer)}; - } - } - ALOGE( - "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.", - __FUNCTION__); - return ErrorStatus(EBUSY); -} - -pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() { - if (capacity() != 0) { - ALOGE( - "%s: producer queue can only be taken out as a parcelable when empty. " - "Current queue capacity: %zu", - __FUNCTION__, capacity()); - return ErrorStatus(EINVAL); - } - - std::unique_ptr<pdx::ClientChannel> channel = TakeChannel(); - ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable()); - - // Here the queue parcelable is returned and holds the underlying system - // resources backing the queue; while the original client channel of this - // producer queue is destroyed in place so that this client can no longer - // provide producer operations. - return {std::move(queue_parcelable)}; -} - -/*static */ -std::unique_ptr<ConsumerQueue> ConsumerQueue::Import( - LocalChannelHandle handle) { - return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle))); -} - -ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) - : BufferHubQueue(std::move(handle)) { - auto status = ImportQueue(); - if (!status) { - ALOGE("%s: Failed to import queue: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - Close(-status.error()); - } - - auto import_status = ImportBuffers(); - if (import_status) { - ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get()); - } else { - ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, - import_status.GetErrorMessage().c_str()); - } -} - -Status<size_t> ConsumerQueue::ImportBuffers() { - auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); - if (!status) { - if (status.error() == EBADR) { - ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__); - return {0}; - } else { - ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return status.error_status(); - } - } - - int ret; - Status<void> last_error; - size_t imported_buffers_count = 0; - - auto buffer_handle_slots = status.take(); - for (auto& buffer_handle_slot : buffer_handle_slots) { - ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__, - buffer_handle_slot.first.value()); - - std::unique_ptr<ConsumerBuffer> consumer_buffer = - ConsumerBuffer::Import(std::move(buffer_handle_slot.first)); - if (!consumer_buffer) { - ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__, - buffer_handle_slot.second); - last_error = ErrorStatus(EPIPE); - continue; - } - - auto add_status = - AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second); - if (!add_status) { - ALOGE("%s: Failed to add buffer: %s", __FUNCTION__, - add_status.GetErrorMessage().c_str()); - last_error = add_status; - } else { - imported_buffers_count++; - } - } - - if (imported_buffers_count > 0) - return {imported_buffers_count}; - else - return last_error.error_status(); -} - -Status<void> ConsumerQueue::AddBuffer( - const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(), - buffer->id(), slot); - return BufferHubQueue::AddBuffer(buffer, slot); -} - -Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue( - int timeout, size_t* slot, void* meta, size_t user_metadata_size, - LocalHandle* acquire_fence) { - if (user_metadata_size != user_metadata_size_) { - ALOGE( - "%s: Metadata size (%zu) for the dequeuing buffer does not match " - "metadata size (%zu) for the queue.", - __FUNCTION__, user_metadata_size, user_metadata_size_); - return ErrorStatus(EINVAL); - } - - DvrNativeBufferMetadata canonical_meta; - auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence); - if (!status) - return status.error_status(); - - if (meta && user_metadata_size) { - void* metadata_src = - reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); - if (metadata_src) { - memcpy(meta, metadata_src, user_metadata_size); - } else { - ALOGW("%s: no user-defined metadata.", __FUNCTION__); - } - } - - return status; -} - -Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* acquire_fence) { - ATRACE_NAME("ConsumerQueue::Dequeue"); - if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) { - ALOGE("%s: Invalid parameter.", __FUNCTION__); - return ErrorStatus(EINVAL); - } - - auto status = BufferHubQueue::Dequeue(timeout, slot); - if (!status) - return status.error_status(); - - auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take()); - const int ret = buffer->AcquireAsync(out_meta, acquire_fence); - if (ret < 0) - return ErrorStatus(-ret); - - return {std::move(buffer)}; -} - -Status<void> ConsumerQueue::OnBufferAllocated() { - ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id()); - - auto status = ImportBuffers(); - if (!status) { - ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return ErrorStatus(status.error()); - } else if (status.get() == 0) { - ALOGW("%s: No new buffers allocated!", __FUNCTION__); - return ErrorStatus(ENOBUFS); - } else { - ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__, - status.get()); - return {}; - } -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp deleted file mode 100644 index f705749243..0000000000 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "include/private/dvr/buffer_hub_queue_parcelable.h" - -#include <binder/Parcel.h> -#include <pdx/default_transport/channel_parcelable.h> - -namespace android { -namespace dvr { - -template <BufferHubQueueParcelableMagic Magic> -bool BufferHubQueueParcelable<Magic>::IsValid() const { - return !!channel_parcelable_ && channel_parcelable_->IsValid(); -} - -template <BufferHubQueueParcelableMagic Magic> -pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() { - if (!IsValid()) { - ALOGE( - "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel."); - return {}; // Returns an empty channel handle. - } - - // Take channel handle out of the parcelable and reset the parcelable. - pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle(); - // Now channel_parcelable_ should already be invalid, but reset it to release - // the invalid parcelable object from unique_ptr. - channel_parcelable_ = nullptr; - return handle; -} - -template <BufferHubQueueParcelableMagic Magic> -status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const { - if (!IsValid()) { - ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel."); - return -EINVAL; - } - - status_t res = parcel->writeUint32(Magic); - if (res != OK) { - ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic."); - return res; - } - - return channel_parcelable_->writeToParcel(parcel); -} - -template <BufferHubQueueParcelableMagic Magic> -status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) { - if (IsValid()) { - ALOGE( - "BufferHubQueueParcelable::readFromParcel: This parcelable object has " - "been initialized already."); - return -EINVAL; - } - - uint32_t out_magic = 0; - status_t res = OK; - - res = parcel->readUint32(&out_magic); - if (res != OK) - return res; - - if (out_magic != Magic) { - ALOGE( - "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, " - "epxected: 0x%x", - out_magic, Magic); - return -EINVAL; - } - - // (Re)Alocate channel parcelable object. - channel_parcelable_ = - std::make_unique<pdx::default_transport::ChannelParcelable>(); - return channel_parcelable_->readFromParcel(parcel); -} - -template class BufferHubQueueParcelable< - BufferHubQueueParcelableMagic::Producer>; -template class BufferHubQueueParcelable< - BufferHubQueueParcelableMagic::Consumer>; - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h deleted file mode 100644 index 74b4b3d67f..0000000000 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h +++ /dev/null @@ -1,476 +0,0 @@ -#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ -#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ - -#include <ui/BufferQueueDefs.h> - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -// The following headers are included without checking every warning. -// TODO(b/72172820): Remove the workaround once we have enforced -Weverything -// in these headers and their dependencies. -#include <pdx/client.h> -#include <pdx/status.h> -#include <private/dvr/buffer_hub_queue_parcelable.h> -#include <private/dvr/bufferhub_rpc.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/epoll_file_descriptor.h> -#include <private/dvr/producer_buffer.h> - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#include <memory> -#include <queue> -#include <vector> - -namespace android { -namespace dvr { - -class ConsumerQueue; - -// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are -// automatically re-requeued when released by the remote side. -class BufferHubQueue : public pdx::Client { - public: - using BufferAvailableCallback = std::function<void()>; - using BufferRemovedCallback = - std::function<void(const std::shared_ptr<BufferHubBase>&)>; - - virtual ~BufferHubQueue() {} - - // Creates a new consumer queue that is attached to the producer. Returns - // a new consumer queue client or nullptr on failure. - std::unique_ptr<ConsumerQueue> CreateConsumerQueue(); - - // Creates a new consumer queue that is attached to the producer. This queue - // sets each of its imported consumer buffers to the ignored state to avoid - // participation in lifecycle events. - std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue(); - - // Returns whether the buffer queue is in async mode. - bool is_async() const { return is_async_; } - - // Returns the default buffer width of this buffer queue. - uint32_t default_width() const { return default_width_; } - - // Returns the default buffer height of this buffer queue. - uint32_t default_height() const { return default_height_; } - - // Returns the default buffer format of this buffer queue. - uint32_t default_format() const { return default_format_; } - - // Creates a new consumer in handle form for immediate transport over RPC. - pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle( - bool silent = false); - - // Creates a new consumer in parcelable form for immediate transport over - // Binder. - pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable( - bool silent = false); - - // Returns the number of buffers avaiable for dequeue. - size_t count() const { return available_buffers_.size(); } - - // Returns the total number of buffers that the queue is tracking. - size_t capacity() const { return capacity_; } - - // Returns the size of metadata structure associated with this queue. - size_t metadata_size() const { return user_metadata_size_; } - - // Returns whether the buffer queue is full. - bool is_full() const { - return available_buffers_.size() >= kMaxQueueCapacity; - } - - // Returns whether the buffer queue is connected to bufferhubd. - bool is_connected() const { return !!GetChannel(); } - - int GetBufferId(size_t slot) const { - return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id() - : -1; - } - - std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const { - return buffers_[slot]; - } - - pdx::Status<int> GetEventMask(int events) { - if (auto* client_channel = GetChannel()) { - return client_channel->GetEventMask(events); - } else { - return pdx::ErrorStatus(EINVAL); - } - } - - // Returns an fd that signals pending queue events using - // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be - // called to handle pending queue events. - int queue_fd() const { return epoll_fd_.Get(); } - - // Handles any pending events, returning available buffers to the queue and - // reaping disconnected buffers. Returns true if successful, false if an error - // occurred. - bool HandleQueueEvents() { return WaitForBuffers(0); } - - // Set buffer event callbacks, which are std::function wrappers. The caller is - // responsible for ensuring the validity of these callbacks' callable targets. - void SetBufferAvailableCallback(BufferAvailableCallback callback); - void SetBufferRemovedCallback(BufferRemovedCallback callback); - - // The queue tracks at most this many buffers. - static constexpr size_t kMaxQueueCapacity = - android::BufferQueueDefs::NUM_BUFFER_SLOTS; - - static constexpr int kNoTimeOut = -1; - - int id() const { return id_; } - bool hung_up() const { return hung_up_; } - - protected: - explicit BufferHubQueue(pdx::LocalChannelHandle channel); - explicit BufferHubQueue(const std::string& endpoint_path); - - // Imports the queue parameters by querying BufferHub for the parameters for - // this channel. - pdx::Status<void> ImportQueue(); - - // Sets up the queue with the given parameters. - void SetupQueue(const QueueInfo& queue_info); - - // Register a buffer for management by the queue. Used by subclasses to add a - // buffer to internal bookkeeping. - pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer, - size_t slot); - - // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only - // to deregister a buffer for epoll and internal bookkeeping. - virtual pdx::Status<void> RemoveBuffer(size_t slot); - - // Free all buffers that belongs to this queue. Can only be called from - // producer side. - virtual pdx::Status<void> FreeAllBuffers(); - - // Dequeue a buffer from the free queue, blocking until one is available. The - // timeout argument specifies the number of milliseconds that |Dequeue()| will - // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely, - // while specifying a timeout equal to zero cause Dequeue() to return - // immediately, even if no buffers are available. - pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout, - size_t* slot); - - // Waits for buffers to become available and adds them to the available queue. - bool WaitForBuffers(int timeout); - - pdx::Status<void> HandleBufferEvent(size_t slot, int event_fd, - int poll_events); - pdx::Status<void> HandleQueueEvent(int poll_events); - - // Entry in the priority queue of available buffers that stores related - // per-buffer data. - struct Entry { - Entry() : slot(0) {} - Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot, - uint64_t in_index) - : buffer(in_buffer), slot(in_slot), index(in_index) {} - Entry(const std::shared_ptr<BufferHubBase>& in_buffer, - std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence, - size_t in_slot) - : buffer(in_buffer), - metadata(std::move(in_metadata)), - fence(std::move(in_fence)), - slot(in_slot) {} - Entry(Entry&&) = default; - Entry& operator=(Entry&&) = default; - - std::shared_ptr<BufferHubBase> buffer; - std::unique_ptr<uint8_t[]> metadata; - pdx::LocalHandle fence; - size_t slot; - uint64_t index; - }; - - struct EntryComparator { - bool operator()(const Entry& lhs, const Entry& rhs) { - return lhs.index > rhs.index; - } - }; - - // Enqueues a buffer to the available list (Gained for producer or Acquireed - // for consumer). - pdx::Status<void> Enqueue(Entry entry); - - // Called when a buffer is allocated remotely. - virtual pdx::Status<void> OnBufferAllocated() { return {}; } - - // Size of the metadata that buffers in this queue cary. - size_t user_metadata_size_{0}; - - // Buffers and related data that are available for dequeue. - std::priority_queue<Entry, std::vector<Entry>, EntryComparator> - available_buffers_; - - // Slot of the buffers that are not available for normal dequeue. For example, - // the slot of posted or acquired buffers in the perspective of a producer. - std::vector<size_t> unavailable_buffers_slot_; - - private: - void Initialize(); - - // Special epoll data field indicating that the epoll event refers to the - // queue. - static constexpr int64_t kEpollQueueEventIndex = -1; - - static constexpr size_t kMaxEvents = 128; - - // The u64 data field of an epoll event is interpreted as int64_t: - // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific - // element of |buffers_| as a direct index; - static bool is_buffer_event_index(int64_t index) { - return index >= 0 && - index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity); - } - - // When |index| == kEpollQueueEventIndex it refers to the queue itself. - static bool is_queue_event_index(int64_t index) { - return index == BufferHubQueue::kEpollQueueEventIndex; - } - - // Whether the buffer queue is operating in Async mode. - // From GVR's perspective of view, this means a buffer can be acquired - // asynchronously by the compositor. - // From Android Surface's perspective of view, this is equivalent to - // IGraphicBufferProducer's async mode. When in async mode, a producer - // will never block even if consumer is running slow. - bool is_async_{false}; - - // Default buffer width that is set during ProducerQueue's creation. - uint32_t default_width_{1}; - - // Default buffer height that is set during ProducerQueue's creation. - uint32_t default_height_{1}; - - // Default buffer format that is set during ProducerQueue's creation. - uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888 - - // Tracks the buffers belonging to this queue. Buffers are stored according to - // "slot" in this vector. Each slot is a logical id of the buffer within this - // queue regardless of its queue position or presence in the ring buffer. - std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_; - - // Keeps track with how many buffers have been added into the queue. - size_t capacity_{0}; - - // Epoll fd used to manage buffer events. - EpollFileDescriptor epoll_fd_; - - // Flag indicating that the other side hung up. For ProducerQueues this - // triggers when BufferHub dies or explicitly closes the queue channel. For - // ConsumerQueues this can either mean the same or that the ProducerQueue on - // the other end hung up. - bool hung_up_{false}; - - // Global id for the queue that is consistent across processes. - int id_{-1}; - - // Buffer event callbacks - BufferAvailableCallback on_buffer_available_; - BufferRemovedCallback on_buffer_removed_; - - BufferHubQueue(const BufferHubQueue&) = delete; - void operator=(BufferHubQueue&) = delete; -}; - -class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { - public: - // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits - // in |usage_clear_mask| will be automatically masked off. Note that - // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but - // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer - // allocation through this producer queue shall not have any of the usage bits - // in |usage_deny_set_mask| set. Allocation calls violating this will be - // rejected. All buffer allocation through this producer queue must have all - // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating - // this will be rejected. Note that |usage_deny_set_mask| and - // |usage_deny_clear_mask| shall not conflict with each other. Such - // configuration will be treated as invalid input on creation. - static std::unique_ptr<ProducerQueue> Create( - const ProducerQueueConfig& config, const UsagePolicy& usage) { - return BASE::Create(config, usage); - } - - // Import a ProducerQueue from a channel handle. - static std::unique_ptr<ProducerQueue> Import(pdx::LocalChannelHandle handle) { - return BASE::Create(std::move(handle)); - } - - // Get a producer buffer. Note that the method doesn't check whether the - // buffer slot has a valid buffer that has been allocated already. When no - // buffer has been imported before it returns nullptr; otherwise it returns - // a shared pointer to a ProducerBuffer. - std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const { - return std::static_pointer_cast<ProducerBuffer>( - BufferHubQueue::GetBuffer(slot)); - } - - // Batch allocate buffers. Once allocated, producer buffers are automatically - // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED - // state). Upon success, returns a list of slots for each buffer allocated. - pdx::Status<std::vector<size_t>> AllocateBuffers( - uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t buffer_count); - - // Allocate producer buffer to populate the queue. Once allocated, a producer - // buffer is automatically enqueue'd into the ProducerQueue and available to - // use (i.e. in GAINED state). Upon success, returns the slot number for the - // buffer allocated. - pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height, - uint32_t layer_count, uint32_t format, - uint64_t usage); - - // Add a producer buffer to populate the queue. Once added, a producer buffer - // is available to use (i.e. in GAINED state). - pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer, - size_t slot); - - // Inserts a ProducerBuffer into the queue. On success, the method returns the - // |slot| number where the new buffer gets inserted. Note that the buffer - // being inserted should be in Gain'ed state prior to the call and it's - // considered as already Dequeued when the function returns. - pdx::Status<size_t> InsertBuffer( - const std::shared_ptr<ProducerBuffer>& buffer); - - // Remove producer buffer from the queue. - pdx::Status<void> RemoveBuffer(size_t slot) override; - - // Free all buffers on this producer queue. - pdx::Status<void> FreeAllBuffers() override { - return BufferHubQueue::FreeAllBuffers(); - } - - // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, - // and caller should call Post() once it's done writing to release the buffer - // to the consumer side. - // @return a buffer in gained state, which was originally in released state. - pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue( - int timeout, size_t* slot, pdx::LocalHandle* release_fence); - - // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, - // and caller should call Post() once it's done writing to release the buffer - // to the consumer side. - // - // @param timeout to dequeue a buffer. - // @param slot is the slot of the output ProducerBuffer. - // @param release_fence for gaining a buffer. - // @param out_meta metadata of the output buffer. - // @param gain_posted_buffer whether to gain posted buffer if no released - // buffer is available to gain. - // @return a buffer in gained state, which was originally in released state if - // gain_posted_buffer is false, or in posted/released state if - // gain_posted_buffer is true. - // TODO(b/112007999): gain_posted_buffer true is only used to prevent - // libdvrtracking from starving when there are non-responding clients. This - // gain_posted_buffer param can be removed once libdvrtracking start to use - // the new AHardwareBuffer API. - pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* release_fence, bool gain_posted_buffer = false); - - // Enqueues a producer buffer in the queue. - pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer, - size_t slot, uint64_t index) { - return BufferHubQueue::Enqueue({buffer, slot, index}); - } - - // Takes out the current producer queue as a binder parcelable object. Note - // that the queue must be empty to be exportable. After successful export, the - // producer queue client should no longer be used. - pdx::Status<ProducerQueueParcelable> TakeAsParcelable(); - - private: - friend BASE; - - // Constructors are automatically exposed through ProducerQueue::Create(...) - // static template methods inherited from ClientBase, which take the same - // arguments as the constructors. - explicit ProducerQueue(pdx::LocalChannelHandle handle); - ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage); - - // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, - // and caller should call Post() once it's done writing to release the buffer - // to the consumer side. - // - // @param slot the slot of the returned buffer. - // @return a buffer in gained state, which was originally in posted state or - // released state. - pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer( - size_t* slot); -}; - -class ConsumerQueue : public BufferHubQueue { - public: - // Get a consumer buffer. Note that the method doesn't check whether the - // buffer slot has a valid buffer that has been imported already. When no - // buffer has been imported before it returns nullptr; otherwise returns a - // shared pointer to a ConsumerBuffer. - std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const { - return std::static_pointer_cast<ConsumerBuffer>( - BufferHubQueue::GetBuffer(slot)); - } - - // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls - // whether or not buffers are set to be ignored when imported. This may be - // used to avoid participation in the buffer lifecycle by a consumer queue - // that is only used to spawn other consumer queues, such as in an - // intermediate service. - static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle); - - // Import newly created buffers from the service side. - // Returns number of buffers successfully imported or an error. - pdx::Status<size_t> ImportBuffers(); - - // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed - // mode, and caller should call Releasse() once it's done writing to release - // the buffer to the producer side. |meta| is passed along from BufferHub, - // The user of ProducerBuffer is responsible with making sure that the - // Dequeue() is done with the corect metadata type and size with those used - // when the buffer is orignally created. - template <typename Meta> - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) { - return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence); - } - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) { - return Dequeue(timeout, slot, nullptr, 0, acquire_fence); - } - - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, void* meta, size_t user_metadata_size, - pdx::LocalHandle* acquire_fence); - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* acquire_fence); - - private: - friend BufferHubQueue; - - explicit ConsumerQueue(pdx::LocalChannelHandle handle); - - // Add a consumer buffer to populate the queue. Once added, a consumer buffer - // is NOT available to use until the producer side |Post| it. |WaitForBuffers| - // will catch the |Post| and |Acquire| the buffer to make it available for - // consumer. - pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, - size_t slot); - - pdx::Status<void> OnBufferAllocated() override; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h deleted file mode 100644 index 36ab5f6ac7..0000000000 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ -#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -// The following headers are included without checking every warning. -// TODO(b/72172820): Remove the workaround once we have enforced -Weverything -// in these headers and their dependencies. -#include <pdx/channel_parcelable.h> - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -namespace android { -namespace dvr { - -enum BufferHubQueueParcelableMagic : uint32_t { - Producer = 0x62687170, // 'bhqp' - Consumer = 0x62687163, // 'bhqc' -}; - -template <BufferHubQueueParcelableMagic Magic> -class BufferHubQueueParcelable : public Parcelable { - public: - BufferHubQueueParcelable() = default; - - BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default; - BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept { - channel_parcelable_ = std::move(other.channel_parcelable_); - return *this; - } - - // Constructs an parcelable contains the channel parcelable. - explicit BufferHubQueueParcelable( - std::unique_ptr<pdx::ChannelParcelable> channel_parcelable) - : channel_parcelable_(std::move(channel_parcelable)) {} - - BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete; - void operator=(const BufferHubQueueParcelable&) = delete; - - bool IsValid() const; - - // Returns a channel handle constructed from this parcelable object and takes - // the ownership of all resources from the parcelable object. - pdx::LocalChannelHandle TakeChannelHandle(); - - // Serializes the queue parcelable into the given parcel. Note that no system - // resources are getting duplicated, nor did the parcel takes ownership of the - // queue parcelable. Thus, the parcelable object must remain valid for the - // lifetime of the parcel. - status_t writeToParcel(Parcel* parcel) const override; - - // Deserialize the queue parcelable from the given parcel. Note that system - // resources are duplicated from the parcel into the queue parcelable. Returns - // error if the targeting parcelable object is already valid. - status_t readFromParcel(const Parcel* parcel) override; - - private: - std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_; -}; - -using ProducerQueueParcelable = - BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>; -using ConsumerQueueParcelable = - BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h deleted file mode 100644 index 2f14f7cd91..0000000000 --- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ -#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ - -#include <android-base/unique_fd.h> -#include <log/log.h> -#include <sys/epoll.h> - -namespace android { -namespace dvr { - -class EpollFileDescriptor { - public: - static const int CTL_ADD = EPOLL_CTL_ADD; - static const int CTL_MOD = EPOLL_CTL_MOD; - static const int CTL_DEL = EPOLL_CTL_DEL; - - EpollFileDescriptor() : fd_(-1) {} - - // Constructs an EpollFileDescriptor from an integer file descriptor and - // takes ownership. - explicit EpollFileDescriptor(int fd) : fd_(fd) {} - - bool IsValid() const { return fd_.get() >= 0; } - - int Create() { - if (IsValid()) { - ALOGW("epoll fd has already been created."); - return -EALREADY; - } - - fd_.reset(epoll_create1(EPOLL_CLOEXEC)); - - if (fd_.get() < 0) - return -errno; - else - return 0; - } - - int Control(int op, int target_fd, epoll_event* ev) { - if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0) - return -errno; - else - return 0; - } - - int Wait(epoll_event* events, int maxevents, int timeout) { - int ret = epoll_wait(fd_.get(), events, maxevents, timeout); - - if (ret < 0) - return -errno; - else - return ret; - } - - int Get() const { return fd_.get(); } - - private: - base::unique_fd fd_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp deleted file mode 100644 index 33a0d754c1..0000000000 --- a/libs/vr/libbufferhubqueue/tests/Android.bp +++ /dev/null @@ -1,66 +0,0 @@ - -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"], -} - -header_libraries = [ - "libdvr_headers", -] - -shared_libraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libpdx_default_transport", -] - -static_libraries = [ - "libchrome", - "libdvrcommon", - "libperformance", -] - -cc_test { - srcs: ["buffer_hub_queue-test.cpp"], - header_libs: header_libraries, - static_libs: static_libraries, - shared_libs: shared_libraries, - cflags: [ - "-DLOG_TAG=\"buffer_hub_queue-test\"", - "-DTRACE=0", - "-O0", - "-g", - "-Wall", - "-Werror", - "-Wno-error=sign-compare", // to fix later - ], - name: "buffer_hub_queue-test", -} - -cc_test { - srcs: ["buffer_hub_queue_producer-test.cpp"], - header_libs: header_libraries, - static_libs: static_libraries, - shared_libs: shared_libraries, - cflags: [ - "-DLOG_TAG=\"buffer_hub_queue_producer-test\"", - "-DTRACE=0", - "-O0", - "-g", - "-Wall", - "-Werror", - ], - name: "buffer_hub_queue_producer-test", -} diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp deleted file mode 100644 index 6ae603b892..0000000000 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp +++ /dev/null @@ -1,1083 +0,0 @@ -#include <base/logging.h> -#include <binder/Parcel.h> -#include <dvr/dvr_api.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/producer_buffer.h> - -#include <gtest/gtest.h> -#include <poll.h> -#include <sys/eventfd.h> - -#include <vector> - -// Enable/disable debug logging. -#define TRACE 0 - -namespace android { -namespace dvr { - -using pdx::LocalChannelHandle; -using pdx::LocalHandle; - -namespace { - -constexpr uint32_t kBufferWidth = 100; -constexpr uint32_t kBufferHeight = 1; -constexpr uint32_t kBufferLayerCount = 1; -constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; -constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; -constexpr int kTimeoutMs = 100; -constexpr int kNoTimeout = 0; - -class BufferHubQueueTest : public ::testing::Test { - public: - bool CreateProducerQueue(const ProducerQueueConfig& config, - const UsagePolicy& usage) { - producer_queue_ = ProducerQueue::Create(config, usage); - return producer_queue_ != nullptr; - } - - bool CreateConsumerQueue() { - if (producer_queue_) { - consumer_queue_ = producer_queue_->CreateConsumerQueue(); - return consumer_queue_ != nullptr; - } else { - return false; - } - } - - bool CreateQueues(const ProducerQueueConfig& config, - const UsagePolicy& usage) { - return CreateProducerQueue(config, usage) && CreateConsumerQueue(); - } - - void AllocateBuffer(size_t* slot_out = nullptr) { - // Create producer buffer. - auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, - kBufferFormat, kBufferUsage); - - ASSERT_TRUE(status.ok()); - size_t slot = status.take(); - if (slot_out) - *slot_out = slot; - } - - bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) { - pollfd pfd{queue->queue_fd(), POLLIN, 0}; - int ret; - do { - ret = poll(&pfd, 1, timeout_ms); - } while (ret == -1 && errno == EINTR); - - if (ret < 0) { - ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(), - strerror(errno)); - return false; - } else if (ret == 0) { - return false; - } - return queue->HandleQueueEvents(); - } - - protected: - ProducerQueueConfigBuilder config_builder_; - std::unique_ptr<ProducerQueue> producer_queue_; - std::unique_ptr<ConsumerQueue> consumer_queue_; -}; - -TEST_F(BufferHubQueueTest, TestDequeue) { - const int64_t nb_dequeue_times = 16; - - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Allocate only one buffer. - AllocateBuffer(); - - // But dequeue multiple times. - for (int64_t i = 0; i < nb_dequeue_times; i++) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer gains a buffer. - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Producer posts the buffer. - mi.index = i; - EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); - - // Consumer acquires a buffer. - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - EXPECT_EQ(mi.index, i); - EXPECT_EQ(mo.index, i); - - // Consumer releases the buffer. - EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); - } -} - -TEST_F(BufferHubQueueTest, - TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Allocate 3 buffers to use. - const size_t test_queue_capacity = 3; - for (int64_t i = 0; i < test_queue_capacity; i++) { - AllocateBuffer(); - } - EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); - - size_t producer_slot, consumer_slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer posts 2 buffers and remember their posted sequence. - std::deque<size_t> posted_slots; - for (int64_t i = 0; i < 2; i++) { - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Producer should not be gaining posted buffer when there are still - // available buffers to gain. - auto found_iter = - std::find(posted_slots.begin(), posted_slots.end(), producer_slot); - EXPECT_EQ(found_iter, posted_slots.end()); - posted_slots.push_back(producer_slot); - - // Producer posts the buffer. - mi.index = i; - EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); - } - - // Consumer acquires one buffer. - auto c1_status = - consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - // Consumer should get the oldest posted buffer. No checks here. - // posted_slots[0] should be in acquired state now. - EXPECT_EQ(mo.index, 0); - // Consumer releases the buffer. - EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); - // posted_slots[0] should be in released state now. - - // Producer gain and post 2 buffers. - for (int64_t i = 0; i < 2; i++) { - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // The gained buffer should be the one in released state or the one haven't - // been use. - EXPECT_NE(posted_slots[1], producer_slot); - - mi.index = i + 2; - EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); - } - - // Producer gains a buffer. - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // The gained buffer should be the oldest posted buffer. - EXPECT_EQ(posted_slots[1], producer_slot); - - // Producer posts the buffer. - mi.index = 4; - EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); -} - -TEST_F(BufferHubQueueTest, - TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Allocate 4 buffers to use. - const size_t test_queue_capacity = 4; - for (int64_t i = 0; i < test_queue_capacity; i++) { - AllocateBuffer(); - } - EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); - - // Post all allowed buffers and remember their posted sequence. - std::deque<size_t> posted_slots; - for (int64_t i = 0; i < test_queue_capacity; i++) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer gains a buffer. - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Producer should not be gaining posted buffer when there are still - // available buffers to gain. - auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot); - EXPECT_EQ(found_iter, posted_slots.end()); - posted_slots.push_back(slot); - - // Producer posts the buffer. - mi.index = i; - EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); - } - - // Gain posted buffers in sequence. - const int64_t nb_dequeue_all_times = 2; - for (int j = 0; j < nb_dequeue_all_times; ++j) { - for (int i = 0; i < test_queue_capacity; ++i) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer gains a buffer. - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // The gained buffer should be the oldest posted buffer. - EXPECT_EQ(posted_slots[i], slot); - - // Producer posts the buffer. - mi.index = i + test_queue_capacity * (j + 1); - EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); - } - } -} - -TEST_F(BufferHubQueueTest, TestProducerConsumer) { - const size_t kBufferCount = 16; - size_t slot; - DvrNativeBufferMetadata mi, mo; - LocalHandle fence; - - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - for (size_t i = 0; i < kBufferCount; i++) { - AllocateBuffer(); - - // Producer queue has all the available buffers on initialize. - ASSERT_EQ(producer_queue_->count(), i + 1); - ASSERT_EQ(producer_queue_->capacity(), i + 1); - - // Consumer queue has no avaiable buffer on initialize. - ASSERT_EQ(consumer_queue_->count(), 0U); - // Consumer queue does not import buffers until a dequeue is issued. - ASSERT_EQ(consumer_queue_->capacity(), i); - // Dequeue returns timeout since no buffer is ready to consumer, but - // this implicitly triggers buffer import and bump up |capacity|. - auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence); - ASSERT_FALSE(status.ok()); - ASSERT_EQ(ETIMEDOUT, status.error()); - ASSERT_EQ(consumer_queue_->capacity(), i + 1); - } - - // Use eventfd as a stand-in for a fence. - LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); - - for (size_t i = 0; i < kBufferCount; i++) { - // First time there is no buffer available to dequeue. - auto consumer_status = - consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence); - ASSERT_FALSE(consumer_status.ok()); - ASSERT_EQ(consumer_status.error(), ETIMEDOUT); - - // Make sure Producer buffer is POSTED so that it's ready to Accquire - // in the consumer's Dequeue() function. - auto producer_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - auto producer = producer_status.take(); - ASSERT_NE(nullptr, producer); - - mi.index = static_cast<int64_t>(i); - ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0); - - // Second time the just the POSTED buffer should be dequeued. - consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(consumer_status.ok()); - EXPECT_TRUE(fence.IsValid()); - - auto consumer = consumer_status.take(); - ASSERT_NE(nullptr, consumer); - ASSERT_EQ(mi.index, mo.index); - } -} - -TEST_F(BufferHubQueueTest, TestInsertBuffer) { - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - - consumer_queue_ = producer_queue_->CreateConsumerQueue(); - ASSERT_TRUE(consumer_queue_ != nullptr); - EXPECT_EQ(producer_queue_->capacity(), 0); - EXPECT_EQ(consumer_queue_->capacity(), 0); - - std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create( - kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0); - ASSERT_TRUE(p1 != nullptr); - ASSERT_EQ(p1->GainAsync(), 0); - - // Inserting a posted buffer will fail. - DvrNativeBufferMetadata meta; - EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0); - auto status_or_slot = producer_queue_->InsertBuffer(p1); - EXPECT_FALSE(status_or_slot.ok()); - EXPECT_EQ(status_or_slot.error(), EINVAL); - - // Inserting a gained buffer will succeed. - std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create( - kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage); - ASSERT_EQ(p2->GainAsync(), 0); - ASSERT_TRUE(p2 != nullptr); - status_or_slot = producer_queue_->InsertBuffer(p2); - EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage(); - // This is the first buffer inserted, should take slot 0. - size_t slot = status_or_slot.get(); - EXPECT_EQ(slot, 0); - - // Wait and expect the consumer to kick up the newly inserted buffer. - WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); - EXPECT_EQ(consumer_queue_->capacity(), 1ULL); -} - -TEST_F(BufferHubQueueTest, TestRemoveBuffer) { - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - DvrNativeBufferMetadata mo; - - // Allocate buffers. - const size_t kBufferCount = 4u; - for (size_t i = 0; i < kBufferCount; i++) { - AllocateBuffer(); - } - ASSERT_EQ(kBufferCount, producer_queue_->count()); - ASSERT_EQ(kBufferCount, producer_queue_->capacity()); - - consumer_queue_ = producer_queue_->CreateConsumerQueue(); - ASSERT_NE(nullptr, consumer_queue_); - - // Check that buffers are correctly imported on construction. - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_EQ(0u, consumer_queue_->count()); - - // Dequeue all the buffers and keep track of them in an array. This prevents - // the producer queue ring buffer ref counts from interfering with the tests. - struct Entry { - std::shared_ptr<ProducerBuffer> buffer; - LocalHandle fence; - size_t slot; - }; - std::array<Entry, kBufferCount> buffers; - - for (size_t i = 0; i < kBufferCount; i++) { - Entry* entry = &buffers[i]; - auto producer_status = - producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence); - ASSERT_TRUE(producer_status.ok()); - entry->buffer = producer_status.take(); - ASSERT_NE(nullptr, entry->buffer); - } - - // Remove a buffer and make sure both queues reflect the change. - ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot)); - EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); - - // As long as the removed buffer is still alive the consumer queue won't know - // its gone. - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Release the removed buffer. - buffers[0].buffer = nullptr; - - // Now the consumer queue should know it's gone. - EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs)); - ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity()); - - // Allocate a new buffer. This should take the first empty slot. - size_t slot; - AllocateBuffer(&slot); - ALOGE_IF(TRACE, "ALLOCATE %zu", slot); - EXPECT_EQ(buffers[0].slot, slot); - EXPECT_EQ(kBufferCount, producer_queue_->capacity()); - - // The consumer queue should pick up the new buffer. - EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Remove and allocate a buffer. - ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot)); - EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); - buffers[1].buffer = nullptr; - - AllocateBuffer(&slot); - ALOGE_IF(TRACE, "ALLOCATE %zu", slot); - EXPECT_EQ(buffers[1].slot, slot); - EXPECT_EQ(kBufferCount, producer_queue_->capacity()); - - // The consumer queue should pick up the new buffer but the count shouldn't - // change. - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Remove and allocate a buffer, but don't free the buffer right away. - ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot)); - EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); - - AllocateBuffer(&slot); - ALOGE_IF(TRACE, "ALLOCATE %zu", slot); - EXPECT_EQ(buffers[2].slot, slot); - EXPECT_EQ(kBufferCount, producer_queue_->capacity()); - - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Release the producer buffer to trigger a POLLHUP event for an already - // removed buffer. - buffers[2].buffer = nullptr; - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); -} - -TEST_F(BufferHubQueueTest, TestMultipleConsumers) { - // ProducerConfigureBuilder doesn't set Metadata{size}, which means there - // is no metadata associated with this BufferQueue's buffer. - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - - // Allocate buffers. - const size_t kBufferCount = 4u; - for (size_t i = 0; i < kBufferCount; i++) { - AllocateBuffer(); - } - ASSERT_EQ(kBufferCount, producer_queue_->count()); - - // Build a silent consumer queue to test multi-consumer queue features. - auto silent_queue = producer_queue_->CreateSilentConsumerQueue(); - ASSERT_NE(nullptr, silent_queue); - - // Check that silent queue doesn't import buffers on creation. - EXPECT_EQ(silent_queue->capacity(), 0U); - - // Dequeue and post a buffer. - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - auto producer_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(producer_status.ok()); - auto producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0); - // After post, check the number of remaining available buffers. - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); - - // Currently we expect no buffer to be available prior to calling - // WaitForBuffers/HandleQueueEvents. - // TODO(eieio): Note this behavior may change in the future. - EXPECT_EQ(silent_queue->count(), 0U); - EXPECT_FALSE(silent_queue->HandleQueueEvents()); - EXPECT_EQ(silent_queue->count(), 0U); - - // Build a new consumer queue to test multi-consumer queue features. - consumer_queue_ = silent_queue->CreateConsumerQueue(); - ASSERT_NE(consumer_queue_, nullptr); - - // Check that buffers are correctly imported on construction. - EXPECT_EQ(consumer_queue_->capacity(), kBufferCount); - // Buffers are only imported, but their availability is not checked until - // first call to Dequeue(). - EXPECT_EQ(consumer_queue_->count(), 0U); - - // Reclaim released/ignored buffers. - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); - - usleep(10000); - WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs); - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); - - // Post another buffer. - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0); - - // Verify that the consumer queue receives it. - size_t consumer_queue_count = consumer_queue_->count(); - WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); - EXPECT_GT(consumer_queue_->count(), consumer_queue_count); - - // Save the current consumer queue buffer count to compare after the dequeue. - consumer_queue_count = consumer_queue_->count(); - - // Dequeue and acquire/release (discard) buffers on the consumer end. - auto consumer_status = - consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(consumer_status.ok()); - auto consumer_buffer = consumer_status.take(); - ASSERT_NE(consumer_buffer, nullptr); - consumer_buffer->Discard(); - - // Buffer should be returned to the producer queue without being handled by - // the silent consumer queue. - EXPECT_LT(consumer_queue_->count(), consumer_queue_count); - EXPECT_EQ(producer_queue_->count(), kBufferCount - 2); - - WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs); - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); -} - -struct TestUserMetadata { - char a; - int32_t b; - int64_t c; -}; - -constexpr uint64_t kUserMetadataSize = - static_cast<uint64_t>(sizeof(TestUserMetadata)); - -TEST_F(BufferHubQueueTest, TestUserMetadata) { - ASSERT_TRUE(CreateQueues( - config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{})); - - AllocateBuffer(); - - std::vector<TestUserMetadata> user_metadata_list = { - {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}}; - - for (auto user_metadata : user_metadata_list) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // TODO(b/69469185): Test against metadata from consumer once we implement - // release metadata properly. - // EXPECT_EQ(mo.user_metadata_ptr, 0U); - // EXPECT_EQ(mo.user_metadata_size, 0U); - - mi.user_metadata_size = kUserMetadataSize; - mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); - EXPECT_EQ(p1->PostAsync(&mi, {}), 0); - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - - EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize); - auto out_user_metadata = - reinterpret_cast<TestUserMetadata*>(mo.user_metadata_ptr); - EXPECT_EQ(user_metadata.a, out_user_metadata->a); - EXPECT_EQ(user_metadata.b, out_user_metadata->b); - EXPECT_EQ(user_metadata.c, out_user_metadata->c); - - // When release, empty metadata is also legit. - mi.user_metadata_size = 0U; - mi.user_metadata_ptr = 0U; - c1->ReleaseAsync(&mi, {}); - } -} - -TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) { - ASSERT_TRUE(CreateQueues( - config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{})); - - AllocateBuffer(); - - TestUserMetadata user_metadata; - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Post with mismatched user metadata size will fail. But the producer buffer - // itself should stay untouched. - mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); - mi.user_metadata_size = kUserMetadataSize + 1; - EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG); - // Post with the exact same user metdata size can success. - mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); - mi.user_metadata_size = kUserMetadataSize; - EXPECT_EQ(p1->PostAsync(&mi, {}), 0); -} - -TEST_F(BufferHubQueueTest, TestEnqueue) { - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(), - UsagePolicy{})); - AllocateBuffer(); - - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(nullptr, p1); - - producer_queue_->Enqueue(p1, slot, 0ULL); - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_FALSE(c1_status.ok()); -} - -TEST_F(BufferHubQueueTest, TestAllocateBuffer) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - size_t ps1; - AllocateBuffer(); - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // producer queue is exhausted - size_t ps2; - auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence); - ASSERT_FALSE(p2_status.ok()); - ASSERT_EQ(ETIMEDOUT, p2_status.error()); - - // dynamically add buffer. - AllocateBuffer(); - ASSERT_EQ(producer_queue_->count(), 1U); - ASSERT_EQ(producer_queue_->capacity(), 2U); - - // now we can dequeue again - p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence); - ASSERT_TRUE(p2_status.ok()); - auto p2 = p2_status.take(); - ASSERT_NE(p2, nullptr); - ASSERT_EQ(producer_queue_->count(), 0U); - // p1 and p2 should have different slot number - ASSERT_NE(ps1, ps2); - - // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers| - // are called. So far consumer_queue_ should be empty. - ASSERT_EQ(consumer_queue_->count(), 0U); - - int64_t seq = 1; - mi.index = seq; - ASSERT_EQ(p1->PostAsync(&mi, {}), 0); - - size_t cs1, cs2; - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence); - ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - ASSERT_EQ(consumer_queue_->count(), 0U); - ASSERT_EQ(consumer_queue_->capacity(), 2U); - ASSERT_EQ(cs1, ps1); - - ASSERT_EQ(p2->PostAsync(&mi, {}), 0); - auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence); - ASSERT_TRUE(c2_status.ok()); - auto c2 = c2_status.take(); - ASSERT_NE(c2, nullptr); - ASSERT_EQ(cs2, ps2); -} - -TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - ASSERT_EQ(producer_queue_->capacity(), 0); - auto status = producer_queue_->AllocateBuffers( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage, /*buffer_count=*/2); - ASSERT_TRUE(status.ok()); - std::vector<size_t> buffer_slots = status.take(); - ASSERT_EQ(buffer_slots.size(), 2); - ASSERT_EQ(producer_queue_->capacity(), 2); -} - -TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - ASSERT_EQ(producer_queue_->capacity(), 0); - auto status = producer_queue_->AllocateBuffers( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage, /*buffer_count=*/0); - ASSERT_TRUE(status.ok()); - std::vector<size_t> buffer_slots = status.take(); - ASSERT_EQ(buffer_slots.size(), 0); - ASSERT_EQ(producer_queue_->capacity(), 0); -} - -TEST_F(BufferHubQueueTest, TestUsageSetMask) { - const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE( - CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0})); - - // When allocation, leave out |set_mask| from usage bits on purpose. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage & ~set_mask); - ASSERT_TRUE(status.ok()); - - LocalHandle fence; - size_t slot; - DvrNativeBufferMetadata mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_EQ(p1->usage() & set_mask, set_mask); -} - -TEST_F(BufferHubQueueTest, TestUsageClearMask) { - const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE( - CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0})); - - // When allocation, add |clear_mask| into usage bits on purpose. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage | clear_mask); - ASSERT_TRUE(status.ok()); - - LocalHandle fence; - size_t slot; - DvrNativeBufferMetadata mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_EQ(p1->usage() & clear_mask, 0U); -} - -TEST_F(BufferHubQueueTest, TestUsageDenySetMask) { - const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(), - UsagePolicy{0, 0, deny_set_mask, 0})); - - // Now that |deny_set_mask| is illegal, allocation without those bits should - // be able to succeed. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage & ~deny_set_mask); - ASSERT_TRUE(status.ok()); - - // While allocation with those bits should fail. - status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, kBufferFormat, - kBufferUsage | deny_set_mask); - ASSERT_FALSE(status.ok()); - ASSERT_EQ(EINVAL, status.error()); -} - -TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) { - const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(), - UsagePolicy{0, 0, 0, deny_clear_mask})); - - // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are - // mandatory), allocation with those bits should be able to succeed. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage | deny_clear_mask); - ASSERT_TRUE(status.ok()); - - // While allocation without those bits should fail. - status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, kBufferFormat, - kBufferUsage & ~deny_clear_mask); - ASSERT_FALSE(status.ok()); - ASSERT_EQ(EINVAL, status.error()); -} - -TEST_F(BufferHubQueueTest, TestQueueInfo) { - static const bool kIsAsync = true; - ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync) - .SetDefaultWidth(kBufferWidth) - .SetDefaultHeight(kBufferHeight) - .SetDefaultFormat(kBufferFormat) - .Build(), - UsagePolicy{})); - - EXPECT_EQ(producer_queue_->default_width(), kBufferWidth); - EXPECT_EQ(producer_queue_->default_height(), kBufferHeight); - EXPECT_EQ(producer_queue_->default_format(), kBufferFormat); - EXPECT_EQ(producer_queue_->is_async(), kIsAsync); - - EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth); - EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight); - EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat); - EXPECT_EQ(consumer_queue_->is_async(), kIsAsync); -} - -TEST_F(BufferHubQueueTest, TestFreeAllBuffers) { - constexpr size_t kBufferCount = 2; - -#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers) \ - EXPECT_EQ(consumer_queue_->count(), 0U); \ - EXPECT_EQ(consumer_queue_->capacity(), 0U); \ - EXPECT_EQ(producer_queue_->count(), 0U); \ - EXPECT_EQ(producer_queue_->capacity(), 0U); \ - for (size_t i = 0; i < num_buffers; i++) { \ - AllocateBuffer(); \ - } \ - EXPECT_EQ(producer_queue_->count(), num_buffers); \ - EXPECT_EQ(producer_queue_->capacity(), num_buffers); - - size_t slot; - LocalHandle fence; - pdx::Status<void> status; - pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status; - pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status; - std::shared_ptr<ConsumerBuffer> consumer_buffer; - std::shared_ptr<ProducerBuffer> producer_buffer; - DvrNativeBufferMetadata mi, mo; - - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Free all buffers when buffers are avaible for dequeue. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when one buffer is dequeued. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when all buffers are dequeued. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - for (size_t i = 0; i < kBufferCount; i++) { - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - } - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when one buffer is posted. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(nullptr, producer_buffer); - ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence)); - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when all buffers are posted. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - for (size_t i = 0; i < kBufferCount; i++) { - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); - } - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when all buffers are acquired. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - for (size_t i = 0; i < kBufferCount; i++) { - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); - consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage(); - } - - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // In addition to FreeAllBuffers() from the queue, it is also required to - // delete all references to the ProducerBuffer (i.e. the PDX client). - producer_buffer = nullptr; - - // Crank consumer queue events to pickup EPOLLHUP events on the queue. - consumer_queue_->HandleQueueEvents(); - - // One last check. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - -#undef CHECK_NO_BUFFER_THEN_ALLOCATE -} - -TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) { - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(), - UsagePolicy{})); - - // Allocate only one buffer. - AllocateBuffer(); - - // Export should fail as the queue is not empty. - auto status = producer_queue_->TakeAsParcelable(); - EXPECT_FALSE(status.ok()); -} - -TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - auto s1 = producer_queue_->TakeAsParcelable(); - EXPECT_TRUE(s1.ok()); - - ProducerQueueParcelable output_parcelable = s1.take(); - EXPECT_TRUE(output_parcelable.IsValid()); - - Parcel parcel; - status_t res; - res = output_parcelable.writeToParcel(&parcel); - EXPECT_EQ(res, OK); - - // After written into parcelable, the output_parcelable is still valid has - // keeps the producer channel alive. - EXPECT_TRUE(output_parcelable.IsValid()); - - // Creating producer buffer should fail. - auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, kBufferFormat, - kBufferUsage); - ASSERT_FALSE(s2.ok()); - - // Reset the data position so that we can read back from the same parcel - // without doing actually Binder IPC. - parcel.setDataPosition(0); - producer_queue_ = nullptr; - - // Recreate the producer queue from the parcel. - ProducerQueueParcelable input_parcelable; - EXPECT_FALSE(input_parcelable.IsValid()); - - res = input_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, OK); - EXPECT_TRUE(input_parcelable.IsValid()); - - EXPECT_EQ(producer_queue_, nullptr); - producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle()); - EXPECT_FALSE(input_parcelable.IsValid()); - ASSERT_NE(producer_queue_, nullptr); - - // Newly created queue from the parcel can allocate buffer, post buffer to - // consumer. - EXPECT_NO_FATAL_FAILURE(AllocateBuffer()); - EXPECT_EQ(producer_queue_->count(), 1U); - EXPECT_EQ(producer_queue_->capacity(), 1U); - - size_t slot; - DvrNativeBufferMetadata producer_meta; - DvrNativeBufferMetadata consumer_meta; - LocalHandle fence; - auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence); - EXPECT_TRUE(s3.ok()); - - std::shared_ptr<ProducerBuffer> p1 = s3.take(); - ASSERT_NE(p1, nullptr); - - producer_meta.timestamp = 42; - EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0); - - // Make sure the buffer can be dequeued from consumer side. - auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); - EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage(); - EXPECT_EQ(consumer_queue_->capacity(), 1U); - - auto consumer = s4.take(); - ASSERT_NE(consumer, nullptr); - EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp); -} - -TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - - auto s1 = producer_queue_->CreateConsumerQueueParcelable(); - EXPECT_TRUE(s1.ok()); - ConsumerQueueParcelable output_parcelable = s1.take(); - EXPECT_TRUE(output_parcelable.IsValid()); - - // Write to a Parcel new object. - Parcel parcel; - status_t res; - res = output_parcelable.writeToParcel(&parcel); - - // Reset the data position so that we can read back from the same parcel - // without doing actually Binder IPC. - parcel.setDataPosition(0); - - // No consumer queue created yet. - EXPECT_EQ(consumer_queue_, nullptr); - - // If the parcel contains a consumer queue, read into a - // ProducerQueueParcelable should fail. - ProducerQueueParcelable wrongly_typed_parcelable; - EXPECT_FALSE(wrongly_typed_parcelable.IsValid()); - res = wrongly_typed_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, -EINVAL); - parcel.setDataPosition(0); - - // Create the consumer queue from the parcel. - ConsumerQueueParcelable input_parcelable; - EXPECT_FALSE(input_parcelable.IsValid()); - - res = input_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, OK); - EXPECT_TRUE(input_parcelable.IsValid()); - - consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle()); - EXPECT_FALSE(input_parcelable.IsValid()); - ASSERT_NE(consumer_queue_, nullptr); - - EXPECT_NO_FATAL_FAILURE(AllocateBuffer()); - EXPECT_EQ(producer_queue_->count(), 1U); - EXPECT_EQ(producer_queue_->capacity(), 1U); - - size_t slot; - DvrNativeBufferMetadata producer_meta; - DvrNativeBufferMetadata consumer_meta; - LocalHandle fence; - auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence); - EXPECT_TRUE(s2.ok()); - - std::shared_ptr<ProducerBuffer> p1 = s2.take(); - ASSERT_NE(p1, nullptr); - - producer_meta.timestamp = 42; - EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0); - - // Make sure the buffer can be dequeued from consumer side. - auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); - EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage(); - EXPECT_EQ(consumer_queue_->capacity(), 1U); - - auto consumer = s3.take(); - ASSERT_NE(consumer, nullptr); - EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp); -} - -} // namespace - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp deleted file mode 100644 index fab10978d6..0000000000 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp +++ /dev/null @@ -1,603 +0,0 @@ -#include <base/logging.h> -#include <gui/BufferHubProducer.h> -#include <gui/IProducerListener.h> -#include <gui/Surface.h> -#include <pdx/default_transport/channel_parcelable.h> - -#include <gtest/gtest.h> - -namespace android { -namespace dvr { - -using pdx::LocalHandle; - -namespace { - -// Default dimensions before setDefaultBufferSize is called by the consumer. -constexpr uint32_t kDefaultWidth = 1; -constexpr uint32_t kDefaultHeight = 1; - -// Default format before setDefaultBufferFormat is called by the consumer. -constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888; -constexpr int kDefaultConsumerUsageBits = 0; - -// Default transform hint before setTransformHint is called by the consumer. -constexpr uint32_t kDefaultTransformHint = 0; - -constexpr int kTestApi = NATIVE_WINDOW_API_CPU; -constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL; -constexpr int kTestApiInvalid = 0xDEADBEEF; -constexpr int kTestProducerUsageBits = 0; -constexpr bool kTestControlledByApp = true; - -// Builder pattern to slightly vary *almost* correct input -// -- avoids copying and pasting -struct QueueBufferInputBuilder { - IGraphicBufferProducer::QueueBufferInput build() { - return IGraphicBufferProducer::QueueBufferInput( - mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode, - mTransform, mFence); - } - - QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { - this->mTimestamp = timestamp; - return *this; - } - - QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) { - this->mIsAutoTimestamp = isAutoTimestamp; - return *this; - } - - QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) { - this->mDataSpace = dataSpace; - return *this; - } - - QueueBufferInputBuilder& setCrop(Rect crop) { - this->mCrop = crop; - return *this; - } - - QueueBufferInputBuilder& setScalingMode(int scalingMode) { - this->mScalingMode = scalingMode; - return *this; - } - - QueueBufferInputBuilder& setTransform(uint32_t transform) { - this->mTransform = transform; - return *this; - } - - QueueBufferInputBuilder& setFence(sp<Fence> fence) { - this->mFence = fence; - return *this; - } - - private: - int64_t mTimestamp{1384888611}; - bool mIsAutoTimestamp{false}; - android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN}; - Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)}; - int mScalingMode{0}; - uint32_t mTransform{0}; - sp<Fence> mFence{Fence::NO_FENCE}; -}; - -// This is a test that covers our implementation of bufferhubqueue-based -// IGraphicBufferProducer. -class BufferHubQueueProducerTest : public ::testing::Test { - protected: - virtual void SetUp() { - const ::testing::TestInfo* const testInfo = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(), - testInfo->name()); - - auto config = ProducerQueueConfigBuilder().Build(); - auto queue = ProducerQueue::Create(config, UsagePolicy{}); - ASSERT_TRUE(queue != nullptr); - - mProducer = BufferHubProducer::Create(std::move(queue)); - ASSERT_TRUE(mProducer != nullptr); - mSurface = new Surface(mProducer, true); - ASSERT_TRUE(mSurface != nullptr); - } - - // Connect to a producer in a 'correct' fashion. - void ConnectProducer() { - IGraphicBufferProducer::QueueBufferOutput output; - // Can connect the first time. - ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi, - kTestControlledByApp, &output)); - } - - // Dequeue a buffer in a 'correct' fashion. - // Precondition: Producer is connected. - void DequeueBuffer(int* outSlot) { - sp<Fence> fence; - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence)); - } - - void DequeueBuffer(int* outSlot, sp<Fence>* outFence) { - ASSERT_NE(nullptr, outSlot); - ASSERT_NE(nullptr, outFence); - - int ret = mProducer->dequeueBuffer( - outSlot, outFence, kDefaultWidth, kDefaultHeight, kDefaultFormat, - kTestProducerUsageBits, nullptr, nullptr); - // BUFFER_NEEDS_REALLOCATION can be either on or off. - ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret); - - // Slot number should be in boundary. - ASSERT_LE(0, *outSlot); - ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot); - } - - // Create a generic "valid" input for queueBuffer - // -- uses the default buffer format, width, etc. - static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() { - return QueueBufferInputBuilder().build(); - } - - const sp<IProducerListener> kStubListener{new StubProducerListener}; - - sp<BufferHubProducer> mProducer; - sp<Surface> mSurface; -}; - -TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) { - IGraphicBufferProducer::QueueBufferOutput output; - - // NULL output returns BAD_VALUE - EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi, - kTestControlledByApp, nullptr)); - - // Invalid API returns bad value - EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid, - kTestControlledByApp, &output)); -} - -TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - // Can't connect when there is already a producer connected. - IGraphicBufferProducer::QueueBufferOutput output; - EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi, - kTestControlledByApp, &output)); -} - -TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); -} - -TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - // Must disconnect with same API number - EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther)); - // API must not be out of range - EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid)); -} - -TEST_F(BufferHubQueueProducerTest, Query_Succeeds) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - int32_t value = -1; - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); - EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value)); - - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); - EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value)); - - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); - EXPECT_EQ(kDefaultFormat, value); - - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); - EXPECT_LE(0, value); - EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value); - - EXPECT_EQ(OK, - mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); - EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue - - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); - EXPECT_EQ(kDefaultConsumerUsageBits, value); -} - -TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - // One past the end of the last 'query' enum value. Update this if we add more - // enums. - const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1; - - int value; - // What was out of range - EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value)); - EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value)); - EXPECT_EQ(BAD_VALUE, - mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value)); - - // Some enums from window.h are 'invalid' - EXPECT_EQ(BAD_VALUE, - mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value)); - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value)); - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value)); - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value)); - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); - - // Value was NULL - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL)); -} - -TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) { - int slot = -1; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - - // Request the buffer (pre-requisite for queueing) - sp<GraphicBuffer> buffer; - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - - // A generic "valid" input - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - - // Queue the buffer back into the BQ - ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - - EXPECT_EQ(kDefaultWidth, output.width); - EXPECT_EQ(kDefaultHeight, output.height); - EXPECT_EQ(kDefaultTransformHint, output.transformHint); - - // BufferHubQueue delivers buffers to consumer immediately. - EXPECT_EQ(0u, output.numPendingBuffers); - - // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to - // be a SurfaceFlinger specific optimization. - EXPECT_EQ(0u, output.nextFrameNumber); - - // Buffer was not in the dequeued state - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); -} - -// Test invalid slot number -TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - // A generic "valid" input - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output)); - EXPECT_EQ(BAD_VALUE, - mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output)); - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS, - input, &output)); -} - -// Slot was not in the dequeued state (all slots start out in Free state) -TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output)); -} - -// Slot was enqueued without requesting a buffer -TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) { - int slot = -1; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); -} - -// Test when fence was NULL -TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) { - int slot = -1; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - - sp<GraphicBuffer> buffer; - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - - sp<Fence> nullFence = NULL; - - IGraphicBufferProducer::QueueBufferInput input = - QueueBufferInputBuilder().setFence(nullFence).build(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); -} - -// Test scaling mode was invalid -TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) { - int slot = -1; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - - sp<GraphicBuffer> buffer; - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - - IGraphicBufferProducer::QueueBufferInput input = - QueueBufferInputBuilder().setScalingMode(-1).build(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); - - input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); -} - -// Test crop rect is out of bounds of the buffer dimensions -TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) { - int slot = -1; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - - sp<GraphicBuffer> buffer; - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - - IGraphicBufferProducer::QueueBufferInput input = - QueueBufferInputBuilder() - .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1)) - .build(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); -} - -TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) { - int slot = -1; - sp<Fence> fence; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); - - // Should be able to cancel buffer after a dequeue. - EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); -} - -TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { - return; - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - int minUndequeuedBuffers; - ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBuffers)); - - const int minBuffers = 1; - const int maxBuffers = - BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; - - ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false; - ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers)) - << "bufferCount: " << minBuffers; - - // Should now be able to dequeue up to minBuffers times - // Should now be able to dequeue up to maxBuffers times - int slot = -1; - for (int i = 0; i < minBuffers; ++i) { - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - } - - ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers)); - - // queue the first buffer to enable max dequeued buffer count checking - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - sp<GraphicBuffer> buffer; - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - - sp<Fence> fence; - for (int i = 0; i < maxBuffers; ++i) { - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); - } - - // Cancel a buffer, so we can decrease the buffer count - ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence)); - - // Should now be able to decrease the max dequeued count by 1 - ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); -} - -TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - - int minUndequeuedBuffers; - ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBuffers)); - - const int minBuffers = 1; - const int maxBuffers = - BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; - - ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false; - // Buffer count was out of range - EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0)) - << "bufferCount: " << 0; - EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1)) - << "bufferCount: " << maxBuffers + 1; - - // Set max dequeue count to 2 - ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2)); - // Dequeue 2 buffers - int slot = -1; - sp<Fence> fence; - for (int i = 0; i < 2; i++) { - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, - kDefaultHeight, kDefaultFormat, - kTestProducerUsageBits, - nullptr, nullptr))) - << "slot: " << slot; - } - - // Client has too many buffers dequeued - EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1)) - << "bufferCount: " << minBuffers; -} - -TEST_F(BufferHubQueueProducerTest, - DisconnectedProducerReturnsError_dequeueBuffer) { - int slot = -1; - sp<Fence> fence; - - ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, - kDefaultHeight, kDefaultFormat, - kTestProducerUsageBits, - nullptr, nullptr)); -} - -TEST_F(BufferHubQueueProducerTest, - DisconnectedProducerReturnsError_requestBuffer) { - int slot = -1; - sp<GraphicBuffer> buffer; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - - // Shouldn't be able to request buffer after disconnect. - ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); - ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer)); -} - -TEST_F(BufferHubQueueProducerTest, - DisconnectedProducerReturnsError_queueBuffer) { - int slot = -1; - sp<GraphicBuffer> buffer; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - - // A generic "valid" input - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - - // Shouldn't be able to queue buffer after disconnect. - ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); - ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output)); -} - -TEST_F(BufferHubQueueProducerTest, - DisconnectedProducerReturnsError_cancelBuffer) { - int slot = -1; - sp<GraphicBuffer> buffer; - - ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - - // Shouldn't be able to cancel buffer after disconnect. - ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); - ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE)); -} - -TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) { - int slot = -1; - sp<GraphicBuffer> buffer; - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; - - EXPECT_NO_FATAL_FAILURE(ConnectProducer()); - - constexpr int maxDequeuedBuffers = 1; - int minUndequeuedBuffers; - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBuffers)); - EXPECT_EQ(OK, mProducer->setAsyncMode(false)); - EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers)); - - int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers; - - // Dequeue, request, and queue all buffers. - for (int i = 0; i < maxCapacity; i++) { - EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - } - - // Disconnect then reconnect. - EXPECT_EQ(OK, mProducer->disconnect(kTestApi)); - EXPECT_NO_FATAL_FAILURE(ConnectProducer()); - - // Dequeue, request, and queue all buffers. - for (int i = 0; i < maxCapacity; i++) { - EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); - EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - } - - EXPECT_EQ(OK, mProducer->disconnect(kTestApi)); -} - -TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { - // Connected producer cannot be taken out as a parcelable. - EXPECT_NO_FATAL_FAILURE(ConnectProducer()); - ProducerQueueParcelable producer_parcelable; - EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE); - - // Create a valid fake producer parcelable. - auto fake_channel_parcelable = - std::make_unique<pdx::default_transport::ChannelParcelable>( - LocalHandle(0), LocalHandle(0), LocalHandle(0)); - EXPECT_TRUE(fake_channel_parcelable->IsValid()); - ProducerQueueParcelable fake_producer_parcelable( - std::move(fake_channel_parcelable)); - EXPECT_TRUE(fake_producer_parcelable.IsValid()); - - // Disconnect producer can be taken out, but only to an invalid parcelable. - ASSERT_EQ(mProducer->disconnect(kTestApi), OK); - EXPECT_EQ(mProducer->TakeAsParcelable(&fake_producer_parcelable), BAD_VALUE); - EXPECT_FALSE(producer_parcelable.IsValid()); - EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK); - EXPECT_TRUE(producer_parcelable.IsValid()); - - // Should still be able to query buffer dimension after disconnect. - int32_t value = -1; - EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); - EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth); - - EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK); - EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight); - - EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK); - EXPECT_EQ(value, kDefaultFormat); - - // But connect to API will fail. - IGraphicBufferProducer::QueueBufferOutput output; - EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp, - &output), - BAD_VALUE); - - // Create a new producer from the parcelable and connect to kTestApi should - // succeed. - sp<BufferHubProducer> new_producer = - BufferHubProducer::Create(std::move(producer_parcelable)); - ASSERT_TRUE(new_producer != nullptr); - EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp, - &output), - OK); -} - -} // namespace - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp deleted file mode 100644 index b0ed950c51..0000000000 --- a/libs/vr/libdisplay/Android.bp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2015 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"], -} - -sourceFiles = [ - "display_client.cpp", - "display_manager_client.cpp", - "display_protocol.cpp", - "shared_buffer_helpers.cpp", - "vsync_service.cpp", -] - -localIncludeFiles = [ - "include", -] - -sharedLibraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "liblog", - "libutils", - "libui", - "libgui", - "libhardware", - "libsync", - "libnativewindow", - "libpdx_default_transport", -] - -staticLibraries = [ - "libdvrcommon", - "libbroadcastring", -] - -headerLibraries = [ - "vulkan_headers", - "libdvr_headers", -] - -cc_library { - srcs: sourceFiles, - cflags: ["-DLOG_TAG=\"libdisplay\"", - "-DTRACE=0", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-DGL_GLEXT_PROTOTYPES", - "-DEGL_EGLEXT_PROTOTYPES", - "-Wall", - "-Werror", - ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ], - export_include_dirs: localIncludeFiles, - shared_libs: sharedLibraries, - static_libs: staticLibraries, - header_libs: headerLibraries, - export_header_lib_headers: headerLibraries, - - name: "libdisplay", -} diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp deleted file mode 100644 index 62856dfbf8..0000000000 --- a/libs/vr/libdisplay/display_client.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include "include/private/dvr/display_client.h" - -#include <cutils/native_handle.h> -#include <log/log.h> -#include <pdx/default_transport/client_channel.h> -#include <pdx/default_transport/client_channel_factory.h> -#include <pdx/status.h> - -#include <mutex> - -#include <private/dvr/display_protocol.h> - -using android::pdx::ErrorStatus; -using android::pdx::LocalHandle; -using android::pdx::LocalChannelHandle; -using android::pdx::Status; -using android::pdx::Transaction; -using android::pdx::rpc::IfAnyOf; - -namespace android { -namespace dvr { -namespace display { - -Surface::Surface(LocalChannelHandle channel_handle, int* error) - : BASE{pdx::default_transport::ClientChannel::Create( - std::move(channel_handle))} { - auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>(); - if (!status) { - ALOGE("Surface::Surface: Failed to get surface info: %s", - status.GetErrorMessage().c_str()); - Close(status.error()); - if (error) - *error = status.error(); - } - - surface_id_ = status.get().surface_id; - z_order_ = status.get().z_order; - visible_ = status.get().visible; -} - -Surface::Surface(const SurfaceAttributes& attributes, int* error) - : BASE{pdx::default_transport::ClientChannelFactory::Create( - DisplayProtocol::kClientPath), - kInfiniteTimeout} { - auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes); - if (!status) { - ALOGE("Surface::Surface: Failed to create display surface: %s", - status.GetErrorMessage().c_str()); - Close(status.error()); - if (error) - *error = status.error(); - } - - surface_id_ = status.get().surface_id; - z_order_ = status.get().z_order; - visible_ = status.get().visible; -} - -Status<void> Surface::SetVisible(bool visible) { - return SetAttributes( - {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}}); -} - -Status<void> Surface::SetZOrder(int z_order) { - return SetAttributes( - {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}}); -} - -Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) { - auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes); - if (!status) { - ALOGE( - "Surface::SetAttributes: Failed to set display surface " - "attributes: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - // Set the local cached copies of the attributes we care about from the full - // set of attributes sent to the display service. - for (const auto& attribute : attributes) { - const auto& key = attribute.first; - const auto* variant = &attribute.second; - bool invalid_value = false; - switch (key) { - case SurfaceAttribute::Visible: - invalid_value = - !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_); - break; - case SurfaceAttribute::ZOrder: - invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_); - break; - } - - if (invalid_value) { - ALOGW( - "Surface::SetAttributes: Failed to set display surface " - "attribute %d because of incompatible type: %d", - key, variant->index()); - } - } - - return {}; -} - -Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue( - uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) { - ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue."); - auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>( - ProducerQueueConfigBuilder() - .SetDefaultWidth(width) - .SetDefaultHeight(height) - .SetDefaultFormat(format) - .SetMetadataSize(metadata_size) - .Build()); - if (!status) { - ALOGE("Surface::CreateQueue: Failed to create queue: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto producer_queue = ProducerQueue::Import(status.take()); - if (!producer_queue) { - ALOGE("Surface::CreateQueue: Failed to import producer queue!"); - return ErrorStatus(ENOMEM); - } - - return {std::move(producer_queue)}; -} - -Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue( - uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t capacity, size_t metadata_size) { - ALOGD_IF(TRACE, - "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u " - "usage=%" PRIx64 " capacity=%zu", - width, height, layer_count, format, usage, capacity); - auto status = CreateQueue(width, height, format, metadata_size); - if (!status) - return status.error_status(); - - auto producer_queue = status.take(); - - ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity); - auto allocate_status = producer_queue->AllocateBuffers( - width, height, layer_count, format, usage, capacity); - if (!allocate_status) { - ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s", - producer_queue->id(), allocate_status.GetErrorMessage().c_str()); - return allocate_status.error_status(); - } - - return {std::move(producer_queue)}; -} - -DisplayClient::DisplayClient(int* error) - : BASE(pdx::default_transport::ClientChannelFactory::Create( - DisplayProtocol::kClientPath), - kInfiniteTimeout) { - if (error) - *error = Client::error(); -} - -Status<Metrics> DisplayClient::GetDisplayMetrics() { - return InvokeRemoteMethod<DisplayProtocol::GetMetrics>(); -} - -Status<std::string> DisplayClient::GetConfigurationData( - ConfigFileType config_type) { - auto status = - InvokeRemoteMethod<DisplayProtocol::GetConfigurationData>(config_type); - if (!status && status.error() != ENOENT) { - ALOGE( - "DisplayClient::GetConfigurationData: Unable to get" - "configuration data. Error: %s", - status.GetErrorMessage().c_str()); - } - return status; -} - -Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() { - return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(); -} - -Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface( - const SurfaceAttributes& attributes) { - int error; - if (auto client = Surface::Create(attributes, &error)) - return {std::move(client)}; - else - return ErrorStatus(error); -} - -pdx::Status<std::unique_ptr<IonBuffer>> DisplayClient::SetupGlobalBuffer( - DvrGlobalBufferKey key, size_t size, uint64_t usage) { - auto status = - InvokeRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(key, size, usage); - if (!status) { - ALOGE( - "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer " - "%s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto ion_buffer = std::make_unique<IonBuffer>(); - auto native_buffer_handle = status.take(); - const int ret = native_buffer_handle.Import(ion_buffer.get()); - if (ret < 0) { - ALOGE( - "DisplayClient::GetGlobalBuffer: Failed to import global buffer: " - "key=%d; error=%s", - key, strerror(-ret)); - return ErrorStatus(-ret); - } - - return {std::move(ion_buffer)}; -} - -pdx::Status<void> DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) { - auto status = InvokeRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(key); - if (!status) { - ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s", - status.GetErrorMessage().c_str()); - } - - return status; -} - -Status<std::unique_ptr<IonBuffer>> DisplayClient::GetGlobalBuffer( - DvrGlobalBufferKey key) { - auto status = InvokeRemoteMethod<DisplayProtocol::GetGlobalBuffer>(key); - if (!status) { - ALOGE( - "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; " - "error=%s", - key, status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto ion_buffer = std::make_unique<IonBuffer>(); - auto native_buffer_handle = status.take(); - const int ret = native_buffer_handle.Import(ion_buffer.get()); - if (ret < 0) { - ALOGE( - "DisplayClient::GetGlobalBuffer: Failed to import global buffer: " - "key=%d; error=%s", - key, strerror(-ret)); - return ErrorStatus(-ret); - } - - return {std::move(ion_buffer)}; -} - -Status<bool> DisplayClient::IsVrAppRunning() { - return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>(); -} - -} // namespace display -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp deleted file mode 100644 index fdeeb70dfb..0000000000 --- a/libs/vr/libdisplay/display_manager_client.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "include/private/dvr/display_manager_client.h" - -#include <pdx/default_transport/client_channel_factory.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/display_protocol.h> -#include <utils/Log.h> - -using android::pdx::ErrorStatus; -using android::pdx::LocalChannelHandle; -using android::pdx::Transaction; - -namespace android { -namespace dvr { -namespace display { - -DisplayManagerClient::DisplayManagerClient() - : BASE(pdx::default_transport::ClientChannelFactory::Create( - DisplayManagerProtocol::kClientPath)) {} - -DisplayManagerClient::~DisplayManagerClient() {} - -pdx::Status<std::vector<display::SurfaceState>> -DisplayManagerClient::GetSurfaceState() { - auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(); - if (!status) { - ALOGE( - "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s", - status.GetErrorMessage().c_str()); - } - - return status; -} - -pdx::Status<std::unique_ptr<ConsumerQueue>> -DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) { - auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>( - surface_id, queue_id); - if (!status) { - ALOGE( - "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for " - "surface_id=%d queue_id=%d: %s", - surface_id, queue_id, status.GetErrorMessage().c_str()); - return status.error_status(); - } - - return {ConsumerQueue::Import(status.take())}; -} - -} // namespace display -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp deleted file mode 100644 index 773f9a5aa3..0000000000 --- a/libs/vr/libdisplay/display_protocol.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "include/private/dvr/display_protocol.h" - -namespace android { -namespace dvr { -namespace display { - -constexpr char DisplayProtocol::kClientPath[]; -constexpr char DisplayManagerProtocol::kClientPath[]; -constexpr char VSyncProtocol::kClientPath[]; - -} // namespace display -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg deleted file mode 100644 index 2f8a3c018c..0000000000 --- a/libs/vr/libdisplay/include/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -filter=-build/header_guard diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h deleted file mode 100644 index 81546ac5c2..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/display_client.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_ -#define ANDROID_DVR_DISPLAY_CLIENT_H_ - -#include <dvr/dvr_api.h> -#include <hardware/hwcomposer.h> -#include <pdx/client.h> -#include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/display_protocol.h> - -namespace android { -namespace dvr { -namespace display { - -class Surface : public pdx::ClientBase<Surface> { - public: - // Utility named constructor. This can be removed once ClientBase::Create is - // refactored to return Status<T> types. - static pdx::Status<std::unique_ptr<Surface>> CreateSurface( - const SurfaceAttributes& attributes) { - int error; - pdx::Status<std::unique_ptr<Surface>> status; - if (auto surface = Create(attributes, &error)) - status.SetValue(std::move(surface)); - else - status.SetError(error); - return status; - } - - int surface_id() const { return surface_id_; } - int z_order() const { return z_order_; } - bool visible() const { return visible_; } - - pdx::Status<void> SetVisible(bool visible); - pdx::Status<void> SetZOrder(int z_order); - pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes); - - // Creates an empty queue. - pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width, - uint32_t height, - uint32_t format, - size_t metadata_size); - - // Creates a queue and populates it with |capacity| buffers of the specified - // parameters. - pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width, - uint32_t height, - uint32_t layer_count, - uint32_t format, - uint64_t usage, - size_t capacity, - size_t metadata_size); - - private: - friend BASE; - - int surface_id_ = -1; - int z_order_ = 0; - bool visible_ = false; - - // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create() - // returns Status<T>. - explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr); - explicit Surface(pdx::LocalChannelHandle channel_handle, - int* error = nullptr); - - Surface(const Surface&) = delete; - void operator=(const Surface&) = delete; -}; - -class DisplayClient : public pdx::ClientBase<DisplayClient> { - public: - pdx::Status<Metrics> GetDisplayMetrics(); - pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type); - pdx::Status<uint8_t> GetDisplayIdentificationPort(); - pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer( - DvrGlobalBufferKey key, size_t size, uint64_t usage); - pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key); - pdx::Status<std::unique_ptr<IonBuffer>> GetGlobalBuffer( - DvrGlobalBufferKey key); - pdx::Status<std::unique_ptr<Surface>> CreateSurface( - const SurfaceAttributes& attributes); - - // Temporary query for current VR status. Will be removed later. - pdx::Status<bool> IsVrAppRunning(); - - private: - friend BASE; - - explicit DisplayClient(int* error = nullptr); - - DisplayClient(const DisplayClient&) = delete; - void operator=(const DisplayClient&) = delete; -}; - -} // namespace display -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DISPLAY_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h deleted file mode 100644 index 45aef51baf..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ -#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ - -#include <string> -#include <vector> - -#include <pdx/client.h> -#include <pdx/status.h> -#include <private/dvr/display_protocol.h> - -namespace android { -namespace dvr { - -class IonBuffer; -class ConsumerQueue; - -namespace display { - -class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> { - public: - ~DisplayManagerClient() override; - - pdx::Status<std::vector<SurfaceState>> GetSurfaceState(); - pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id, - int queue_id); - - using Client::event_fd; - - pdx::Status<int> GetEventMask(int events) { - if (auto* client_channel = GetChannel()) - return client_channel->GetEventMask(events); - else - return pdx::ErrorStatus(EINVAL); - } - - private: - friend BASE; - - DisplayManagerClient(); - - DisplayManagerClient(const DisplayManagerClient&) = delete; - void operator=(const DisplayManagerClient&) = delete; -}; - -} // namespace display -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h deleted file mode 100644 index 9f4cc4afcc..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h +++ /dev/null @@ -1,304 +0,0 @@ -#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_ -#define ANDROID_DVR_DISPLAY_PROTOCOL_H_ - -#include <sys/types.h> - -#include <array> -#include <map> - -#include <dvr/dvr_display_types.h> - -#include <dvr/dvr_api.h> -#include <pdx/rpc/buffer_wrapper.h> -#include <pdx/rpc/remote_method.h> -#include <pdx/rpc/serializable.h> -#include <pdx/rpc/variant.h> -#include <private/dvr/bufferhub_rpc.h> - -// RPC protocol definitions for DVR display services (VrFlinger). - -namespace android { -namespace dvr { -namespace display { - -// Native display metrics. -struct Metrics { - // Basic display properties. - uint32_t display_width; - uint32_t display_height; - uint32_t display_x_dpi; - uint32_t display_y_dpi; - uint32_t vsync_period_ns; - - // HMD metrics. - // TODO(eieio): Determine how these fields should be populated. On phones - // these values are determined at runtime by VrCore based on which headset the - // phone is in. On dedicated hardware this needs to come from somewhere else. - // Perhaps these should be moved to a separate structure that is returned by a - // separate runtime call. - uint32_t distorted_width; - uint32_t distorted_height; - uint32_t hmd_ipd_mm; - float inter_lens_distance_m; - std::array<float, 4> left_fov_lrbt; - std::array<float, 4> right_fov_lrbt; - - private: - PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height, - display_x_dpi, display_y_dpi, vsync_period_ns, - distorted_width, distorted_height, hmd_ipd_mm, - inter_lens_distance_m, left_fov_lrbt, - right_fov_lrbt); -}; - -// Serializable base type for enum structs. Enum structs are easier to use than -// enum classes, especially for bitmasks. This base type provides common -// utilities for flags types. -template <typename Integer> -class Flags { - public: - using Base = Flags<Integer>; - using Type = Integer; - - // NOLINTNEXTLINE(google-explicit-constructor) - Flags(const Integer& value) : value_{value} {} - Flags(const Flags&) = default; - Flags& operator=(const Flags&) = default; - - Integer value() const { return value_; } - // NOLINTNEXTLINE(google-explicit-constructor) - operator Integer() const { return value_; } - - bool IsSet(Integer bits) const { return (value_ & bits) == bits; } - bool IsClear(Integer bits) const { return (value_ & bits) == 0; } - - void Set(Integer bits) { value_ |= bits; } - void Clear(Integer bits) { value_ &= ~bits; } - - Integer operator|(Integer bits) const { return value_ | bits; } - Integer operator&(Integer bits) const { return value_ & bits; } - - Flags& operator|=(Integer bits) { - value_ |= bits; - return *this; - } - Flags& operator&=(Integer bits) { - value_ &= bits; - return *this; - } - - private: - Integer value_; - - PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_); -}; - -// Flags indicating what changed since last update. -struct SurfaceUpdateFlags : public Flags<uint32_t> { - enum : Type { - None = DVR_SURFACE_UPDATE_FLAGS_NONE, - NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE, - BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED, - VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED, - AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED, - }; - - SurfaceUpdateFlags() : Base{None} {} - using Base::Base; -}; - -// Surface attribute key/value types. -using SurfaceAttributeKey = int32_t; -using SurfaceAttributeValue = - pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>, - std::array<float, 3>, std::array<float, 4>, - std::array<float, 8>, std::array<float, 16>>; - -// Defined surface attribute keys. -struct SurfaceAttribute : public Flags<SurfaceAttributeKey> { - enum : Type { - // Keys in the negative integer space are interpreted by VrFlinger for - // direct surfaces. - Direct = DVR_SURFACE_ATTRIBUTE_DIRECT, - ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER, - Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE, - - // Invalid key. May be used to terminate C style lists in public API code. - Invalid = DVR_SURFACE_ATTRIBUTE_INVALID, - - // Positive keys are interpreted by the compositor only. - FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY, - }; - - SurfaceAttribute() : Base{Invalid} {} - using Base::Base; -}; - -// Collection of surface attribute key/value pairs. -using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>; - -struct SurfaceState { - int32_t surface_id; - int32_t process_id; - int32_t user_id; - - SurfaceAttributes surface_attributes; - SurfaceUpdateFlags update_flags; - std::vector<int32_t> queue_ids; - - // Convenience accessors. - bool GetVisible() const { - bool bool_value = false; - GetAttribute(SurfaceAttribute::Visible, &bool_value, - ValidTypes<int32_t, int64_t, bool, float>{}); - return bool_value; - } - - int GetZOrder() const { - int int_value = 0; - GetAttribute(SurfaceAttribute::ZOrder, &int_value, - ValidTypes<int32_t, int64_t, float>{}); - return int_value; - } - - private: - template <typename... Types> - struct ValidTypes {}; - - template <typename ReturnType, typename... Types> - bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value, - ValidTypes<Types...>) const { - auto search = surface_attributes.find(key); - if (search != surface_attributes.end()) - return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value); - else - return false; - } - - PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id, - surface_attributes, update_flags, queue_ids); -}; - -struct SurfaceInfo { - int surface_id; - bool visible; - int z_order; - - private: - PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order); -}; - -enum class ConfigFileType : uint32_t { - kLensMetrics, - kDeviceMetrics, - kDeviceConfiguration, - kDeviceEdid -}; - -struct DisplayProtocol { - // Service path. - static constexpr char kClientPath[] = "system/vr/display/client"; - - // Op codes. - enum { - kOpGetMetrics = 0, - kOpGetConfigurationData, - kOpSetupGlobalBuffer, - kOpDeleteGlobalBuffer, - kOpGetGlobalBuffer, - kOpIsVrAppRunning, - kOpCreateSurface, - kOpGetSurfaceInfo, - kOpCreateQueue, - kOpSetAttributes, - kOpGetDisplayIdentificationPort, - }; - - // Aliases. - using LocalChannelHandle = pdx::LocalChannelHandle; - using Void = pdx::rpc::Void; - - // Methods. - PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void)); - PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData, - std::string(ConfigFileType config_type)); - PDX_REMOTE_METHOD(GetDisplayIdentificationPort, - kOpGetDisplayIdentificationPort, uint8_t(Void)); - PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer, - LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size, - uint64_t usage)); - PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer, - void(DvrGlobalBufferKey key)); - PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer, - LocalNativeBufferHandle(DvrGlobalBufferKey key)); - PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void)); - PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface, - SurfaceInfo(const SurfaceAttributes& attributes)); - PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void)); - PDX_REMOTE_METHOD( - CreateQueue, kOpCreateQueue, - LocalChannelHandle(const ProducerQueueConfig& producer_config)); - PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes, - void(const SurfaceAttributes& attributes)); -}; - -struct DisplayManagerProtocol { - // Service path. - static constexpr char kClientPath[] = "system/vr/display/manager"; - - // Op codes. - enum { - kOpGetSurfaceState = 0, - kOpGetSurfaceQueue, - }; - - // Aliases. - using LocalChannelHandle = pdx::LocalChannelHandle; - using Void = pdx::rpc::Void; - - // Methods. - PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState, - std::vector<SurfaceState>(Void)); - PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue, - LocalChannelHandle(int surface_id, int queue_id)); -}; - -struct VSyncSchedInfo { - int64_t vsync_period_ns; - int64_t timestamp_ns; - uint32_t next_vsync_count; - - private: - PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns, - next_vsync_count); -}; - -struct VSyncProtocol { - // Service path. - static constexpr char kClientPath[] = "system/vr/display/vsync"; - - // Op codes. - enum { - kOpWait = 0, - kOpAck, - kOpGetLastTimestamp, - kOpGetSchedInfo, - kOpAcknowledge, - }; - - // Aliases. - using Void = pdx::rpc::Void; - using Timestamp = int64_t; - - // Methods. - PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void)); - PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void)); - PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void)); - PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void)); -}; - -} // namespace display -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h b/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h deleted file mode 100644 index 20541a69a5..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ -#define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ - -#include <assert.h> -#include <tuple> - -#include <libbroadcastring/broadcast_ring.h> -#include <private/dvr/display_client.h> - -namespace android { -namespace dvr { - -// The buffer usage type for mapped shared buffers. -enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY }; - -// Holds the memory for the mapped shared buffer. Unlocks and releases the -// underlying IonBuffer in destructor. -class CPUMappedBuffer { - public: - // This constructor will create a display client and get the buffer from it. - CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode); - - // If you already have the IonBuffer, use this. It will take ownership. - CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode); - - // Use this if you do not want to take ownership. - CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode); - - ~CPUMappedBuffer(); - - // Getters. - size_t Size() const { return size_; } - void* Address() const { return address_; } - bool IsMapped() const { return Address() != nullptr; } - - // Attempt mapping this buffer to the CPU addressable space. - // This will create a display client and see if the buffer exists. - // If the buffer has not been setup yet, you will need to try again later. - void TryMapping(); - - protected: - // The memory area if we managed to map it. - size_t size_ = 0; - void* address_ = nullptr; - - // If we are polling the display client, the buffer key here. - DvrGlobalBufferKey buffer_key_; - - // If we just own the IonBuffer outright, it's here. - std::unique_ptr<IonBuffer> owned_buffer_ = nullptr; - - // The last time we connected to the display service. - int64_t last_display_service_connection_ns_ = 0; - - // If we do not own the IonBuffer, it's here - IonBuffer* buffer_ = nullptr; - - // The usage mode. - CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN; -}; - -// Represents a broadcast ring inside a mapped shared memory buffer. -// If has the same set of constructors as CPUMappedBuffer. -// The template argument is the concrete BroadcastRing class that this buffer -// holds. -template <class RingType> -class CPUMappedBroadcastRing : public CPUMappedBuffer { - public: - CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode) - : CPUMappedBuffer(key, mode) {} - - CPUMappedBroadcastRing(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode) - : CPUMappedBuffer(std::move(buffer), mode) {} - - CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode) - : CPUMappedBuffer(buffer, mode) {} - - // Helper function for publishing records in the ring. - void Publish(const typename RingType::Record& record) { - assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) || - (usage_mode_ == CPUUsageMode::WRITE_RARELY)); - - auto ring = Ring(); - if (ring) { - ring->Put(record); - } - } - - // Helper function for getting records from the ring. - // Returns true if we were able to retrieve the latest. - bool GetNewest(typename RingType::Record* record) { - assert((usage_mode_ == CPUUsageMode::READ_OFTEN) || - (usage_mode_ == CPUUsageMode::READ_RARELY)); - - auto ring = Ring(); - if (ring) { - return ring->GetNewest(&sequence_, record); - } - - return false; - } - - // Try obtaining the ring. If the named buffer has not been created yet, it - // will return nullptr. - RingType* Ring() { - // No ring created yet? - if (ring_ == nullptr) { - // Not mapped the memory yet? - if (IsMapped() == false) { - TryMapping(); - } - - // If have the memory mapped, allocate the ring. - if (IsMapped()) { - switch (usage_mode_) { - case CPUUsageMode::READ_OFTEN: - case CPUUsageMode::READ_RARELY: { - RingType ring; - bool import_ok; - std::tie(ring, import_ok) = RingType::Import(address_, size_); - if (import_ok) { - ring_ = std::make_unique<RingType>(ring); - } - } break; - case CPUUsageMode::WRITE_OFTEN: - case CPUUsageMode::WRITE_RARELY: - ring_ = - std::make_unique<RingType>(RingType::Create(address_, size_)); - break; - } - } - } - - return ring_.get(); - } - - protected: - std::unique_ptr<RingType> ring_ = nullptr; - - uint32_t sequence_ = 0; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h deleted file mode 100644 index 152464abd1..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/vsync_service.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef ANDROID_DVR_VSYNC_SERVICE_H_ -#define ANDROID_DVR_VSYNC_SERVICE_H_ - -#include <binder/IInterface.h> - -namespace android { -namespace dvr { - -class IVsyncCallback : public IInterface { - public: - DECLARE_META_INTERFACE(VsyncCallback) - - enum { - ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION - }; - - virtual status_t onVsync(int64_t vsync_timestamp) = 0; -}; - -class BnVsyncCallback : public BnInterface<IVsyncCallback> { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -// Register a callback with IVsyncService to be notified of vsync events and -// timestamps. There's also a shared memory vsync buffer defined in -// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared -// memory buffer that make it preferable in certain situations: -// -// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService -// is always available as long as surface flinger is running. -// -// 2. IVsyncService will make a binder callback when a vsync event occurs. This -// allows the client to not write code to implement periodic "get the latest -// vsync" calls, which is necessary with the vsync shared memory buffer. -// -// 3. The IVsyncService provides the real vsync timestamp reported by hardware -// composer, whereas the vsync shared memory buffer only has predicted vsync -// times. -class IVsyncService : public IInterface { -public: - DECLARE_META_INTERFACE(VsyncService) - - static const char* GetServiceName() { return "vrflinger_vsync"; } - - enum { - REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, - UNREGISTER_CALLBACK - }; - - virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0; - virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0; -}; - -class BnVsyncService : public BnInterface<IVsyncService> { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_VSYNC_SERVICE_H_ diff --git a/libs/vr/libdisplay/shared_buffer_helpers.cpp b/libs/vr/libdisplay/shared_buffer_helpers.cpp deleted file mode 100644 index 6ebf487d16..0000000000 --- a/libs/vr/libdisplay/shared_buffer_helpers.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include <private/dvr/clock_ns.h> -#include <private/dvr/shared_buffer_helpers.h> - -namespace android { -namespace dvr { -namespace { - -// We will not poll the display service for buffers more frequently than this. -constexpr size_t kDisplayServiceTriesPerSecond = 2; -} // namespace - -CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode) - : buffer_key_(key), usage_mode_(mode) { - TryMapping(); -} - -CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, - CPUUsageMode mode) - : owned_buffer_(std::move(buffer)), - buffer_(owned_buffer_.get()), - usage_mode_(mode) { - TryMapping(); -} - -CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode) - : buffer_(buffer), usage_mode_(mode) { - TryMapping(); -} - -CPUMappedBuffer::~CPUMappedBuffer() { - if (IsMapped()) { - buffer_->Unlock(); - } -} - -void CPUMappedBuffer::TryMapping() { - // Do we have an IonBuffer for this shared memory object? - if (buffer_ == nullptr) { - // Has it been too long since we last connected to the display service? - const auto current_time_ns = GetSystemClockNs(); - if ((current_time_ns - last_display_service_connection_ns_) < - (1e9 / kDisplayServiceTriesPerSecond)) { - // Early exit. - return; - } - last_display_service_connection_ns_ = current_time_ns; - - // Create a display client and get the buffer. - auto display_client = display::DisplayClient::Create(); - if (display_client) { - auto get_result = display_client->GetGlobalBuffer(buffer_key_); - if (get_result.ok()) { - owned_buffer_ = get_result.take(); - buffer_ = owned_buffer_.get(); - } else { - // The buffer has not been created yet. This is OK, we will keep - // retrying. - } - } else { - ALOGE("Unable to create display client for shared buffer access"); - } - } - - if (buffer_) { - auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK & - ~GRALLOC_USAGE_SW_WRITE_MASK; - - // Figure out the usage bits. - switch (usage_mode_) { - case CPUUsageMode::READ_OFTEN: - usage |= GRALLOC_USAGE_SW_READ_OFTEN; - break; - case CPUUsageMode::READ_RARELY: - usage |= GRALLOC_USAGE_SW_READ_RARELY; - break; - case CPUUsageMode::WRITE_OFTEN: - usage |= GRALLOC_USAGE_SW_WRITE_OFTEN; - break; - case CPUUsageMode::WRITE_RARELY: - usage |= GRALLOC_USAGE_SW_WRITE_RARELY; - break; - } - - int width = static_cast<int>(buffer_->width()); - int height = 1; - const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_); - - if (ret < 0 || !address_) { - ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_); - buffer_->Unlock(); - } else { - size_ = width; - } - } -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg deleted file mode 100644 index 2f8a3c018c..0000000000 --- a/libs/vr/libdisplay/system/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -filter=-build/header_guard diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp deleted file mode 100644 index 04d4f30140..0000000000 --- a/libs/vr/libdisplay/vsync_service.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "include/private/dvr/vsync_service.h" - -#include <binder/Parcel.h> -#include <log/log.h> - -namespace android { -namespace dvr { - -status_t BnVsyncCallback::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch (code) { - case ON_VSYNC: { - CHECK_INTERFACE(IVsyncCallback, data, reply); - int64_t vsync_timestamp = 0; - status_t result = data.readInt64(&vsync_timestamp); - if (result != OK) { - ALOGE("onVsync failed to readInt64: %d", result); - return result; - } - onVsync(vsync_timestamp); - return OK; - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -class BpVsyncCallback : public BpInterface<IVsyncCallback> { -public: - explicit BpVsyncCallback(const sp<IBinder>& impl) - : BpInterface<IVsyncCallback>(impl) {} - virtual ~BpVsyncCallback() {} - - virtual status_t onVsync(int64_t vsync_timestamp) { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IVsyncCallback::getInterfaceDescriptor()); - if (result != OK) { - ALOGE("onVsync failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeInt64(vsync_timestamp); - if (result != OK) { - ALOGE("onVsync failed to writeInt64: %d", result); - return result; - } - result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply, - IBinder::FLAG_ONEWAY); - if (result != OK) { - ALOGE("onVsync failed to transact: %d", result); - return result; - } - return result; - } -}; - -IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback"); - - -status_t BnVsyncService::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch (code) { - case REGISTER_CALLBACK: { - CHECK_INTERFACE(IVsyncService, data, reply); - sp<IBinder> callback; - status_t result = data.readStrongBinder(&callback); - if (result != OK) { - ALOGE("registerCallback failed to readStrongBinder: %d", result); - return result; - } - registerCallback(interface_cast<IVsyncCallback>(callback)); - return OK; - } - case UNREGISTER_CALLBACK: { - CHECK_INTERFACE(IVsyncService, data, reply); - sp<IBinder> callback; - status_t result = data.readStrongBinder(&callback); - if (result != OK) { - ALOGE("unregisterCallback failed to readStrongBinder: %d", result); - return result; - } - unregisterCallback(interface_cast<IVsyncCallback>(callback)); - return OK; - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -class BpVsyncService : public BpInterface<IVsyncService> { -public: - explicit BpVsyncService(const sp<IBinder>& impl) - : BpInterface<IVsyncService>(impl) {} - virtual ~BpVsyncService() {} - - virtual status_t registerCallback(const sp<IVsyncCallback> callback) { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IVsyncService::getInterfaceDescriptor()); - if (result != OK) { - ALOGE("registerCallback failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(IInterface::asBinder(callback)); - if (result != OK) { - ALOGE("registerCallback failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact( - BnVsyncService::REGISTER_CALLBACK, data, &reply); - if (result != OK) { - ALOGE("registerCallback failed to transact: %d", result); - return result; - } - return result; - } - - virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IVsyncService::getInterfaceDescriptor()); - if (result != OK) { - ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(IInterface::asBinder(callback)); - if (result != OK) { - ALOGE("unregisterCallback failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact( - BnVsyncService::UNREGISTER_CALLBACK, data, &reply); - if (result != OK) { - ALOGE("unregisterCallback failed to transact: %d", result); - return result; - } - return result; - } -}; - -IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService"); - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index 96023ddf86..9dbeacba94 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -33,87 +33,3 @@ cc_library_headers { ], min_sdk_version: "29", } - -cc_library_headers { - name: "libdvr_private_headers", - export_include_dirs: ["."], - vendor_available: false, -} - -cflags = [ - "-DDVR_TRACKING_IMPLEMENTED=0", - "-DLOG_TAG=\"libdvr\"", - "-DTRACE=0", - "-Wall", - "-Werror", -] - -srcs = [ - "dvr_api.cpp", - "dvr_buffer.cpp", - "dvr_buffer_queue.cpp", - "dvr_configuration_data.cpp", - "dvr_display_manager.cpp", - "dvr_hardware_composer_client.cpp", - "dvr_performance.cpp", - "dvr_pose.cpp", - "dvr_surface.cpp", - "dvr_tracking.cpp", -] - -static_libs = [ - "libbroadcastring", - "libvrsensor", - "libdisplay", - "libvirtualtouchpadclient", - "libvr_hwc-impl", - "libvr_hwc-binder", - "libgrallocusage", - "libperformance", -] - -shared_libs = [ - "android.hardware.graphics.bufferqueue@1.0", - "android.hidl.token@1.0-utils", - "libbase", - "libbufferhubqueue", - "libbinder", - "liblog", - "libcutils", - "libutils", - "libnativewindow", - "libgui", - "libui", - "libpdx_default_transport", -] - -cc_library_shared { - name: "libdvr.google", - system_ext_specific: true, - owner: "google", - cflags: cflags, - header_libs: ["libdvr_headers"], - export_header_lib_headers: ["libdvr_headers"], - srcs: srcs, - static_libs: static_libs, - shared_libs: shared_libs, - version_script: "exported_apis.lds", -} - -// Also build a static libdvr for linking into tests. The linker script -// restricting function access in the shared lib makes it inconvenient to use in -// test code. -cc_library_static { - name: "libdvr_static.google", - owner: "google", - cflags: cflags, - header_libs: ["libdvr_headers"], - export_header_lib_headers: ["libdvr_headers"], - srcs: srcs, - static_libs: static_libs, - shared_libs: shared_libs, -} - -subdirs = [ - "tests", -] diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp deleted file mode 100644 index e099f6a699..0000000000 --- a/libs/vr/libdvr/dvr_api.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "include/dvr/dvr_api.h" - -#include <errno.h> -#include <utils/Log.h> - -#include <algorithm> - -// Headers from libdvr -#include <dvr/dvr_buffer.h> -#include <dvr/dvr_buffer_queue.h> -#include <dvr/dvr_configuration_data.h> -#include <dvr/dvr_display_manager.h> -#include <dvr/dvr_performance.h> -#include <dvr/dvr_surface.h> -#include <dvr/dvr_tracking.h> -#include <dvr/dvr_vsync.h> - -// Headers not yet moved into libdvr. -// TODO(jwcai) Move these once their callers are moved into Google3. -#include <dvr/dvr_hardware_composer_client.h> -#include <dvr/pose_client.h> -#include <dvr/virtual_touchpad_client.h> - -extern "C" { - -int dvrGetApi(void* api, size_t struct_size, int version) { - ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size, - version); - if (version == 1) { - // New entry points are added at the end. If the caller's struct and - // this library have different sizes, we define the entry points in common. - // The caller is expected to handle unset entry points if necessary. - size_t clamped_struct_size = std::min(struct_size, sizeof(DvrApi_v1)); - DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api); - -// Defines an API entry for V1 (no version suffix). -#define DVR_V1_API_ENTRY(name) \ - do { \ - if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \ - clamped_struct_size) { \ - dvr_api->name = dvr##name; \ - } \ - } while (0) - -#define DVR_V1_API_ENTRY_DEPRECATED(name) \ - do { \ - if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \ - clamped_struct_size) { \ - dvr_api->name = nullptr; \ - } \ - } while (0) - -#include "include/dvr/dvr_api_entries.h" - -// Undefine macro definitions to play nice with Google3 style rules. -#undef DVR_V1_API_ENTRY -#undef DVR_V1_API_ENTRY_DEPRECATED - - return 0; - } - - ALOGE("dvrGetApi: Unknown API version=%d", version); - return -EINVAL; -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp deleted file mode 100644 index c11706fc7f..0000000000 --- a/libs/vr/libdvr/dvr_buffer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "include/dvr/dvr_buffer.h" - -#include <android/hardware_buffer.h> -#include <dvr/dvr_shared_buffers.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/producer_buffer.h> -#include <ui/GraphicBuffer.h> - -#include "dvr_internal.h" - -using namespace android; - -namespace android { -namespace dvr { - -DvrBuffer* CreateDvrBufferFromIonBuffer( - const std::shared_ptr<IonBuffer>& ion_buffer) { - if (!ion_buffer) - return nullptr; - return new DvrBuffer{std::move(ion_buffer)}; -} - -} // namespace dvr -} // namespace android - -namespace { - -int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer, - AHardwareBuffer** hardware_buffer) { - if (!hardware_buffer || !graphic_buffer) { - return -EINVAL; - } - *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer); - AHardwareBuffer_acquire(*hardware_buffer); - return 0; -} - -} // anonymous namespace - -extern "C" { - -void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) { - if (write_buffer != nullptr) { - ALOGW_IF( - write_buffer->slot != -1, - "dvrWriteBufferDestroy: Destroying a buffer associated with a valid " - "buffer queue slot. This may indicate possible leaks, buffer_id=%d.", - dvrWriteBufferGetId(write_buffer)); - delete write_buffer; - } -} - -int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) { - return write_buffer && write_buffer->write_buffer; -} - -int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) { - if (!write_buffer || !write_buffer->write_buffer) - return -EINVAL; - - return write_buffer->write_buffer->id(); -} - -int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer, - AHardwareBuffer** hardware_buffer) { - if (!write_buffer || !write_buffer->write_buffer) - return -EINVAL; - - return ConvertToAHardwareBuffer( - write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer); -} - -void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { - if (read_buffer != nullptr) { - ALOGW_IF( - read_buffer->slot != -1, - "dvrReadBufferDestroy: Destroying a buffer associated with a valid " - "buffer queue slot. This may indicate possible leaks, buffer_id=%d.", - dvrReadBufferGetId(read_buffer)); - delete read_buffer; - } -} - -int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) { - return read_buffer && read_buffer->read_buffer; -} - -int dvrReadBufferGetId(DvrReadBuffer* read_buffer) { - if (!read_buffer || !read_buffer->read_buffer) - return -EINVAL; - - return read_buffer->read_buffer->id(); -} - -int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer, - AHardwareBuffer** hardware_buffer) { - if (!read_buffer || !read_buffer->read_buffer) - return -EINVAL; - - return ConvertToAHardwareBuffer( - read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer); -} - -void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; } - -int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer, - AHardwareBuffer** hardware_buffer) { - if (!buffer || !buffer->buffer || !hardware_buffer) { - return -EINVAL; - } - - return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(), - hardware_buffer); -} - -// Retrieve the shared buffer layout version defined in dvr_shared_buffers.h. -int dvrBufferGlobalLayoutVersionGet() { - return android::dvr::kSharedBufferLayoutVersion; -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp deleted file mode 100644 index 1ca653c4ff..0000000000 --- a/libs/vr/libdvr/dvr_buffer_queue.cpp +++ /dev/null @@ -1,552 +0,0 @@ -#include "include/dvr/dvr_api.h" -#include "include/dvr/dvr_buffer_queue.h" - -#include <android/native_window.h> -#include <gui/BufferHubProducer.h> - -#include "dvr_internal.h" -#include "dvr_buffer_queue_internal.h" - -using namespace android; -using android::dvr::BufferHubBase; -using android::dvr::ConsumerBuffer; -using android::dvr::ConsumerQueue; -using android::dvr::ProducerBuffer; -using android::dvr::ProducerQueue; -using android::dvr::ProducerQueueConfigBuilder; -using android::dvr::UsagePolicy; - -extern "C" { - -DvrWriteBufferQueue::DvrWriteBufferQueue( - const std::shared_ptr<ProducerQueue>& producer_queue) - : producer_queue_(producer_queue), - width_(producer_queue->default_width()), - height_(producer_queue->default_height()), - format_(producer_queue->default_format()) {} - -int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) { - if (native_window_ == nullptr) { - // Lazy creation of |native_window|, as not everyone is using - // DvrWriteBufferQueue as an external surface. - sp<IGraphicBufferProducer> gbp = BufferHubProducer::Create(producer_queue_); - native_window_ = new Surface(gbp, true); - } - - *out_window = static_cast<ANativeWindow*>(native_window_.get()); - return 0; -} - -int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { - std::unique_ptr<ConsumerQueue> consumer_queue = - producer_queue_->CreateConsumerQueue(); - if (consumer_queue == nullptr) { - ALOGE( - "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue " - "from producer queue: queue_id=%d.", producer_queue_->id()); - return -ENOMEM; - } - - *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue)); - return 0; -} - -int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, - int* out_fence_fd) { - DvrNativeBufferMetadata meta; - DvrWriteBuffer* buffer = nullptr; - int fence_fd = -1; - if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd)) - return ret; - if (!buffer) - return -ENOMEM; - - write_buffers_[buffer->slot].reset(buffer); - write_buffer->write_buffer = std::move(buffer->write_buffer); - *out_fence_fd = fence_fd; - return 0; -} - -int DvrWriteBufferQueue::GainBuffer(int timeout, - DvrWriteBuffer** out_write_buffer, - DvrNativeBufferMetadata* out_meta, - int* out_fence_fd) { - size_t slot; - pdx::LocalHandle release_fence; - - // Need to retry N+1 times, where N is total number of buffers in the queue. - // As in the worst case, we will dequeue all N buffers and reallocate them, on - // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension. - size_t max_retries = 1 + producer_queue_->capacity(); - size_t retry = 0; - - for (; retry < max_retries; retry++) { - auto buffer_status = - producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence); - if (!buffer_status) { - ALOGE_IF(buffer_status.error() != ETIMEDOUT, - "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s", - buffer_status.GetErrorMessage().c_str()); - return -buffer_status.error(); - } - - if (write_buffers_[slot] == nullptr) { - // Lazy initialization of a write_buffers_ slot. Note that a slot will - // only be dynamically allocated once during the entire cycle life of a - // queue. - write_buffers_[slot] = std::make_unique<DvrWriteBuffer>(); - write_buffers_[slot]->slot = slot; - } - - LOG_ALWAYS_FATAL_IF( - write_buffers_[slot]->write_buffer, - "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot); - write_buffers_[slot]->write_buffer = std::move(buffer_status.take()); - - const auto& producer_buffer = write_buffers_[slot]->write_buffer; - if (!producer_buffer) - return -ENOMEM; - - if (width_ == producer_buffer->width() && - height_ == producer_buffer->height() && - format_ == producer_buffer->format()) { - // Producer queue returns a buffer matches the current request. - break; - } - - // Needs reallocation. Note that if there are already multiple available - // buffers in the queue, the next one returned from |queue_->Dequeue| may - // still have the old buffer dimension or format. Retry up to N+1 times or - // until we dequeued a buffer with new configuration. - ALOGD_IF(TRACE, - "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu " - "(w=%u, h=%u, fmt=%u) is different from the buffer returned " - "(w=%u, h=%u, fmt=%u). Need re-allocation.", - slot, width_, height_, format_, producer_buffer->width(), - producer_buffer->height(), producer_buffer->format()); - - // Currently, we are not storing |layer_count| and |usage| in queue - // configuration. Copy those setup from the last buffer dequeued before we - // remove it. - uint32_t old_layer_count = producer_buffer->layer_count(); - uint64_t old_usage = producer_buffer->usage(); - - // Allocate a new producer buffer with new buffer configs. Note that if - // there are already multiple available buffers in the queue, the next one - // returned from |queue_->Dequeue| may still have the old buffer dimension - // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until - // we dequeued a buffer with new configuration. - auto remove_status = producer_queue_->RemoveBuffer(slot); - if (!remove_status) { - ALOGE("DvrWriteBufferQueue::Dequeue: Failed to remove buffer: %s", - remove_status.GetErrorMessage().c_str()); - return -remove_status.error(); - } - // Make sure that the previously allocated buffer is dereferenced from - // write_buffers_ array. - write_buffers_[slot]->write_buffer = nullptr; - - auto allocate_status = producer_queue_->AllocateBuffer( - width_, height_, old_layer_count, format_, old_usage); - if (!allocate_status) { - ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s", - allocate_status.GetErrorMessage().c_str()); - return -allocate_status.error(); - } - } - - if (retry >= max_retries) { - ALOGE( - "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after " - "resizing."); - return -ENOMEM; - } - - *out_write_buffer = write_buffers_[slot].release(); - *out_fence_fd = release_fence.Release(); - - return 0; -} - -int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer, - const DvrNativeBufferMetadata* meta, - int ready_fence_fd) { - // Some basic sanity checks before we put the buffer back into a slot. - size_t slot = static_cast<size_t>(write_buffer->slot); - LOG_FATAL_IF( - (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()), - "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); - - if (write_buffers_[slot] != nullptr) { - ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot); - return -EINVAL; - } - if (write_buffer->write_buffer == nullptr) { - ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer."); - return -EINVAL; - } - if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) { - ALOGE( - "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not " - "belong to this buffer queue. Posting buffer: id=%d, buffer in " - "queue: id=%d", - write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot)); - return -EINVAL; - } - - write_buffer->write_buffer->SetQueueIndex(next_post_index_++); - pdx::LocalHandle fence(ready_fence_fd); - const int ret = write_buffer->write_buffer->PostAsync(meta, fence); - if (ret < 0) { - ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d", - ret); - return ret; - } - - // Put the DvrWriteBuffer pointer back into its slot for reuse. - write_buffers_[slot].reset(write_buffer); - // It's import to reset the write buffer client now. It should stay invalid - // until next GainBuffer on the same slot. - write_buffers_[slot]->write_buffer = nullptr; - return 0; -} - -int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) { - if (width == 0 || height == 0) { - ALOGE( - "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, " - "h=%u.", - width, height); - return -EINVAL; - } - - width_ = width; - height_ = height; - return 0; -} - -int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format, - uint32_t layer_count, uint64_t usage, - size_t capacity, size_t metadata_size, - DvrWriteBufferQueue** out_write_queue) { - if (!out_write_queue) - return -EINVAL; - - auto config_builder = ProducerQueueConfigBuilder() - .SetDefaultWidth(width) - .SetDefaultHeight(height) - .SetDefaultFormat(format) - .SetMetadataSize(metadata_size); - std::unique_ptr<ProducerQueue> producer_queue = - ProducerQueue::Create(config_builder.Build(), UsagePolicy{}); - if (!producer_queue) { - ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue."); - return -ENOMEM; - } - - auto status = producer_queue->AllocateBuffers(width, height, layer_count, - format, usage, capacity); - if (!status.ok()) { - ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers."); - return -ENOMEM; - } - - *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue)); - return 0; -} - -void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) { - delete write_queue; -} - -ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) { - if (!write_queue) - return -EINVAL; - - return write_queue->capacity(); -} - -int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) { - if (!write_queue) - return -EINVAL; - - return write_queue->id(); -} - -int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue, - ANativeWindow** out_window) { - if (!write_queue || !out_window) - return -EINVAL; - - return write_queue->GetNativeWindow(out_window); -} - -int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, - DvrReadBufferQueue** out_read_queue) { - if (!write_queue || !out_read_queue) - return -EINVAL; - - return write_queue->CreateReadQueue(out_read_queue); -} - -int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout, - DvrWriteBuffer** out_write_buffer, - DvrNativeBufferMetadata* out_meta, - int* out_fence_fd) { - if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd) - return -EINVAL; - - return write_queue->GainBuffer(timeout, out_write_buffer, out_meta, - out_fence_fd); -} - -int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue, - DvrWriteBuffer* write_buffer, - const DvrNativeBufferMetadata* meta, - int ready_fence_fd) { - if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta) - return -EINVAL; - - return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd); -} - -int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue, - uint32_t width, uint32_t height) { - if (!write_queue) - return -EINVAL; - - return write_queue->ResizeBuffer(width, height); -} - -// ReadBufferQueue - -DvrReadBufferQueue::DvrReadBufferQueue( - const std::shared_ptr<ConsumerQueue>& consumer_queue) - : consumer_queue_(consumer_queue) {} - -int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { - std::unique_ptr<ConsumerQueue> consumer_queue = - consumer_queue_->CreateConsumerQueue(); - if (consumer_queue == nullptr) { - ALOGE( - "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue " - "from producer queue: queue_id=%d.", consumer_queue_->id()); - return -ENOMEM; - } - - *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue)); - return 0; -} - -int DvrReadBufferQueue::AcquireBuffer(int timeout, - DvrReadBuffer** out_read_buffer, - DvrNativeBufferMetadata* out_meta, - int* out_fence_fd) { - size_t slot; - pdx::LocalHandle acquire_fence; - auto buffer_status = - consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence); - if (!buffer_status) { - ALOGE_IF(buffer_status.error() != ETIMEDOUT, - "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s", - buffer_status.GetErrorMessage().c_str()); - return -buffer_status.error(); - } - - if (read_buffers_[slot] == nullptr) { - // Lazy initialization of a read_buffers_ slot. Note that a slot will only - // be dynamically allocated once during the entire cycle life of a queue. - read_buffers_[slot] = std::make_unique<DvrReadBuffer>(); - read_buffers_[slot]->slot = slot; - } - - LOG_FATAL_IF( - read_buffers_[slot]->read_buffer, - "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot); - read_buffers_[slot]->read_buffer = std::move(buffer_status.take()); - - *out_read_buffer = read_buffers_[slot].release(); - *out_fence_fd = acquire_fence.Release(); - - return 0; -} - -int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer, - const DvrNativeBufferMetadata* meta, - int release_fence_fd) { - // Some basic sanity checks before we put the buffer back into a slot. - size_t slot = static_cast<size_t>(read_buffer->slot); - LOG_FATAL_IF( - (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()), - "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); - - if (read_buffers_[slot] != nullptr) { - ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot); - return -EINVAL; - } - if (read_buffer->read_buffer == nullptr) { - ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer."); - return -EINVAL; - } - if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) { - if (consumer_queue_->GetBufferId(slot) > 0) { - ALOGE( - "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not " - "belong to this queue (queue_id=%d): attempting to release buffer " - "(buffer_id=%d) at slot %d which holds a different buffer " - "(buffer_id=%d).", - consumer_queue_->id(), read_buffer->read_buffer->id(), - static_cast<int>(slot), consumer_queue_->GetBufferId(slot)); - } else { - ALOGI( - "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not " - "belong to this queue (queue_id=%d): attempting to release buffer " - "(buffer_id=%d) at slot %d which is empty.", - consumer_queue_->id(), read_buffer->read_buffer->id(), - static_cast<int>(slot)); - } - } - - pdx::LocalHandle fence(release_fence_fd); - int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence); - if (ret < 0) { - ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d", - ret); - return ret; - } - - // Put the DvrReadBuffer pointer back into its slot for reuse. - read_buffers_[slot].reset(read_buffer); - // It's import to reset the read buffer client now. It should stay invalid - // until next AcquireBuffer on the same slot. - read_buffers_[slot]->read_buffer = nullptr; - return 0; -} - -void DvrReadBufferQueue::SetBufferAvailableCallback( - DvrReadBufferQueueBufferAvailableCallback callback, void* context) { - if (callback == nullptr) { - consumer_queue_->SetBufferAvailableCallback(nullptr); - } else { - consumer_queue_->SetBufferAvailableCallback( - [callback, context]() { callback(context); }); - } -} - -void DvrReadBufferQueue::SetBufferRemovedCallback( - DvrReadBufferQueueBufferRemovedCallback callback, void* context) { - if (callback == nullptr) { - consumer_queue_->SetBufferRemovedCallback(nullptr); - } else { - consumer_queue_->SetBufferRemovedCallback( - [callback, context](const std::shared_ptr<BufferHubBase>& buffer) { - // When buffer is removed from the queue, the slot is already invalid. - auto read_buffer = std::make_unique<DvrReadBuffer>(); - read_buffer->read_buffer = - std::static_pointer_cast<ConsumerBuffer>(buffer); - callback(read_buffer.release(), context); - }); - } -} - -int DvrReadBufferQueue::HandleEvents() { - // TODO(jwcai) Probably should change HandleQueueEvents to return Status. - consumer_queue_->HandleQueueEvents(); - return 0; -} - -void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) { - delete read_queue; -} - -ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) { - if (!read_queue) - return -EINVAL; - - return read_queue->capacity(); -} - -int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) { - if (!read_queue) - return -EINVAL; - - return read_queue->id(); -} - -int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue) { - if (!read_queue) - return -EINVAL; - - return read_queue->event_fd(); -} - -int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, - DvrReadBufferQueue** out_read_queue) { - if (!read_queue || !out_read_queue) - return -EINVAL; - - return read_queue->CreateReadQueue(out_read_queue); -} - -int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, - DvrReadBuffer* read_buffer, int* out_fence_fd, - void* out_meta, size_t meta_size_bytes) { - if (!read_queue || !read_buffer || !out_fence_fd) - return -EINVAL; - - if (meta_size_bytes != 0 && !out_meta) - return -EINVAL; - - return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta, - meta_size_bytes); -} - -int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout, - DvrReadBuffer** out_read_buffer, - DvrNativeBufferMetadata* out_meta, - int* out_fence_fd) { - if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd) - return -EINVAL; - - return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta, - out_fence_fd); -} - -int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue, - DvrReadBuffer* read_buffer, - const DvrNativeBufferMetadata* meta, - int release_fence_fd) { - if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta) - return -EINVAL; - - return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd); -} - -int dvrReadBufferQueueSetBufferAvailableCallback( - DvrReadBufferQueue* read_queue, - DvrReadBufferQueueBufferAvailableCallback callback, void* context) { - if (!read_queue) - return -EINVAL; - - read_queue->SetBufferAvailableCallback(callback, context); - return 0; -} - -int dvrReadBufferQueueSetBufferRemovedCallback( - DvrReadBufferQueue* read_queue, - DvrReadBufferQueueBufferRemovedCallback callback, void* context) { - if (!read_queue) - return -EINVAL; - - read_queue->SetBufferRemovedCallback(callback, context); - return 0; -} - -int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue) { - if (!read_queue) - return -EINVAL; - - return read_queue->HandleEvents(); -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_buffer_queue_internal.h b/libs/vr/libdvr/dvr_buffer_queue_internal.h deleted file mode 100644 index e53a6868ff..0000000000 --- a/libs/vr/libdvr/dvr_buffer_queue_internal.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ -#define ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ - -#include <gui/Surface.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <sys/cdefs.h> - -#include <array> -#include <memory> - -#include "dvr_internal.h" - -struct ANativeWindow; - -typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata; -typedef struct DvrReadBuffer DvrReadBuffer; -typedef struct DvrReadBufferQueue DvrReadBufferQueue; -typedef struct DvrWriteBuffer DvrWriteBuffer; -typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context); -typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer, - void* context); - -struct DvrWriteBufferQueue { - using BufferHubQueue = android::dvr::BufferHubQueue; - using ProducerQueue = android::dvr::ProducerQueue; - - // Create a concrete object for DvrWriteBufferQueue. - // - // @param producer_queue The BufferHub's ProducerQueue that is used to back - // this DvrWriteBufferQueue, must not be NULL. - explicit DvrWriteBufferQueue( - const std::shared_ptr<ProducerQueue>& producer_queue); - - int id() const { return producer_queue_->id(); } - uint32_t width() const { return width_; }; - uint32_t height() const { return height_; }; - uint32_t format() const { return format_; }; - size_t capacity() const { return producer_queue_->capacity(); } - const std::shared_ptr<ProducerQueue>& producer_queue() const { - return producer_queue_; - } - - int GetNativeWindow(ANativeWindow** out_window); - int CreateReadQueue(DvrReadBufferQueue** out_read_queue); - int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd); - int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer, - DvrNativeBufferMetadata* out_meta, int* out_fence_fd); - int PostBuffer(DvrWriteBuffer* write_buffer, - const DvrNativeBufferMetadata* meta, int ready_fence_fd); - int ResizeBuffer(uint32_t width, uint32_t height); - - private: - std::shared_ptr<ProducerQueue> producer_queue_; - std::array<std::unique_ptr<DvrWriteBuffer>, BufferHubQueue::kMaxQueueCapacity> - write_buffers_; - - int64_t next_post_index_ = 0; - uint32_t width_; - uint32_t height_; - uint32_t format_; - - android::sp<android::Surface> native_window_; -}; - -struct DvrReadBufferQueue { - using BufferHubQueue = android::dvr::BufferHubQueue; - using ConsumerQueue = android::dvr::ConsumerQueue; - - explicit DvrReadBufferQueue( - const std::shared_ptr<ConsumerQueue>& consumer_queue); - - int id() const { return consumer_queue_->id(); } - int event_fd() const { return consumer_queue_->queue_fd(); } - size_t capacity() const { return consumer_queue_->capacity(); } - - int CreateReadQueue(DvrReadBufferQueue** out_read_queue); - int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd, - void* out_meta, size_t user_metadata_size); - int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer, - DvrNativeBufferMetadata* out_meta, int* out_fence_fd); - int ReleaseBuffer(DvrReadBuffer* read_buffer, - const DvrNativeBufferMetadata* meta, int release_fence_fd); - void SetBufferAvailableCallback( - DvrReadBufferQueueBufferAvailableCallback callback, void* context); - void SetBufferRemovedCallback( - DvrReadBufferQueueBufferRemovedCallback callback, void* context); - int HandleEvents(); - - private: - std::shared_ptr<ConsumerQueue> consumer_queue_; - std::array<std::unique_ptr<DvrReadBuffer>, BufferHubQueue::kMaxQueueCapacity> - read_buffers_; -}; - -#endif // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ diff --git a/libs/vr/libdvr/dvr_configuration_data.cpp b/libs/vr/libdvr/dvr_configuration_data.cpp deleted file mode 100644 index df0d54e1a2..0000000000 --- a/libs/vr/libdvr/dvr_configuration_data.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "include/dvr/dvr_configuration_data.h" - -#include <private/dvr/display_client.h> - -using android::dvr::display::ConfigFileType; -using android::dvr::display::DisplayClient; - -extern "C" { - -int dvrConfigurationDataGet(int config_type, uint8_t** data, - size_t* data_size) { - if (!data || !data_size) { - return -EINVAL; - } - - auto client = DisplayClient::Create(); - if (!client) { - ALOGE("dvrGetGlobalBuffer: Failed to create display client!"); - return -ECOMM; - } - - ConfigFileType config_file_type = static_cast<ConfigFileType>(config_type); - auto config_data_status = - client->GetConfigurationData(config_file_type); - - if (!config_data_status) { - return -config_data_status.error(); - } - - *data_size = config_data_status.get().size(); - *data = new uint8_t[*data_size]; - std::copy_n(config_data_status.get().begin(), *data_size, *data); - return 0; -} - -void dvrConfigurationDataDestroy(uint8_t* data) { - delete[] data; -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp deleted file mode 100644 index 7f631e3d39..0000000000 --- a/libs/vr/libdvr/dvr_display_manager.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "include/dvr/dvr_display_manager.h" - -#include <dvr/dvr_buffer.h> -#include <pdx/rpc/variant.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/display_client.h> -#include <private/dvr/display_manager_client.h> - -#include "dvr_internal.h" -#include "dvr_buffer_queue_internal.h" - -using android::dvr::ConsumerBuffer; -using android::dvr::display::DisplayManagerClient; -using android::dvr::display::SurfaceAttribute; -using android::dvr::display::SurfaceAttributes; -using android::dvr::display::SurfaceState; -using android::pdx::rpc::EmptyVariant; - -namespace { - -// Extracts type and value from the attribute Variant and writes them into the -// respective fields of DvrSurfaceAttribute. -struct AttributeVisitor { - DvrSurfaceAttribute* attribute; - - void operator()(int32_t value) { - attribute->value.int32_value = value; - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32; - } - void operator()(int64_t value) { - attribute->value.int64_value = value; - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64; - } - void operator()(bool value) { - attribute->value.bool_value = value; - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL; - } - void operator()(float value) { - attribute->value.float_value = value; - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT; - } - void operator()(const std::array<float, 2>& value) { - std::copy(value.cbegin(), value.cend(), attribute->value.float2_value); - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2; - } - void operator()(const std::array<float, 3>& value) { - std::copy(value.cbegin(), value.cend(), attribute->value.float3_value); - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3; - } - void operator()(const std::array<float, 4>& value) { - std::copy(value.cbegin(), value.cend(), attribute->value.float4_value); - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4; - } - void operator()(const std::array<float, 8>& value) { - std::copy(value.cbegin(), value.cend(), attribute->value.float8_value); - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8; - } - void operator()(const std::array<float, 16>& value) { - std::copy(value.cbegin(), value.cend(), attribute->value.float16_value); - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16; - } - void operator()(EmptyVariant) { - attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE; - } -}; - -size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes, - DvrSurfaceAttribute* attributes, - size_t max_count) { - size_t count = 0; - for (const auto& attribute : surface_attributes) { - if (count >= max_count) - break; - - // Copy the key and extract the Variant value using a visitor. - attributes[count].key = attribute.first; - attribute.second.Visit(AttributeVisitor{&attributes[count]}); - count++; - } - - return count; -} - -} // anonymous namespace - -extern "C" { - -struct DvrDisplayManager { - std::unique_ptr<DisplayManagerClient> client; -}; - -struct DvrSurfaceState { - std::vector<SurfaceState> state; -}; - -int dvrDisplayManagerCreate(DvrDisplayManager** client_out) { - if (!client_out) - return -EINVAL; - - auto client = DisplayManagerClient::Create(); - if (!client) { - ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!"); - return -EIO; - } - - *client_out = new DvrDisplayManager{std::move(client)}; - return 0; -} - -void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; } - -int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) { - if (!client) - return -EINVAL; - - return client->client->event_fd(); -} - -int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client, - int in_events, int* out_events) { - if (!client || !out_events) - return -EINVAL; - - auto status = client->client->GetEventMask(in_events); - if (!status) - return -status.error(); - - *out_events = status.get(); - return 0; -} - -int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client, - DvrSurfaceState* state) { - if (!client || !state) - return -EINVAL; - - auto status = client->client->GetSurfaceState(); - if (!status) - return -status.error(); - - state->state = status.take(); - return 0; -} - -int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client, - int surface_id, int queue_id, - DvrReadBufferQueue** queue_out) { - if (!client || !queue_out) - return -EINVAL; - - auto status = client->client->GetSurfaceQueue(surface_id, queue_id); - if (!status) { - ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - *queue_out = new DvrReadBufferQueue(status.take()); - return 0; -} - -int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) { - if (!surface_state_out) - return -EINVAL; - - *surface_state_out = new DvrSurfaceState{}; - return 0; -} - -void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) { - delete surface_state; -} - -int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state, - size_t* count_out) { - if (!surface_state) - return -EINVAL; - - *count_out = surface_state->state.size(); - return 0; -} - -int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state, - size_t surface_index, - DvrSurfaceUpdateFlags* flags_out) { - if (!surface_state || surface_index >= surface_state->state.size()) - return -EINVAL; - - *flags_out = surface_state->state[surface_index].update_flags; - return 0; -} - -int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state, - size_t surface_index, int* surface_id_out) { - if (!surface_state || surface_index >= surface_state->state.size()) - return -EINVAL; - - *surface_id_out = surface_state->state[surface_index].surface_id; - return 0; -} - -int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state, - size_t surface_index, int* process_id_out) { - if (!surface_state || surface_index >= surface_state->state.size()) - return -EINVAL; - - *process_id_out = surface_state->state[surface_index].process_id; - return 0; -} - -int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state, - size_t surface_index, size_t* count_out) { - if (!surface_state || surface_index >= surface_state->state.size()) - return -EINVAL; - - *count_out = surface_state->state[surface_index].queue_ids.size(); - return 0; -} - -ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state, - size_t surface_index, int* queue_ids, - size_t max_count) { - if (!surface_state || surface_index >= surface_state->state.size()) - return -EINVAL; - - size_t i; - const auto& state = surface_state->state[surface_index]; - for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) { - queue_ids[i] = state.queue_ids[i]; - } - - return i; -} - -int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state, - size_t surface_index, int* z_order_out) { - if (!surface_state || surface_index >= surface_state->state.size() || - !z_order_out) { - return -EINVAL; - } - - *z_order_out = surface_state->state[surface_index].GetZOrder(); - return 0; -} - -int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state, - size_t surface_index, bool* visible_out) { - if (!surface_state || surface_index >= surface_state->state.size() || - !visible_out) { - return -EINVAL; - } - - *visible_out = surface_state->state[surface_index].GetVisible(); - return 0; -} - -int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state, - size_t surface_index, size_t* count_out) { - if (!surface_state || surface_index >= surface_state->state.size() || - !count_out) { - return -EINVAL; - } - - *count_out = surface_state->state[surface_index].surface_attributes.size(); - return 0; -} - -ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state, - size_t surface_index, - DvrSurfaceAttribute* attributes, - size_t max_count) { - if (!surface_state || surface_index >= surface_state->state.size() || - !attributes) { - return -EINVAL; - } - - return ConvertSurfaceAttributes( - surface_state->state[surface_index].surface_attributes, attributes, - max_count); -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp deleted file mode 100644 index 4e87cf6b8f..0000000000 --- a/libs/vr/libdvr/dvr_hardware_composer_client.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include "include/dvr/dvr_hardware_composer_client.h" - -#include <android/dvr/IVrComposer.h> -#include <android/dvr/BnVrComposerCallback.h> -#include <android/hardware_buffer.h> -#include <binder/IServiceManager.h> -#include <private/android/AHardwareBufferHelpers.h> - -#include <functional> -#include <memory> -#include <mutex> - -struct DvrHwcFrame { - android::dvr::ComposerView::Frame frame; -}; - -namespace { - -class HwcCallback : public android::dvr::BnVrComposerCallback { - public: - using CallbackFunction = std::function<int(DvrHwcFrame*)>; - - explicit HwcCallback(const CallbackFunction& callback); - ~HwcCallback() override; - - // Reset the callback. This needs to be done early to avoid use after free - // accesses from binder thread callbacks. - void Shutdown(); - - std::unique_ptr<DvrHwcFrame> DequeueFrame(); - - private: - // android::dvr::BnVrComposerCallback: - android::binder::Status onNewFrame( - const android::dvr::ParcelableComposerFrame& frame, - android::dvr::ParcelableUniqueFd* fence) override; - - // Protects the |callback_| from uses from multiple threads. During shutdown - // there may be in-flight frame update events. In those cases the callback - // access needs to be protected otherwise binder threads may access an invalid - // callback. - std::mutex mutex_; - CallbackFunction callback_; - - HwcCallback(const HwcCallback&) = delete; - void operator=(const HwcCallback&) = delete; -}; - -HwcCallback::HwcCallback(const CallbackFunction& callback) - : callback_(callback) {} - -HwcCallback::~HwcCallback() {} - -void HwcCallback::Shutdown() { - std::lock_guard<std::mutex> guard(mutex_); - callback_ = nullptr; -} - -android::binder::Status HwcCallback::onNewFrame( - const android::dvr::ParcelableComposerFrame& frame, - android::dvr::ParcelableUniqueFd* fence) { - std::lock_guard<std::mutex> guard(mutex_); - - if (!callback_) { - fence->set_fence(android::base::unique_fd()); - return android::binder::Status::ok(); - } - - std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame()); - dvr_frame->frame = frame.frame(); - - fence->set_fence(android::base::unique_fd(callback_(dvr_frame.release()))); - return android::binder::Status::ok(); -} - -} // namespace - -struct DvrHwcClient { - android::sp<android::dvr::IVrComposer> composer; - android::sp<HwcCallback> callback; -}; - -DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) { - std::unique_ptr<DvrHwcClient> client(new DvrHwcClient()); - - android::sp<android::IServiceManager> sm(android::defaultServiceManager()); - client->composer = android::interface_cast<android::dvr::IVrComposer>( - sm->getService(android::dvr::IVrComposer::SERVICE_NAME())); - if (!client->composer.get()) - return nullptr; - - client->callback = new HwcCallback(std::bind(callback, data, - std::placeholders::_1)); - android::binder::Status status = client->composer->registerObserver( - client->callback); - if (!status.isOk()) - return nullptr; - - return client.release(); -} - -void dvrHwcClientDestroy(DvrHwcClient* client) { - client->composer->clearObserver(); - - // NOTE: Deleting DvrHwcClient* isn't enough since DvrHwcClient::callback is a - // shared pointer that could be referenced from a binder thread. But the - // client callback isn't valid past this calls so that needs to be reset. - client->callback->Shutdown(); - - delete client; -} - -void dvrHwcFrameDestroy(DvrHwcFrame* frame) { - delete frame; -} - -DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) { - return frame->frame.display_id; -} - -int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) { - return frame->frame.display_width; -} - -int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) { - return frame->frame.display_height; -} - -bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) { - return frame->frame.removed; -} - -size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) { - return frame->frame.layers.size(); -} - -uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) { - return static_cast<uint32_t>(frame->frame.active_config); -} - -uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) { - return static_cast<uint32_t>(frame->frame.color_mode); -} - -void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix, - int32_t* out_hint) { - *out_hint = frame->frame.color_transform_hint; - memcpy(out_matrix, frame->frame.color_transform, - sizeof(frame->frame.color_transform)); -} - -uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) { - return static_cast<uint32_t>(frame->frame.power_mode); -} - -uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) { - return static_cast<uint32_t>(frame->frame.vsync_enabled); -} - -DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].id; -} - -AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame, - size_t layer_index) { - AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer( - frame->frame.layers[layer_index].buffer.get()); - AHardwareBuffer_acquire(buffer); - return buffer; -} - -int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].fence->dup(); -} - -DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame, - size_t layer_index) { - return DvrHwcRecti{ - frame->frame.layers[layer_index].display_frame.left, - frame->frame.layers[layer_index].display_frame.top, - frame->frame.layers[layer_index].display_frame.right, - frame->frame.layers[layer_index].display_frame.bottom, - }; -} - -DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) { - return DvrHwcRectf{ - frame->frame.layers[layer_index].crop.left, - frame->frame.layers[layer_index].crop.top, - frame->frame.layers[layer_index].crop.right, - frame->frame.layers[layer_index].crop.bottom, - }; -} - -DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame, - size_t layer_index) { - return static_cast<DvrHwcBlendMode>( - frame->frame.layers[layer_index].blend_mode); -} - -float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].alpha; -} - -uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].type; -} - -uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame, - size_t layer_index) { - return frame->frame.layers[layer_index].app_id; -} - -uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].z_order; -} - -void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index, - int32_t* out_x, int32_t* out_y) { - *out_x = frame->frame.layers[layer_index].cursor_x; - *out_y = frame->frame.layers[layer_index].cursor_y; -} - -uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].transform; -} - -uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) { - return frame->frame.layers[layer_index].dataspace; -} - -uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) { - const auto& color = frame->frame.layers[layer_index].color; - return color.r | (static_cast<uint32_t>(color.g) << 8) | - (static_cast<uint32_t>(color.b) << 16) | - (static_cast<uint32_t>(color.a) << 24); -} - -uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame, - size_t layer_index) { - return frame->frame.layers[layer_index].visible_regions.size(); -} - -DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame, - size_t layer_index, size_t index) { - return DvrHwcRecti{ - frame->frame.layers[layer_index].visible_regions[index].left, - frame->frame.layers[layer_index].visible_regions[index].top, - frame->frame.layers[layer_index].visible_regions[index].right, - frame->frame.layers[layer_index].visible_regions[index].bottom, - }; -} - -uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame, - size_t layer_index) { - return frame->frame.layers[layer_index].damaged_regions.size(); -} - -DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame, - size_t layer_index, size_t index) { - return DvrHwcRecti{ - frame->frame.layers[layer_index].damaged_regions[index].left, - frame->frame.layers[layer_index].damaged_regions[index].top, - frame->frame.layers[layer_index].damaged_regions[index].right, - frame->frame.layers[layer_index].damaged_regions[index].bottom, - }; -} diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h deleted file mode 100644 index f845cd896d..0000000000 --- a/libs/vr/libdvr/dvr_internal.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef ANDROID_DVR_INTERNAL_H_ -#define ANDROID_DVR_INTERNAL_H_ - -#include <sys/cdefs.h> - -#include <memory> - -extern "C" { - -typedef struct DvrBuffer DvrBuffer; -typedef struct DvrReadBuffer DvrReadBuffer; -typedef struct DvrWriteBuffer DvrWriteBuffer; - -} // extern "C" - -namespace android { -namespace dvr { - -class IonBuffer; - -DvrBuffer* CreateDvrBufferFromIonBuffer( - const std::shared_ptr<IonBuffer>& ion_buffer); - -} // namespace dvr -} // namespace android - -extern "C" { - -struct DvrWriteBuffer { - // The slot nubmer of the buffer, a valid slot number must be in the range of - // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for - // DvrWriteBuffer acquired from a DvrWriteBufferQueue. - int32_t slot = -1; - - std::shared_ptr<android::dvr::ProducerBuffer> write_buffer; -}; - -struct DvrReadBuffer { - // The slot nubmer of the buffer, a valid slot number must be in the range of - // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for - // DvrReadBuffer acquired from a DvrReadBufferQueue. - int32_t slot = -1; - - std::shared_ptr<android::dvr::ConsumerBuffer> read_buffer; -}; - -struct DvrBuffer { - std::shared_ptr<android::dvr::IonBuffer> buffer; -}; - -} // extern "C" - -#endif // ANDROID_DVR_INTERNAL_H_ diff --git a/libs/vr/libdvr/dvr_performance.cpp b/libs/vr/libdvr/dvr_performance.cpp deleted file mode 100644 index 599101fea3..0000000000 --- a/libs/vr/libdvr/dvr_performance.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "include/dvr/dvr_performance.h" - -#include <private/dvr/performance_client.h> - -using android::dvr::PerformanceClient; - -extern "C" { - -int dvrPerformanceSetSchedulerPolicy(pid_t task_id, - const char* scheduler_policy) { - int error; - if (auto client = PerformanceClient::Create(&error)) - return client->SetSchedulerPolicy(task_id, scheduler_policy); - else - return error; -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_pose.cpp b/libs/vr/libdvr/dvr_pose.cpp deleted file mode 100644 index c379ef55e8..0000000000 --- a/libs/vr/libdvr/dvr_pose.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "include/dvr/dvr_pose.h" - -#include <memory> - -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/pose_client_internal.h> - -#include "dvr_buffer_queue_internal.h" - -using android::dvr::ConsumerQueue; - -int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type, - DvrReadBufferQueue** queue_out) { - if (!client || !queue_out) - return -EINVAL; - - ConsumerQueue* consumer_queue; - int status = android::dvr::dvrPoseClientGetDataReaderHandle(client, - data_type, - &consumer_queue); - if (status != 0) { - ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status); - return status; - } - - std::shared_ptr<ConsumerQueue> consumer_queue_ptr{consumer_queue}; - *queue_out = new DvrReadBufferQueue(consumer_queue_ptr); - return 0; -} diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp deleted file mode 100644 index 0c7ec01640..0000000000 --- a/libs/vr/libdvr/dvr_surface.cpp +++ /dev/null @@ -1,277 +0,0 @@ -#include "include/dvr/dvr_surface.h" - -#include <inttypes.h> - -#include <pdx/rpc/variant.h> -#include <private/android/AHardwareBufferHelpers.h> -#include <private/dvr/display_client.h> - -#include "dvr_buffer_queue_internal.h" -#include "dvr_internal.h" - -using android::AHardwareBuffer_convertToGrallocUsageBits; -using android::dvr::display::DisplayClient; -using android::dvr::display::Surface; -using android::dvr::display::SurfaceAttributes; -using android::dvr::display::SurfaceAttributeValue; -using android::pdx::rpc::EmptyVariant; - -namespace { - -// Sets the Variant |destination| to the target std::array type and copies the C -// array into it. Unsupported std::array configurations will fail to compile. -template <typename T, std::size_t N> -void ArrayCopy(SurfaceAttributeValue* destination, const T (&source)[N]) { - using ArrayType = std::array<T, N>; - *destination = ArrayType{}; - std::copy(std::begin(source), std::end(source), - std::get<ArrayType>(*destination).begin()); -} - -bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes, - size_t attribute_count, - SurfaceAttributes* surface_attributes, - size_t* error_index) { - for (size_t i = 0; i < attribute_count; i++) { - SurfaceAttributeValue value; - switch (attributes[i].value.type) { - case DVR_SURFACE_ATTRIBUTE_TYPE_INT32: - value = attributes[i].value.int32_value; - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_INT64: - value = attributes[i].value.int64_value; - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL: - // bool_value is defined in an extern "C" block, which makes it look - // like an int to C++. Use a cast to assign the correct type to the - // Variant type SurfaceAttributeValue. - value = static_cast<bool>(attributes[i].value.bool_value); - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT: - value = attributes[i].value.float_value; - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2: - ArrayCopy(&value, attributes[i].value.float2_value); - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3: - ArrayCopy(&value, attributes[i].value.float3_value); - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4: - ArrayCopy(&value, attributes[i].value.float4_value); - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8: - ArrayCopy(&value, attributes[i].value.float8_value); - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16: - ArrayCopy(&value, attributes[i].value.float16_value); - break; - case DVR_SURFACE_ATTRIBUTE_TYPE_NONE: - value = EmptyVariant{}; - break; - default: - *error_index = i; - return false; - } - - surface_attributes->emplace(attributes[i].key, value); - } - - return true; -} - -} // anonymous namespace - -extern "C" { - -struct DvrSurface { - std::unique_ptr<Surface> surface; -}; - -int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes, - size_t attribute_count, DvrSurface** out_surface) { - if (out_surface == nullptr) { - ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface); - return -EINVAL; - } - - size_t error_index; - SurfaceAttributes surface_attributes; - if (!ConvertSurfaceAttributes(attributes, attribute_count, - &surface_attributes, &error_index)) { - ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64, - attributes[error_index].value.type); - return -EINVAL; - } - - auto status = Surface::CreateSurface(surface_attributes); - if (!status) { - ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - *out_surface = new DvrSurface{status.take()}; - return 0; -} - -void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; } - -int dvrSurfaceGetId(DvrSurface* surface) { - return surface->surface->surface_id(); -} - -int dvrSurfaceSetAttributes(DvrSurface* surface, - const DvrSurfaceAttribute* attributes, - size_t attribute_count) { - if (surface == nullptr || attributes == nullptr) { - ALOGE( - "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p " - "attribute_count=%zu", - surface, attributes, attribute_count); - return -EINVAL; - } - - size_t error_index; - SurfaceAttributes surface_attributes; - if (!ConvertSurfaceAttributes(attributes, attribute_count, - &surface_attributes, &error_index)) { - ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64, - attributes[error_index].value.type); - return -EINVAL; - } - - auto status = surface->surface->SetAttributes(surface_attributes); - if (!status) { - ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - return 0; -} - -int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width, - uint32_t height, uint32_t format, - uint32_t layer_count, uint64_t usage, - size_t capacity, size_t metadata_size, - DvrWriteBufferQueue** out_writer) { - if (surface == nullptr || out_writer == nullptr) { - ALOGE( - "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, " - "out_writer=%p.", - surface, out_writer); - return -EINVAL; - } - - auto status = surface->surface->CreateQueue( - width, height, layer_count, format, usage, capacity, metadata_size); - if (!status) { - ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - *out_writer = new DvrWriteBufferQueue(status.take()); - return 0; -} - -int dvrSetupGlobalBuffer(DvrGlobalBufferKey key, size_t size, uint64_t usage, - DvrBuffer** buffer_out) { - if (!buffer_out) - return -EINVAL; - - int error; - auto client = DisplayClient::Create(&error); - if (!client) { - ALOGE("dvrSetupGlobalBuffer: Failed to create display client: %s", - strerror(-error)); - return error; - } - - uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage); - - auto buffer_status = client->SetupGlobalBuffer(key, size, gralloc_usage); - if (!buffer_status) { - ALOGE("dvrSetupGlobalBuffer: Failed to setup global buffer: %s", - buffer_status.GetErrorMessage().c_str()); - return -buffer_status.error(); - } - - *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take()); - return 0; -} - -int dvrDeleteGlobalBuffer(DvrGlobalBufferKey key) { - int error; - auto client = DisplayClient::Create(&error); - if (!client) { - ALOGE("dvrDeleteGlobalBuffer: Failed to create display client: %s", - strerror(-error)); - return error; - } - - auto buffer_status = client->DeleteGlobalBuffer(key); - if (!buffer_status) { - ALOGE("dvrDeleteGlobalBuffer: Failed to delete named buffer: %s", - buffer_status.GetErrorMessage().c_str()); - return -buffer_status.error(); - } - - return 0; -} - -int dvrGetGlobalBuffer(DvrGlobalBufferKey key, DvrBuffer** out_buffer) { - if (!out_buffer) - return -EINVAL; - - int error; - auto client = DisplayClient::Create(&error); - if (!client) { - ALOGE("dvrGetGlobalBuffer: Failed to create display client: %s", - strerror(-error)); - return error; - } - - auto status = client->GetGlobalBuffer(key); - if (!status) { - return -status.error(); - } - *out_buffer = CreateDvrBufferFromIonBuffer(status.take()); - return 0; -} - -int dvrGetNativeDisplayMetrics(size_t sizeof_metrics, - DvrNativeDisplayMetrics* metrics) { - ALOGE_IF(sizeof_metrics != sizeof(DvrNativeDisplayMetrics), - "dvrGetNativeDisplayMetrics: metrics struct mismatch, your dvr api " - "header is out of date."); - - auto client = DisplayClient::Create(); - if (!client) { - ALOGE("dvrGetNativeDisplayMetrics: Failed to create display client!"); - return -ECOMM; - } - - if (metrics == nullptr) { - ALOGE("dvrGetNativeDisplayMetrics: output metrics buffer must be non-null"); - return -EINVAL; - } - - auto status = client->GetDisplayMetrics(); - - if (!status) { - return -status.error(); - } - - if (sizeof_metrics >= 20) { - metrics->display_width = status.get().display_width; - metrics->display_height = status.get().display_height; - metrics->display_x_dpi = status.get().display_x_dpi; - metrics->display_y_dpi = status.get().display_y_dpi; - metrics->vsync_period_ns = status.get().vsync_period_ns; - } - - return 0; -} - -} // extern "C" diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp deleted file mode 100644 index 73addc9a0c..0000000000 --- a/libs/vr/libdvr/dvr_tracking.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "include/dvr/dvr_tracking.h" - -#include <utils/Errors.h> -#include <utils/Log.h> - -#if !DVR_TRACKING_IMPLEMENTED - -extern "C" { - -// This file provides the stub implementation of dvrTrackingXXX APIs. On -// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the -// build file. -int dvrTrackingCameraCreate(DvrTrackingCamera**) { - ALOGE("dvrTrackingCameraCreate is not implemented."); - return -ENOSYS; -} - -void dvrTrackingCameraDestroy(DvrTrackingCamera*) { - ALOGE("dvrTrackingCameraDestroy is not implemented."); -} - -int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) { - ALOGE("dvrTrackingCameraCreate is not implemented."); - return -ENOSYS; -} - -int dvrTrackingCameraStop(DvrTrackingCamera*) { - ALOGE("dvrTrackingCameraCreate is not implemented."); - return -ENOSYS; -} - -int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) { - ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); - return -ENOSYS; -} - -void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) { - ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented."); -} - -int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*, - DvrTrackingFeatureCallback, void*) { - ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); - return -ENOSYS; -} - -int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) { - ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); - return -ENOSYS; -} - -int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*, - DvrReadBuffer*, - const DvrTrackingBufferMetadata*, - bool*) { - ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented."); - return -ENOSYS; -} - -int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) { - ALOGE("dvrTrackingSensorsCreate is not implemented."); - return -ENOSYS; -} - -void dvrTrackingSensorsDestroy(DvrTrackingSensors*) { - ALOGE("dvrTrackingSensorsDestroy is not implemented."); -} - -int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback, - void*) { - ALOGE("dvrTrackingStart is not implemented."); - return -ENOSYS; -} - -int dvrTrackingSensorsStop(DvrTrackingSensors*) { - ALOGE("dvrTrackingStop is not implemented."); - return -ENOSYS; -} - -} // extern "C" - -#endif // DVR_TRACKING_IMPLEMENTED diff --git a/libs/vr/libdvr/exported_apis.lds b/libs/vr/libdvr/exported_apis.lds deleted file mode 100644 index 5ecb49861c..0000000000 --- a/libs/vr/libdvr/exported_apis.lds +++ /dev/null @@ -1,9 +0,0 @@ -{ - global: - # Whitelist the function to load the dvr api. - dvrGetApi; - - local: - # Hide everything else. - *; -}; diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp deleted file mode 100644 index fe7feb824d..0000000000 --- a/libs/vr/libdvr/tests/Android.bp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2017 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_test { - srcs: [ - "dvr_display_manager-test.cpp", - "dvr_named_buffer-test.cpp", - "dvr_tracking-test.cpp", - ], - - header_libs: ["libdvr_headers"], - static_libs: [ - "libdvr_static.google", - "libchrome", - "libdvrcommon", - "libdisplay", - "libbroadcastring", - ], - shared_libs: [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libpdx_default_transport", - ], - cflags: [ - "-DDVR_TRACKING_IMPLEMENTED=0", - "-DLOG_TAG=\"dvr_api-test\"", - "-DTRACE=0", - "-Wno-missing-field-initializers", - "-O0", - "-g", - ], - name: "dvr_api-test", -} - -cc_test { - name: "dvr_buffer_queue-test", - - // Includes the dvr_api.h header. Tests should only include "dvr_api.h", - // and shall only get access to |dvrGetApi|, as other symbols are hidden - // from the library. - include_dirs: ["frameworks/native/libs/vr/libdvr/include"], - - srcs: ["dvr_buffer_queue-test.cpp"], - - shared_libs: [ - "libandroid", - "liblog", - ], - - cflags: [ - "-DTRACE=0", - "-O2", - "-g", - ], - - // DTS Should only link to NDK libraries. - sdk_version: "26", - stl: "c++_static", -} - -cc_test { - name: "dvr_display-test", - - include_dirs: [ - "frameworks/native/libs/vr/libdvr/include", - "frameworks/native/libs/nativewindow/include", - ], - - srcs: ["dvr_display-test.cpp"], - - shared_libs: [ - "libandroid", - "liblog", - ], - - cflags: [ - "-DTRACE=0", - "-O2", - "-g", - ], - - // DTS Should only link to NDK libraries. - sdk_version: "26", - stl: "c++_static", -} diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h deleted file mode 100644 index 5d2ec285eb..0000000000 --- a/libs/vr/libdvr/tests/dvr_api_test.h +++ /dev/null @@ -1,36 +0,0 @@ -#include <dlfcn.h> -#include <dvr/dvr_api.h> - -#include <gtest/gtest.h> - -/** DvrTestBase loads the libdvr.so at runtime and get the Dvr API version 1. */ -class DvrApiTest : public ::testing::Test { - protected: - void SetUp() override { - int flags = RTLD_NOW | RTLD_LOCAL; - - // Here we need to ensure that libdvr is loaded with RTLD_NODELETE flag set - // (so that calls to `dlclose` don't actually unload the library). This is a - // workaround for an Android NDK bug. See more detail: - // https://github.com/android-ndk/ndk/issues/360 - flags |= RTLD_NODELETE; - platform_handle_ = dlopen("libdvr.google.so", flags); - ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing."; - - auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>( - dlsym(platform_handle_, "dvrGetApi")); - ASSERT_NE(nullptr, dvr_get_api) << "Platform library missing dvrGetApi."; - - ASSERT_EQ(dvr_get_api(&api_, sizeof(api_), /*version=*/1), 0) - << "Unable to find compatible Dvr API."; - } - - void TearDown() override { - if (platform_handle_ != nullptr) { - dlclose(platform_handle_); - } - } - - void* platform_handle_ = nullptr; - DvrApi_v1 api_; -}; diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp deleted file mode 100644 index df060973ec..0000000000 --- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp +++ /dev/null @@ -1,579 +0,0 @@ -#include <android/log.h> -#include <android/native_window.h> -#include <dvr/dvr_api.h> -#include <dvr/dvr_buffer_queue.h> - -#include <gtest/gtest.h> - -#include <array> -#include <unordered_map> - -#include "dvr_api_test.h" - -#define LOG_TAG "dvr_buffer_queue-test" - -#ifndef ALOGD -#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#endif - -#ifndef ALOGD_IF -#define ALOGD_IF(cond, ...) \ - ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0) -#endif - -namespace { - -static constexpr uint32_t kBufferWidth = 100; -static constexpr uint32_t kBufferHeight = 1; -static constexpr uint32_t kLayerCount = 1; -static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB; -static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; -static constexpr size_t kQueueCapacity = 3; - -class DvrBufferQueueTest : public DvrApiTest { - public: - static void BufferAvailableCallback(void* context) { - DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context); - thiz->HandleBufferAvailable(); - } - - static void BufferRemovedCallback(DvrReadBuffer* buffer, void* context) { - DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context); - thiz->HandleBufferRemoved(buffer); - } - - protected: - void TearDown() override { - if (write_queue_ != nullptr) { - api_.WriteBufferQueueDestroy(write_queue_); - write_queue_ = nullptr; - } - DvrApiTest::TearDown(); - } - - void HandleBufferAvailable() { - buffer_available_count_ += 1; - ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_); - } - - void HandleBufferRemoved(DvrReadBuffer* buffer) { - buffer_removed_count_ += 1; - ALOGD_IF(TRACE, "Buffer removed, buffer=%p, count=%d", buffer, - buffer_removed_count_); - } - - DvrWriteBufferQueue* write_queue_ = nullptr; - int buffer_available_count_{0}; - int buffer_removed_count_{0}; -}; - -TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - api_.WriteBufferQueueDestroy(write_queue_); - write_queue_ = nullptr; -} - -TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - size_t capacity = api_.WriteBufferQueueGetCapacity(write_queue_); - - ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity); - ASSERT_EQ(kQueueCapacity, capacity); -} - -TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - DvrReadBufferQueue* read_queue = nullptr; - ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue); - - api_.ReadBufferQueueDestroy(read_queue); -} - -TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - DvrReadBufferQueue* read_queue1 = nullptr; - DvrReadBufferQueue* read_queue2 = nullptr; - ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); - - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue1); - - ret = api_.ReadBufferQueueCreateReadQueue(read_queue1, &read_queue2); - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue2); - ASSERT_NE(read_queue1, read_queue2); - - api_.ReadBufferQueueDestroy(read_queue1); - api_.ReadBufferQueueDestroy(read_queue2); -} - -TEST_F(DvrBufferQueueTest, GainBuffer) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(ret, 0); - - DvrWriteBuffer* wb = nullptr; - EXPECT_FALSE(api_.WriteBufferIsValid(wb)); - - DvrNativeBufferMetadata meta; - int fence_fd = -1; - ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta, - &fence_fd); - ASSERT_EQ(ret, 0); - EXPECT_EQ(fence_fd, -1); - EXPECT_NE(wb, nullptr); - EXPECT_TRUE(api_.WriteBufferIsValid(wb)); -} - -TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(ret, 0); - - DvrReadBufferQueue* read_queue = nullptr; - DvrReadBuffer* rb = nullptr; - DvrWriteBuffer* wb = nullptr; - DvrNativeBufferMetadata meta1; - DvrNativeBufferMetadata meta2; - int fence_fd = -1; - - ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - - ASSERT_EQ(ret, 0); - ASSERT_NE(read_queue, nullptr); - - api_.ReadBufferQueueSetBufferAvailableCallback( - read_queue, &BufferAvailableCallback, this); - - // Gain buffer for writing. - ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, - &meta1, &fence_fd); - ASSERT_EQ(ret, 0); - ASSERT_NE(wb, nullptr); - ASSERT_TRUE(api_.WriteBufferIsValid(wb)); - ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", - wb, fence_fd); - close(fence_fd); - - // Post buffer to the read_queue. - meta1.timestamp = 42; - ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); - ASSERT_EQ(ret, 0); - ASSERT_FALSE(api_.WriteBufferIsValid(wb)); - wb = nullptr; - - // Acquire buffer for reading. - ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, - &meta2, &fence_fd); - ASSERT_EQ(ret, 0); - ASSERT_NE(rb, nullptr); - - // Dequeue is successfully, BufferAvailableCallback should be fired once. - ASSERT_EQ(buffer_available_count_, 1); - ASSERT_TRUE(api_.ReadBufferIsValid(rb)); - - // Metadata should be passed along from producer to consumer properly. - ASSERT_EQ(meta1.timestamp, meta2.timestamp); - - ALOGD_IF(TRACE, - "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb, - fence_fd); - close(fence_fd); - - // Release buffer to the write_queue. - ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, - /*release_fence_fd=*/-1); - ASSERT_EQ(ret, 0); - ASSERT_FALSE(api_.ReadBufferIsValid(rb)); - rb = nullptr; - - // TODO(b/34387835) Currently buffer allocation has to happen after all queues - // are initialized. - size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue); - - ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity); - ASSERT_EQ(kQueueCapacity, capacity); - - api_.ReadBufferQueueDestroy(read_queue); -} - -TEST_F(DvrBufferQueueTest, GetANativeWindow) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - /*capacity=*/0, /*user_metadata_size=*/0, &write_queue_); - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, write_queue_); - - ANativeWindow* window = nullptr; - ret = api_.WriteBufferQueueGetANativeWindow(write_queue_, &window); - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, window); - - uint32_t width = ANativeWindow_getWidth(window); - uint32_t height = ANativeWindow_getHeight(window); - uint32_t format = ANativeWindow_getFormat(window); - ASSERT_EQ(kBufferWidth, width); - ASSERT_EQ(kBufferHeight, height); - ASSERT_EQ(kBufferFormat, format); -} - -// Create buffer queue of three buffers and dequeue three buffers out of it. -// Before each dequeue operation, we resize the buffer queue and expect the -// queue always return buffer with desired dimension. -TEST_F(DvrBufferQueueTest, ResizeBuffer) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - int fence_fd = -1; - - DvrNativeBufferMetadata meta; - DvrReadBufferQueue* read_queue = nullptr; - DvrWriteBuffer* wb1 = nullptr; - DvrWriteBuffer* wb2 = nullptr; - DvrWriteBuffer* wb3 = nullptr; - AHardwareBuffer* ahb1 = nullptr; - AHardwareBuffer* ahb2 = nullptr; - AHardwareBuffer* ahb3 = nullptr; - AHardwareBuffer_Desc buffer_desc; - - ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue); - - api_.ReadBufferQueueSetBufferRemovedCallback(read_queue, - &BufferRemovedCallback, this); - - // Handle all pending events on the read queue. - ret = api_.ReadBufferQueueHandleEvents(read_queue); - ASSERT_EQ(0, ret); - - size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue); - ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity); - ASSERT_EQ(kQueueCapacity, capacity); - - // Resize before dequeuing. - constexpr uint32_t w1 = 10; - ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight); - ASSERT_EQ(0, ret); - - // Gain first buffer for writing. All buffers will be resized. - ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, - &meta, &fence_fd); - ASSERT_EQ(0, ret); - ASSERT_TRUE(api_.WriteBufferIsValid(wb1)); - ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1); - close(fence_fd); - - // Check the buffer dimension. - ret = api_.WriteBufferGetAHardwareBuffer(wb1, &ahb1); - ASSERT_EQ(0, ret); - AHardwareBuffer_describe(ahb1, &buffer_desc); - ASSERT_EQ(w1, buffer_desc.width); - ASSERT_EQ(kBufferHeight, buffer_desc.height); - AHardwareBuffer_release(ahb1); - - // For the first resize, all buffers are reallocated. - int expected_buffer_removed_count = kQueueCapacity; - ret = api_.ReadBufferQueueHandleEvents(read_queue); - ASSERT_EQ(0, ret); - ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); - - // Resize the queue. We are testing with blob format, keep height to be 1. - constexpr uint32_t w2 = 20; - ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight); - ASSERT_EQ(0, ret); - - // The next buffer we dequeued should have new width. - ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, - &meta, &fence_fd); - ASSERT_EQ(0, ret); - ASSERT_TRUE(api_.WriteBufferIsValid(wb2)); - ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2, - fence_fd); - close(fence_fd); - - // Check the buffer dimension, should be new width - ret = api_.WriteBufferGetAHardwareBuffer(wb2, &ahb2); - ASSERT_EQ(0, ret); - AHardwareBuffer_describe(ahb2, &buffer_desc); - ASSERT_EQ(w2, buffer_desc.width); - AHardwareBuffer_release(ahb2); - - // For the second resize, all but one buffers are reallocated. - expected_buffer_removed_count += (kQueueCapacity - 1); - ret = api_.ReadBufferQueueHandleEvents(read_queue); - ASSERT_EQ(0, ret); - ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); - - // Resize the queue for the third time. - constexpr uint32_t w3 = 30; - ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight); - ASSERT_EQ(0, ret); - - // The next buffer we dequeued should have new width. - ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, - &meta, &fence_fd); - ASSERT_EQ(0, ret); - ASSERT_TRUE(api_.WriteBufferIsValid(wb3)); - ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3, - fence_fd); - close(fence_fd); - - // Check the buffer dimension, should be new width - ret = api_.WriteBufferGetAHardwareBuffer(wb3, &ahb3); - ASSERT_EQ(0, ret); - AHardwareBuffer_describe(ahb3, &buffer_desc); - ASSERT_EQ(w3, buffer_desc.width); - AHardwareBuffer_release(ahb3); - - // For the third resize, all but two buffers are reallocated. - expected_buffer_removed_count += (kQueueCapacity - 2); - ret = api_.ReadBufferQueueHandleEvents(read_queue); - ASSERT_EQ(0, ret); - ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); - - api_.ReadBufferQueueDestroy(read_queue); -} - -TEST_F(DvrBufferQueueTest, ReadQueueEventFd) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - DvrReadBufferQueue* read_queue = nullptr; - ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue); - - int event_fd = api_.ReadBufferQueueGetEventFd(read_queue); - ASSERT_GT(event_fd, 0); -} - -// Verifies a Dvr{Read,Write}BufferQueue contains the same set of -// Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id, -// the corresponding AHardwareBuffer handle stays the same. -TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(0, ret); - - int fence_fd = -1; - DvrReadBufferQueue* read_queue = nullptr; - EXPECT_EQ(0, api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); - - // Read buffers. - std::array<DvrReadBuffer*, kQueueCapacity> rbs; - // Write buffers. - std::array<DvrWriteBuffer*, kQueueCapacity> wbs; - // Buffer metadata. - std::array<DvrNativeBufferMetadata, kQueueCapacity> metas; - // Hardware buffers for Read buffers. - std::unordered_map<int, AHardwareBuffer*> rhbs; - // Hardware buffers for Write buffers. - std::unordered_map<int, AHardwareBuffer*> whbs; - - constexpr int kNumTests = 100; - - // This test runs the following operations many many times. Thus we prefer to - // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output. - std::function<void(size_t i)> Gain = [&](size_t i) { - int ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10, - &wbs[i], &metas[i], &fence_fd); - ASSERT_EQ(ret, 0); - ASSERT_LT(fence_fd, 0); // expect invalid fence. - ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i])); - int buffer_id = api_.WriteBufferGetId(wbs[i]); - ASSERT_GT(buffer_id, 0); - - AHardwareBuffer* hb = nullptr; - ASSERT_EQ(0, api_.WriteBufferGetAHardwareBuffer(wbs[i], &hb)); - - auto whb_it = whbs.find(buffer_id); - if (whb_it == whbs.end()) { - // If this is a new buffer id, check that total number of unique - // hardware buffers won't exceed queue capacity. - ASSERT_LT(whbs.size(), kQueueCapacity); - whbs.emplace(buffer_id, hb); - } else { - // If this is a buffer id we have seen before, check that the - // buffer_id maps to the same AHardwareBuffer handle. - ASSERT_EQ(hb, whb_it->second); - } - }; - - std::function<void(size_t i)> Post = [&](size_t i) { - ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i])); - - metas[i].timestamp++; - int ret = api_.WriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i], - /*fence=*/-1); - ASSERT_EQ(ret, 0); - }; - - std::function<void(size_t i)> Acquire = [&](size_t i) { - int ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, - &rbs[i], &metas[i], &fence_fd); - ASSERT_EQ(ret, 0); - ASSERT_LT(fence_fd, 0); // expect invalid fence. - ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i])); - - int buffer_id = api_.ReadBufferGetId(rbs[i]); - ASSERT_GT(buffer_id, 0); - - AHardwareBuffer* hb = nullptr; - ASSERT_EQ(0, api_.ReadBufferGetAHardwareBuffer(rbs[i], &hb)); - - auto rhb_it = rhbs.find(buffer_id); - if (rhb_it == rhbs.end()) { - // If this is a new buffer id, check that total number of unique hardware - // buffers won't exceed queue capacity. - ASSERT_LT(rhbs.size(), kQueueCapacity); - rhbs.emplace(buffer_id, hb); - } else { - // If this is a buffer id we have seen before, check that the buffer_id - // maps to the same AHardwareBuffer handle. - ASSERT_EQ(hb, rhb_it->second); - } - }; - - std::function<void(size_t i)> Release = [&](size_t i) { - ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i])); - - int ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i], - /*release_fence_fd=*/-1); - ASSERT_EQ(ret, 0); - }; - - // Scenario one: - for (int i = 0; i < kNumTests; i++) { - // Gain all write buffers. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Gain(i)); - } - // Post all write buffers. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Post(i)); - } - // Acquire all read buffers. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Acquire(i)); - } - // Release all read buffers. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Release(i)); - } - } - - // Scenario two: - for (int i = 0; i < kNumTests; i++) { - // Gain and post all write buffers. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Gain(i)); - ASSERT_NO_FATAL_FAILURE(Post(i)); - } - // Acquire and release all read buffers. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Acquire(i)); - ASSERT_NO_FATAL_FAILURE(Release(i)); - } - } - - // Scenario three: - for (int i = 0; i < kNumTests; i++) { - // Gain all write buffers then post them in reversed order. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Gain(i)); - } - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i)); - } - - // Acquire all write buffers then release them in reversed order. - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Acquire(i)); - } - for (size_t i = 0; i < kQueueCapacity; i++) { - ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i)); - } - } -} - -TEST_F(DvrBufferQueueTest, ConsumerReleaseAfterProducerDestroy) { - int ret = api_.WriteBufferQueueCreate( - kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, - kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); - ASSERT_EQ(ret, 0); - - DvrReadBufferQueue* read_queue = nullptr; - DvrReadBuffer* rb = nullptr; - DvrWriteBuffer* wb = nullptr; - DvrNativeBufferMetadata meta1; - DvrNativeBufferMetadata meta2; - int fence_fd = -1; - - ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - ASSERT_EQ(ret, 0); - - api_.ReadBufferQueueSetBufferAvailableCallback( - read_queue, &BufferAvailableCallback, this); - - // Gain buffer for writing. - ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, - &meta1, &fence_fd); - ASSERT_EQ(ret, 0); - close(fence_fd); - - // Post buffer to the read_queue. - ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); - ASSERT_EQ(ret, 0); - wb = nullptr; - - // Acquire buffer for reading. - ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, - &meta2, &fence_fd); - ASSERT_EQ(ret, 0); - close(fence_fd); - - // Destroy the write buffer queue and make sure the reader queue is picking - // these events up. - api_.WriteBufferQueueDestroy(write_queue_); - ret = api_.ReadBufferQueueHandleEvents(read_queue); - ASSERT_EQ(0, ret); - - // Release buffer to the write_queue. - ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, - /*release_fence_fd=*/-1); - ASSERT_EQ(ret, 0); - rb = nullptr; - - api_.ReadBufferQueueDestroy(read_queue); -} - -} // namespace diff --git a/libs/vr/libdvr/tests/dvr_display-test.cpp b/libs/vr/libdvr/tests/dvr_display-test.cpp deleted file mode 100644 index c72f94035b..0000000000 --- a/libs/vr/libdvr/tests/dvr_display-test.cpp +++ /dev/null @@ -1,351 +0,0 @@ -#include <android/hardware_buffer.h> -#include <android/log.h> -#include <dvr/dvr_api.h> -#include <dvr/dvr_display_types.h> -#include <dvr/dvr_surface.h> - -#include <gtest/gtest.h> - -#include "dvr_api_test.h" - -#define LOG_TAG "dvr_display-test" - -#ifndef ALOGD -#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#endif - -class DvrDisplayTest : public DvrApiTest { - protected: - void SetUp() override { - DvrApiTest::SetUp(); - int ret = api_.GetNativeDisplayMetrics(sizeof(display_metrics_), - &display_metrics_); - ASSERT_EQ(ret, 0) << "Failed to get display metrics."; - ALOGD( - "display_width: %d, display_height: %d, display_x_dpi: %d, " - "display_y_dpi: %d, vsync_period_ns: %d.", - display_metrics_.display_width, display_metrics_.display_height, - display_metrics_.display_x_dpi, display_metrics_.display_y_dpi, - display_metrics_.vsync_period_ns); - } - - void TearDown() override { - if (write_queue_ != nullptr) { - api_.WriteBufferQueueDestroy(write_queue_); - write_queue_ = nullptr; - } - if (direct_surface_ != nullptr) { - api_.SurfaceDestroy(direct_surface_); - direct_surface_ = nullptr; - } - DvrApiTest::TearDown(); - } - - /* Convert a write buffer to an android hardware buffer and fill in - * color_textures evenly to the buffer. - * AssertionError if the width of the buffer is not equal to the input width, - * AssertionError if the height of the buffer is not equal to the input - * height. - */ - void FillWriteBuffer(DvrWriteBuffer* write_buffer, - const std::vector<uint32_t>& color_textures, - uint32_t width, uint32_t height); - - // Write buffer queue properties. - static constexpr uint64_t kUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | - AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; - uint32_t kFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; - static constexpr size_t kMetadataSize = 0; - static constexpr int kTimeoutMs = 1000; // Time for getting buffer. - uint32_t kLayerCount = 1; - DvrWriteBufferQueue* write_queue_ = nullptr; - DvrSurface* direct_surface_ = nullptr; - - // Device display properties. - DvrNativeDisplayMetrics display_metrics_; -}; - -TEST_F(DvrDisplayTest, DisplayWithOneBuffer) { - // Create a direct surface. - std::vector<DvrSurfaceAttribute> direct_surface_attributes = { - {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, - .value.int32_value = 10}, - {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - }; - int ret = - api_.SurfaceCreate(direct_surface_attributes.data(), - direct_surface_attributes.size(), &direct_surface_); - ASSERT_EQ(ret, 0) << "Failed to create direct surface."; - - // Create a buffer queue with the direct surface. - constexpr size_t kCapacity = 1; - uint32_t width = display_metrics_.display_width; - uint32_t height = display_metrics_.display_height; - ret = api_.SurfaceCreateWriteBufferQueue( - direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity, - kMetadataSize, &write_queue_); - EXPECT_EQ(0, ret) << "Failed to create buffer queue."; - ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null."; - - // Get buffer from WriteBufferQueue. - DvrWriteBuffer* write_buffer = nullptr; - DvrNativeBufferMetadata out_meta; - int out_fence_fd = -1; - ret = api_.WriteBufferQueueGainBuffer(write_queue_, kTimeoutMs, &write_buffer, - &out_meta, &out_fence_fd); - EXPECT_EQ(0, ret) << "Failed to get the buffer."; - ASSERT_NE(nullptr, write_buffer) << "Gained buffer should not be null."; - - // Color the write buffer. - FillWriteBuffer(write_buffer, - {0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}, - width, height); - - // Post buffer. - int ready_fence_fd = -1; - ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta, - ready_fence_fd); - EXPECT_EQ(0, ret) << "Failed to post the buffer."; - - sleep(5); // For visual check on the device under test. - // Should observe three primary colors on the screen center. -} - -TEST_F(DvrDisplayTest, DisplayWithDoubleBuffering) { - // Create a direct surface. - std::vector<DvrSurfaceAttribute> direct_surface_attributes = { - {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, - .value.int32_value = 10}, - {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - }; - int ret = - api_.SurfaceCreate(direct_surface_attributes.data(), - direct_surface_attributes.size(), &direct_surface_); - ASSERT_EQ(ret, 0) << "Failed to create direct surface."; - - // Create a buffer queue with the direct surface. - constexpr size_t kCapacity = 2; - uint32_t width = display_metrics_.display_width; - uint32_t height = display_metrics_.display_height; - ret = api_.SurfaceCreateWriteBufferQueue( - direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity, - kMetadataSize, &write_queue_); - EXPECT_EQ(0, ret) << "Failed to create buffer queue."; - ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null."; - - int num_display_cycles_in_5s = 5 / (display_metrics_.vsync_period_ns / 1e9); - ALOGD("The number of display cycles: %d", num_display_cycles_in_5s); - int bufferhub_id_prev_write_buffer = -1; - for (int i = 0; i < num_display_cycles_in_5s; ++i) { - // Get a buffer from the WriteBufferQueue. - DvrWriteBuffer* write_buffer = nullptr; - DvrNativeBufferMetadata out_meta; - int out_fence_fd = -1; - ret = api_.WriteBufferQueueGainBuffer( - write_queue_, kTimeoutMs, &write_buffer, &out_meta, &out_fence_fd); - EXPECT_EQ(0, ret) << "Failed to get the a write buffer."; - ASSERT_NE(nullptr, write_buffer) << "The gained buffer should not be null."; - - int bufferhub_id = api_.WriteBufferGetId(write_buffer); - ALOGD("Display cycle: %d, bufferhub id of the write buffer: %d", i, - bufferhub_id); - EXPECT_NE(bufferhub_id_prev_write_buffer, bufferhub_id) - << "Double buffering should be using the two buffers in turns, not " - "reusing the same write buffer."; - bufferhub_id_prev_write_buffer = bufferhub_id; - - // Color the write buffer. - if (i % 2) { - FillWriteBuffer(write_buffer, {0xffff0000, 0xff00ff00, 0xff0000ff}, width, - height); - } else { - FillWriteBuffer(write_buffer, {0xff00ff00, 0xff0000ff, 0xffff0000}, width, - height); - } - - // Post the write buffer. - int ready_fence_fd = -1; - ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta, - ready_fence_fd); - EXPECT_EQ(0, ret) << "Failed to post the buffer."; - } - // Should observe blinking screen in secondary colors - // although it is actually displaying primary colors. -} - -TEST_F(DvrDisplayTest, DisplayWithTwoHardwareLayers) { - // Create the direct_surface_0 of z order 10 and direct_surface_1 of z - // order 11. - DvrSurface* direct_surface_0 = nullptr; - std::vector<DvrSurfaceAttribute> direct_surface_0_attributes = { - {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, - .value.int32_value = 10}, - {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - }; - int ret = - api_.SurfaceCreate(direct_surface_0_attributes.data(), - direct_surface_0_attributes.size(), &direct_surface_0); - EXPECT_EQ(ret, 0) << "Failed to create direct surface."; - - DvrSurface* direct_surface_1 = nullptr; - std::vector<DvrSurfaceAttribute> direct_surface_1_attributes = { - {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, - .value.int32_value = 11}, - {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, - .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - .value.bool_value = true}, - }; - ret = - api_.SurfaceCreate(direct_surface_1_attributes.data(), - direct_surface_1_attributes.size(), &direct_surface_1); - EXPECT_EQ(ret, 0) << "Failed to create direct surface."; - - // Create a buffer queue for each of the direct surfaces. - constexpr size_t kCapacity = 1; - uint32_t width = display_metrics_.display_width; - uint32_t height = display_metrics_.display_height; - - DvrWriteBufferQueue* write_queue_0 = nullptr; - ret = api_.SurfaceCreateWriteBufferQueue( - direct_surface_0, width, height, kFormat, kLayerCount, kUsage, kCapacity, - kMetadataSize, &write_queue_0); - EXPECT_EQ(0, ret) << "Failed to create buffer queue."; - EXPECT_NE(nullptr, write_queue_0) << "Write buffer queue should not be null."; - - DvrWriteBufferQueue* write_queue_1 = nullptr; - ret = api_.SurfaceCreateWriteBufferQueue( - direct_surface_1, width, height, kFormat, kLayerCount, kUsage, kCapacity, - kMetadataSize, &write_queue_1); - EXPECT_EQ(0, ret) << "Failed to create buffer queue."; - EXPECT_NE(nullptr, write_queue_1) << "Write buffer queue should not be null."; - - // Get a buffer from each of the write buffer queues. - DvrWriteBuffer* write_buffer_0 = nullptr; - DvrNativeBufferMetadata out_meta_0; - int out_fence_fd = -1; - ret = api_.WriteBufferQueueGainBuffer( - write_queue_0, kTimeoutMs, &write_buffer_0, &out_meta_0, &out_fence_fd); - EXPECT_EQ(0, ret) << "Failed to get the buffer."; - EXPECT_NE(nullptr, write_buffer_0) << "Gained buffer should not be null."; - - DvrWriteBuffer* write_buffer_1 = nullptr; - DvrNativeBufferMetadata out_meta_1; - out_fence_fd = -1; - ret = api_.WriteBufferQueueGainBuffer( - write_queue_1, kTimeoutMs, &write_buffer_1, &out_meta_1, &out_fence_fd); - EXPECT_EQ(0, ret) << "Failed to get the buffer."; - EXPECT_NE(nullptr, write_buffer_1) << "Gained buffer should not be null."; - - // Color the write buffers. - FillWriteBuffer(write_buffer_0, {0xffff0000, 0xff00ff00, 0xff0000ff}, width, - height); - FillWriteBuffer(write_buffer_1, {0x7f00ff00, 0x7f0000ff, 0x7fff0000}, width, - height); - - // Post buffers. - int ready_fence_fd = -1; - ret = api_.WriteBufferQueuePostBuffer(write_queue_0, write_buffer_0, - &out_meta_0, ready_fence_fd); - EXPECT_EQ(0, ret) << "Failed to post the buffer."; - - ready_fence_fd = -1; - ret = api_.WriteBufferQueuePostBuffer(write_queue_1, write_buffer_1, - &out_meta_1, ready_fence_fd); - EXPECT_EQ(0, ret) << "Failed to post the buffer."; - - sleep(5); // For visual check on the device under test. - // Should observe three secondary colors. - - // Test finished. Clean up buffers and surfaces. - if (write_queue_0 != nullptr) { - api_.WriteBufferQueueDestroy(write_queue_0); - write_queue_0 = nullptr; - } - if (write_queue_1 != nullptr) { - api_.WriteBufferQueueDestroy(write_queue_1); - write_queue_1 = nullptr; - } - if (direct_surface_0 != nullptr) { - api_.SurfaceDestroy(direct_surface_0); - } - if (direct_surface_1 != nullptr) { - api_.SurfaceDestroy(direct_surface_1); - } -} - -void DvrDisplayTest::FillWriteBuffer( - DvrWriteBuffer* write_buffer, const std::vector<uint32_t>& color_textures, - uint32_t width, uint32_t height) { - uint32_t num_colors = color_textures.size(); - // Convert the first write buffer to an android hardware buffer. - AHardwareBuffer* ah_buffer = nullptr; - int ret = api_.WriteBufferGetAHardwareBuffer(write_buffer, &ah_buffer); - ASSERT_EQ(0, ret) << "Failed to get a hardware buffer from the write buffer."; - ASSERT_NE(nullptr, ah_buffer) << "AHardware buffer should not be null."; - AHardwareBuffer_Desc ah_buffer_describe; - AHardwareBuffer_describe(ah_buffer, &ah_buffer_describe); - ASSERT_EQ(ah_buffer_describe.format, kFormat) - << "The format of the android hardware buffer is wrong."; - ASSERT_EQ(ah_buffer_describe.layers, kLayerCount) - << "The obtained android hardware buffer should have 2 layers."; - ASSERT_EQ(ah_buffer_describe.width, width) - << "The obtained android hardware buffer width is wrong."; - ASSERT_EQ(ah_buffer_describe.height, height) - << "The obtained android hardware buffer height is wrong."; - // Change the content of the android hardware buffer. - void* buffer_data = nullptr; - int32_t fence = -1; - ret = AHardwareBuffer_lock(ah_buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, - fence, nullptr, &buffer_data); - ASSERT_EQ(0, ret) << "Failed to lock the hardware buffer."; - ASSERT_NE(nullptr, buffer_data) << "Buffer data should not be null."; - - uint32_t num_pixels = width * height / num_colors; - for (uint32_t color_index = 0; color_index < num_colors - 1; ++color_index) { - uint32_t color_texture = color_textures[color_index]; - for (uint32_t i = 0; i < num_pixels; ++i) { - memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) + - (i + num_pixels * color_index) * - sizeof(color_texture)), - &color_texture, sizeof(color_texture)); - } - } - uint32_t color_texture = color_textures[num_colors - 1]; - uint32_t num_colored_pixels = num_pixels * (num_colors - 1); - num_pixels = width * height - num_colored_pixels; - for (uint32_t i = 0; i < num_pixels; ++i) { - memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) + - (i + num_colored_pixels) * - sizeof(color_texture)), - &color_texture, sizeof(color_texture)); - } - fence = -1; - ret = AHardwareBuffer_unlock(ah_buffer, &fence); - EXPECT_EQ(0, ret) << "Failed to unlock the hardware buffer."; - - // Release the android hardware buffer. - AHardwareBuffer_release(ah_buffer); -} diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp deleted file mode 100644 index 07e2121970..0000000000 --- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp +++ /dev/null @@ -1,891 +0,0 @@ -#include <android-base/properties.h> -#include <base/logging.h> -#include <cutils/properties.h> -#include <gtest/gtest.h> -#include <log/log.h> -#include <poll.h> - -#include <android/hardware_buffer.h> - -#include <algorithm> -#include <array> -#include <set> -#include <thread> -#include <vector> - -#include <dvr/dvr_configuration_data.h> -#include <dvr/dvr_deleter.h> -#include <dvr/dvr_display_manager.h> -#include <dvr/dvr_surface.h> - -#include <pdx/status.h> - -using android::pdx::ErrorStatus; -using android::pdx::Status; - -namespace android { -namespace dvr { - -namespace { - -using ::testing::Test; - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE; - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int32_t value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32; - attribute.value.int32_value = value; - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int64_t value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64; - attribute.value.int64_value = value; - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, bool value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL; - attribute.value.bool_value = value; - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, float value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT; - attribute.value.float_value = value; - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, - const std::array<float, 2>& value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2; - std::copy(value.begin(), value.end(), attribute.value.float2_value); - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, - const std::array<float, 3>& value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3; - std::copy(value.begin(), value.end(), attribute.value.float3_value); - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, - const std::array<float, 4>& value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4; - std::copy(value.begin(), value.end(), attribute.value.float4_value); - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, - const std::array<float, 8>& value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8; - std::copy(value.begin(), value.end(), attribute.value.float8_value); - return attribute; -} - -DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, - const std::array<float, 16>& value) { - DvrSurfaceAttribute attribute; - attribute.key = key; - attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16; - std::copy(value.begin(), value.end(), attribute.value.float16_value); - return attribute; -} - -Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false, - int32_t z_order = 0) { - DvrSurface* surface = nullptr; - DvrSurfaceAttribute attributes[] = { - MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order), - MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)}; - - const int ret = dvrSurfaceCreate( - attributes, std::extent<decltype(attributes)>::value, &surface); - if (ret < 0) - return ErrorStatus(-ret); - else - return {UniqueDvrSurface(surface)}; -} - -Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue( - const UniqueDvrSurface& surface, uint32_t width, uint32_t height, - uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, - size_t metadata_size) { - DvrWriteBufferQueue* queue; - const int ret = dvrSurfaceCreateWriteBufferQueue( - surface.get(), width, height, format, layer_count, usage, capacity, - metadata_size, &queue); - if (ret < 0) - return ErrorStatus(-ret); - else - return {UniqueDvrWriteBufferQueue(queue)}; -} - -Status<std::vector<uint8_t>> GetConfigData(int config_type) { - uint8_t* data = nullptr; - size_t data_size = 0; - int error = dvrConfigurationDataGet(config_type, &data, &data_size); - if (error < 0) { - return ErrorStatus(-error); - } - - if (!data || data_size == 0) { - return ErrorStatus(EINVAL); - } - std::vector<uint8_t> data_result(data, data + data_size); - dvrConfigurationDataDestroy(data); - std::string s(data, data + data_size); - return {std::move(data_result)}; -} - -class TestDisplayManager { - public: - TestDisplayManager(UniqueDvrDisplayManager display_manager, - UniqueDvrSurfaceState surface_state) - : display_manager_(std::move(display_manager)), - surface_state_(std::move(surface_state)) { - const int fd = dvrDisplayManagerGetEventFd(display_manager_.get()); - LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd); - display_manager_event_fd_ = fd; - } - - Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id, - int queue_id) { - DvrReadBufferQueue* queue; - const int ret = dvrDisplayManagerGetReadBufferQueue( - display_manager_.get(), surface_id, queue_id, &queue); - if (ret < 0) - return ErrorStatus(-ret); - else - return {UniqueDvrReadBufferQueue(queue)}; - } - - Status<void> UpdateSurfaceState() { - const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(), - surface_state_.get()); - if (ret < 0) - return ErrorStatus(-ret); - else - return {}; - } - - enum : int { kTimeoutMs = 10000 }; // 10s - - Status<void> WaitForUpdate(int timeout_ms = kTimeoutMs) { - if (display_manager_event_fd_ < 0) - return ErrorStatus(-display_manager_event_fd_); - - pollfd pfd = {display_manager_event_fd_, POLLIN, 0}; - const int count = poll(&pfd, 1, timeout_ms); - if (count < 0) - return ErrorStatus(errno); - else if (count == 0) - return ErrorStatus(ETIMEDOUT); - - int events; - const int ret = dvrDisplayManagerTranslateEpollEventMask( - display_manager_.get(), pfd.revents, &events); - if (ret < 0) - return ErrorStatus(-ret); - else if (events & POLLIN) - return UpdateSurfaceState(); - else - return ErrorStatus(EPROTO); - } - - Status<size_t> GetSurfaceCount() { - size_t count = 0; - const int ret = - dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count); - if (ret < 0) - return ErrorStatus(-ret); - else - return {count}; - } - - Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) { - DvrSurfaceUpdateFlags update_flags; - const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(), - surface_index, &update_flags); - if (ret < 0) - return ErrorStatus(-ret); - else - return {update_flags}; - } - - Status<int> GetSurfaceId(size_t surface_index) { - int surface_id; - const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(), - surface_index, &surface_id); - if (ret < 0) - return ErrorStatus(-ret); - else - return {surface_id}; - } - - Status<int> GetProcessId(size_t surface_index) { - int process_id; - const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(), - surface_index, &process_id); - if (ret < 0) - return ErrorStatus(-ret); - else - return {process_id}; - } - - Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) { - std::vector<DvrSurfaceAttribute> attributes; - size_t count = 0; - const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(), - surface_index, &count); - if (ret < 0) - return ErrorStatus(-ret); - - attributes.resize(count); - const ssize_t return_count = dvrSurfaceStateGetAttributes( - surface_state_.get(), surface_index, attributes.data(), count); - if (return_count < 0) - return ErrorStatus(-return_count); - - attributes.resize(return_count); - return {std::move(attributes)}; - } - - Status<std::vector<int>> GetQueueIds(size_t surface_index) { - std::vector<int> queue_ids; - size_t count = 0; - const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(), - surface_index, &count); - if (ret < 0) - return ErrorStatus(-ret); - - if (count > 0) { - queue_ids.resize(count); - const ssize_t return_count = dvrSurfaceStateGetQueueIds( - surface_state_.get(), surface_index, queue_ids.data(), count); - if (return_count < 0) - return ErrorStatus(-return_count); - - queue_ids.resize(return_count); - } - - return {std::move(queue_ids)}; - } - - private: - UniqueDvrDisplayManager display_manager_; - UniqueDvrSurfaceState surface_state_; - - // Owned by object in display_manager_, do not explicitly close. - int display_manager_event_fd_; - - TestDisplayManager(const TestDisplayManager&) = delete; - void operator=(const TestDisplayManager&) = delete; -}; - -class DvrDisplayManagerTest : public Test { - protected: - void SetUp() override { - int ret; - DvrDisplayManager* display_manager; - DvrSurfaceState* surface_state; - - ret = dvrDisplayManagerCreate(&display_manager); - ASSERT_EQ(0, ret) << "Failed to create display manager client"; - ASSERT_NE(nullptr, display_manager); - - ret = dvrSurfaceStateCreate(&surface_state); - ASSERT_EQ(0, ret) << "Failed to create surface state object"; - ASSERT_NE(nullptr, surface_state); - - manager_.reset( - new TestDisplayManager(UniqueDvrDisplayManager(display_manager), - UniqueDvrSurfaceState(surface_state))); - } - void TearDown() override {} - - std::unique_ptr<TestDisplayManager> manager_; -}; - -// TODO(eieio): Consider moving these somewhere more central because they are -// broadly useful. - -template <typename T> -testing::AssertionResult StatusOk(const char* status_expression, - const Status<T>& status) { - if (!status.ok()) { - return testing::AssertionFailure() - << "(" << status_expression - << ") expected to indicate success but actually contains error (" - << status.error() << ")"; - } else { - return testing::AssertionSuccess(); - } -} - -template <typename T> -testing::AssertionResult StatusError(const char* status_expression, - const Status<T>& status) { - if (status.ok()) { - return testing::AssertionFailure() - << "(" << status_expression - << ") expected to indicate error but instead indicates success."; - } else { - return testing::AssertionSuccess(); - } -} - -template <typename T> -testing::AssertionResult StatusHasError(const char* status_expression, - const char* /*error_code_expression*/, - const Status<T>& status, - int error_code) { - if (status.ok()) { - return StatusError(status_expression, status); - } else if (status.error() != error_code) { - return testing::AssertionFailure() - << "(" << status_expression << ") expected to indicate error (" - << error_code << ") but actually indicates error (" << status.error() - << ")"; - } else { - return testing::AssertionSuccess(); - } -} - -template <typename T, typename U> -testing::AssertionResult StatusHasValue(const char* status_expression, - const char* /*value_expression*/, - const Status<T>& status, - const U& value) { - if (!status.ok()) { - return StatusOk(status_expression, status); - } else if (status.get() != value) { - return testing::AssertionFailure() - << "(" << status_expression << ") expected to contain value (" - << testing::PrintToString(value) << ") but actually contains value (" - << testing::PrintToString(status.get()) << ")"; - } else { - return testing::AssertionSuccess(); - } -} - -template <typename T, typename Op> -testing::AssertionResult StatusPred(const char* status_expression, - const char* pred_expression, - const Status<T>& status, Op pred) { - if (!status.ok()) { - return StatusOk(status_expression, status); - } else if (!pred(status.get())) { - return testing::AssertionFailure() - << status_expression << " value (" - << testing::PrintToString(status.get()) - << ") failed to pass predicate " << pred_expression; - } else { - return testing::AssertionSuccess(); - } -} - -#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status) -#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status) - -#define ASSERT_STATUS_ERROR_VALUE(value, status) \ - ASSERT_PRED_FORMAT2(StatusHasError, status, value) - -#define ASSERT_STATUS_EQ(value, status) \ - ASSERT_PRED_FORMAT2(StatusHasValue, status, value) - -#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status) -#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status) - -#define EXPECT_STATUS_ERROR_VALUE(value, status) \ - EXPECT_PRED_FORMAT2(StatusHasError, status, value) - -#define EXPECT_STATUS_EQ(value, status) \ - EXPECT_PRED_FORMAT2(StatusHasValue, status, value) - -#define EXPECT_STATUS_PRED(pred, status) \ - EXPECT_PRED_FORMAT2(StatusPred, status, pred) - -#if 0 -// Verify utility predicate/macro functionality. This section is commented out -// because it is designed to fail in some cases to validate the helpers. -TEST_F(Test, ExpectVoid) { - Status<void> status_error{ErrorStatus{EINVAL}}; - Status<void> status_ok{}; - - EXPECT_STATUS_ERROR(status_error); - EXPECT_STATUS_ERROR(status_ok); - EXPECT_STATUS_OK(status_error); - EXPECT_STATUS_OK(status_ok); - - EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error); - EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error); - EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok); - EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok); -} - -TEST_F(Test, ExpectInt) { - Status<int> status_error{ErrorStatus{EINVAL}}; - Status<int> status_ok{10}; - - EXPECT_STATUS_ERROR(status_error); - EXPECT_STATUS_ERROR(status_ok); - EXPECT_STATUS_OK(status_error); - EXPECT_STATUS_OK(status_ok); - - EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error); - EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error); - EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok); - EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok); - - EXPECT_STATUS_EQ(10, status_error); - EXPECT_STATUS_EQ(20, status_error); - EXPECT_STATUS_EQ(10, status_ok); - EXPECT_STATUS_EQ(20, status_ok); - - auto pred1 = [](const auto& value) { return value < 15; }; - auto pred2 = [](const auto& value) { return value > 5; }; - auto pred3 = [](const auto& value) { return value > 15; }; - auto pred4 = [](const auto& value) { return value < 5; }; - - EXPECT_STATUS_PRED(pred1, status_error); - EXPECT_STATUS_PRED(pred2, status_error); - EXPECT_STATUS_PRED(pred3, status_error); - EXPECT_STATUS_PRED(pred4, status_error); - EXPECT_STATUS_PRED(pred1, status_ok); - EXPECT_STATUS_PRED(pred2, status_ok); - EXPECT_STATUS_PRED(pred3, status_ok); - EXPECT_STATUS_PRED(pred4, status_ok); -} -#endif - -TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { - // Get surface state and verify there are no surfaces. - ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); - ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); - - // Get flags for invalid surface index. - EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0)); - - // Create an application surface. - auto surface_status = CreateApplicationSurface(); - ASSERT_STATUS_OK(surface_status); - UniqueDvrSurface surface = surface_status.take(); - ASSERT_NE(nullptr, surface.get()); - - const int surface_id = dvrSurfaceGetId(surface.get()); - ASSERT_GE(surface_id, 0); - - // Now there should be one new surface. - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount()); - - // Verify the new surface flag is set. - auto check_flags = [](const auto& value) { - return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE; - }; - EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); - - // Verify the surface id matches. - EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0)); - - // Verify the owning process of the surface. - EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0)); - - surface.reset(); - - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount()); -} - -TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) { - // Get surface state and verify there are no surfaces. - ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); - ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); - - // Get attributes for an invalid surface index. - EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0)); - - const bool kInitialVisibility = true; - const int32_t kInitialZOrder = 10; - auto surface_status = - CreateApplicationSurface(kInitialVisibility, kInitialZOrder); - ASSERT_STATUS_OK(surface_status); - auto surface = surface_status.take(); - ASSERT_NE(nullptr, surface.get()); - - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); - - // Check the initial attribute values. - auto attribute_status = manager_->GetAttributes(0); - ASSERT_STATUS_OK(attribute_status); - auto attributes = attribute_status.take(); - EXPECT_GE(attributes.size(), 2u); - - std::set<int32_t> actual_keys; - std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, - DVR_SURFACE_ATTRIBUTE_VISIBLE}; - - // Collect all the keys in attributes that match the expected keys. - auto compare_keys = [](const auto& attributes, const auto& expected_keys) { - std::set<int32_t> keys; - for (const auto& attribute : attributes) { - if (expected_keys.find(attribute.key) != expected_keys.end()) - keys.emplace(attribute.key); - } - return keys; - }; - - // If the sets match then attributes contained at least the expected keys, - // even if other keys were also present. - actual_keys = compare_keys(attributes, expected_keys); - EXPECT_EQ(expected_keys, actual_keys); - - std::vector<DvrSurfaceAttribute> attributes_to_set = { - MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, 0)}; - - // Test invalid args. - EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(nullptr, attributes_to_set.data(), - attributes_to_set.size())); - EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(surface.get(), nullptr, - attributes_to_set.size())); - - // Test attribute change events. - ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), - attributes_to_set.size())); - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - - // Verify the attributes changed flag is set. - auto check_flags = [](const auto& value) { - return value & DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED; - }; - EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); - - attribute_status = manager_->GetAttributes(0); - ASSERT_STATUS_OK(attribute_status); - attributes = attribute_status.take(); - EXPECT_GE(attributes.size(), 2u); - - expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, - DVR_SURFACE_ATTRIBUTE_VISIBLE}; - - actual_keys.clear(); - actual_keys = compare_keys(attributes, expected_keys); - EXPECT_EQ(expected_keys, actual_keys); - - // Test setting and then deleting an attribute. - const DvrSurfaceAttributeKey kUserKey = 1; - attributes_to_set = {MakeAttribute(kUserKey, 1024)}; - - ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), - attributes_to_set.size())); - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); - - attribute_status = manager_->GetAttributes(0); - ASSERT_STATUS_OK(attribute_status); - attributes = attribute_status.take(); - EXPECT_GE(attributes.size(), 2u); - - expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE, - kUserKey}; - - actual_keys.clear(); - actual_keys = compare_keys(attributes, expected_keys); - EXPECT_EQ(expected_keys, actual_keys); - - // Delete the attribute. - attributes_to_set = {MakeAttribute(kUserKey, nullptr)}; - - ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), - attributes_to_set.size())); - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); - - attribute_status = manager_->GetAttributes(0); - ASSERT_STATUS_OK(attribute_status); - attributes = attribute_status.take(); - EXPECT_GE(attributes.size(), 2u); - - expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE, - kUserKey}; - - actual_keys.clear(); - actual_keys = compare_keys(attributes, expected_keys); - EXPECT_NE(expected_keys, actual_keys); - - // Test deleting a reserved attribute. - attributes_to_set = {MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, nullptr)}; - - EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), - attributes_to_set.size())); - - // Failed attribute operations should not trigger update events. - const int kTimeoutMs = 100; // 0.1s - EXPECT_STATUS_ERROR_VALUE(ETIMEDOUT, manager_->WaitForUpdate(kTimeoutMs)); - - attribute_status = manager_->GetAttributes(0); - ASSERT_STATUS_OK(attribute_status); - attributes = attribute_status.take(); - EXPECT_GE(attributes.size(), 2u); - - expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, - DVR_SURFACE_ATTRIBUTE_VISIBLE}; - - actual_keys.clear(); - actual_keys = compare_keys(attributes, expected_keys); - EXPECT_EQ(expected_keys, actual_keys); -} - -TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) { - // Create an application surface. - auto surface_status = CreateApplicationSurface(); - ASSERT_STATUS_OK(surface_status); - UniqueDvrSurface surface = surface_status.take(); - ASSERT_NE(nullptr, surface.get()); - - enum : std::int32_t { - kInt32Key = 1, - kInt64Key, - kBoolKey, - kFloatKey, - kFloat2Key, - kFloat3Key, - kFloat4Key, - kFloat8Key, - kFloat16Key, - }; - - const std::vector<DvrSurfaceAttribute> attributes_to_set = { - MakeAttribute(kInt32Key, int32_t{0}), - MakeAttribute(kInt64Key, int64_t{0}), - MakeAttribute(kBoolKey, false), - MakeAttribute(kFloatKey, 0.0f), - MakeAttribute(kFloat2Key, std::array<float, 2>{{1.0f, 2.0f}}), - MakeAttribute(kFloat3Key, std::array<float, 3>{{3.0f, 4.0f, 5.0f}}), - MakeAttribute(kFloat4Key, std::array<float, 4>{{6.0f, 7.0f, 8.0f, 9.0f}}), - MakeAttribute(kFloat8Key, - std::array<float, 8>{{10.0f, 11.0f, 12.0f, 13.0f, 14.0f, - 15.0f, 16.0f, 17.0f}}), - MakeAttribute(kFloat16Key, std::array<float, 16>{ - {18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f, - 24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f, - 30.0f, 31.0f, 32.0f, 33.0f}})}; - - EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), - attributes_to_set.size())); - - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - auto attribute_status = manager_->GetAttributes(0); - ASSERT_STATUS_OK(attribute_status); - auto attributes = attribute_status.take(); - EXPECT_GE(attributes.size(), attributes_to_set.size()); - - auto HasAttribute = [](const auto& attributes, - DvrSurfaceAttributeKey key) -> bool { - for (const auto& attribute : attributes) { - if (attribute.key == key) - return true; - } - return false; - }; - auto AttributeType = - [](const auto& attributes, - DvrSurfaceAttributeKey key) -> DvrSurfaceAttributeType { - for (const auto& attribute : attributes) { - if (attribute.key == key) - return attribute.value.type; - } - return DVR_SURFACE_ATTRIBUTE_TYPE_NONE; - }; - - ASSERT_TRUE(HasAttribute(attributes, kInt32Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT32, - AttributeType(attributes, kInt32Key)); - - ASSERT_TRUE(HasAttribute(attributes, kInt64Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT64, - AttributeType(attributes, kInt64Key)); - - ASSERT_TRUE(HasAttribute(attributes, kBoolKey)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, - AttributeType(attributes, kBoolKey)); - - ASSERT_TRUE(HasAttribute(attributes, kFloatKey)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT, - AttributeType(attributes, kFloatKey)); - - ASSERT_TRUE(HasAttribute(attributes, kFloat2Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2, - AttributeType(attributes, kFloat2Key)); - - ASSERT_TRUE(HasAttribute(attributes, kFloat3Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3, - AttributeType(attributes, kFloat3Key)); - - ASSERT_TRUE(HasAttribute(attributes, kFloat4Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4, - AttributeType(attributes, kFloat4Key)); - - ASSERT_TRUE(HasAttribute(attributes, kFloat8Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8, - AttributeType(attributes, kFloat8Key)); - - ASSERT_TRUE(HasAttribute(attributes, kFloat16Key)); - EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16, - AttributeType(attributes, kFloat16Key)); -} - -TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { - // Create an application surface. - auto surface_status = CreateApplicationSurface(); - ASSERT_STATUS_OK(surface_status); - UniqueDvrSurface surface = surface_status.take(); - ASSERT_NE(nullptr, surface.get()); - - const int surface_id = dvrSurfaceGetId(surface.get()); - ASSERT_GE(surface_id, 0); - // Get surface state and verify there is one surface. - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); - - // Verify there are no queues for the surface recorded in the state - // snapshot. - EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0)); - - // Create a new queue in the surface. - auto write_queue_status = CreateSurfaceQueue( - surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1, - AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0); - ASSERT_STATUS_OK(write_queue_status); - UniqueDvrWriteBufferQueue write_queue = write_queue_status.take(); - ASSERT_NE(nullptr, write_queue.get()); - - const int queue_id = dvrWriteBufferQueueGetId(write_queue.get()); - ASSERT_GE(queue_id, 0); - - // Update surface state. - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); - - // Verify the buffers changed flag is set. - auto check_flags = [](const auto& value) { - return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED; - }; - EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); - - auto queue_ids_status = manager_->GetQueueIds(0); - ASSERT_STATUS_OK(queue_ids_status); - - auto queue_ids = queue_ids_status.take(); - ASSERT_EQ(1u, queue_ids.size()); - EXPECT_EQ(queue_id, queue_ids[0]); - - auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id); - ASSERT_STATUS_OK(read_queue_status); - UniqueDvrReadBufferQueue read_queue = read_queue_status.take(); - ASSERT_NE(nullptr, read_queue.get()); - EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get())); - - write_queue.reset(); - - // Verify that destroying the queue generates a surface update event. - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); - - // Verify that the buffers changed flag is set. - EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); - - // Verify that the queue ids reflect the change. - queue_ids_status = manager_->GetQueueIds(0); - ASSERT_STATUS_OK(queue_ids_status); - - queue_ids = queue_ids_status.take(); - ASSERT_EQ(0u, queue_ids.size()); -} - -TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) { - // Create an application surface. - auto surface_status = CreateApplicationSurface(); - ASSERT_STATUS_OK(surface_status); - UniqueDvrSurface surface = surface_status.take(); - ASSERT_NE(nullptr, surface.get()); - - // Get surface state and verify there is one surface. - ASSERT_STATUS_OK(manager_->WaitForUpdate()); - ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); - - // Create a new queue in the surface. - const uint32_t kLayerCount = 3; - auto write_queue_status = CreateSurfaceQueue( - surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount, - AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0); - ASSERT_STATUS_OK(write_queue_status); - UniqueDvrWriteBufferQueue write_queue = write_queue_status.take(); - ASSERT_NE(nullptr, write_queue.get()); - - DvrWriteBuffer* buffer = nullptr; - DvrNativeBufferMetadata metadata; - int fence_fd = -1; - int error = dvrWriteBufferQueueGainBuffer(write_queue.get(), /*timeout=*/1000, - &buffer, &metadata, &fence_fd); - ASSERT_EQ(0, error); - - AHardwareBuffer* hardware_buffer = nullptr; - error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer); - ASSERT_EQ(0, error); - - AHardwareBuffer_Desc desc = {}; - AHardwareBuffer_describe(hardware_buffer, &desc); - ASSERT_EQ(kLayerCount, desc.layers); - - AHardwareBuffer_release(hardware_buffer); - dvrWriteBufferDestroy(buffer); -} - -TEST_F(Test, ConfigurationData) { - // TODO(hendrikw): Move this test and GetConfigData helper function out of the - // display manager tests. - auto data1 = GetConfigData(-1); - ASSERT_STATUS_ERROR(data1); - - const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics"; - - // This should be run on devices with and without built in metrics. - bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty(); - auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS); - if (has_metric) { - ASSERT_STATUS_OK(data2); - ASSERT_NE(0u, data2.get().size()); - } else { - ASSERT_STATUS_ERROR(data2); - } -} - -} // namespace - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp deleted file mode 100644 index 5c837e74ea..0000000000 --- a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include <android/hardware_buffer.h> -#include <dvr/dvr_buffer.h> -#include <dvr/dvr_config.h> -#include <dvr/dvr_shared_buffers.h> -#include <dvr/dvr_surface.h> -#include <system/graphics.h> - -#include <gtest/gtest.h> - -namespace android { -namespace dvr { - -namespace { - -TEST(DvrGlobalBufferTest, TestGlobalBuffersSameName) { - const DvrGlobalBufferKey buffer_key = 101; - DvrBuffer* buffer1 = nullptr; - int ret1 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer1); - ASSERT_EQ(0, ret1); - ASSERT_NE(nullptr, buffer1); - - DvrBuffer* buffer2 = nullptr; - int ret2 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer2); - ASSERT_EQ(0, ret2); - ASSERT_NE(nullptr, buffer2); - - AHardwareBuffer* hardware_buffer1 = nullptr; - int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1); - ASSERT_EQ(0, e1); - ASSERT_NE(nullptr, hardware_buffer1); - - AHardwareBuffer* hardware_buffer2 = nullptr; - int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2); - ASSERT_EQ(0, e2); - ASSERT_NE(nullptr, hardware_buffer2); - - AHardwareBuffer_Desc desc1 = {}; - AHardwareBuffer_describe(hardware_buffer1, &desc1); - AHardwareBuffer_Desc desc2 = {}; - AHardwareBuffer_describe(hardware_buffer2, &desc2); - ASSERT_EQ(desc1.width, 10u); - ASSERT_EQ(desc1.height, 1u); - ASSERT_EQ(desc1.layers, 1u); - ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB); - ASSERT_EQ(desc1.usage, 0u); - ASSERT_EQ(desc2.width, 10u); - ASSERT_EQ(desc2.height, 1u); - ASSERT_EQ(desc2.layers, 1u); - ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB); - ASSERT_EQ(desc2.usage, 0u); - - dvrBufferDestroy(buffer1); - dvrBufferDestroy(buffer2); - - DvrBuffer* buffer3 = nullptr; - int e3 = dvrGetGlobalBuffer(buffer_key, &buffer3); - ASSERT_NE(nullptr, buffer3); - ASSERT_EQ(0, e3); - - AHardwareBuffer* hardware_buffer3 = nullptr; - int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3); - ASSERT_EQ(0, e4); - ASSERT_NE(nullptr, hardware_buffer3); - - AHardwareBuffer_Desc desc3 = {}; - AHardwareBuffer_describe(hardware_buffer3, &desc3); - ASSERT_EQ(desc3.width, 10u); - ASSERT_EQ(desc3.height, 1u); - ASSERT_EQ(desc3.layers, 1u); - ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB); - ASSERT_EQ(desc3.usage, 0u); - - dvrBufferDestroy(buffer3); - - AHardwareBuffer_release(hardware_buffer1); - AHardwareBuffer_release(hardware_buffer2); - AHardwareBuffer_release(hardware_buffer3); -} - -TEST(DvrGlobalBufferTest, TestMultipleGlobalBuffers) { - const DvrGlobalBufferKey buffer_key1 = 102; - const DvrGlobalBufferKey buffer_key2 = 103; - DvrBuffer* setup_buffer1 = nullptr; - int ret1 = dvrSetupGlobalBuffer(buffer_key1, 10, 0, &setup_buffer1); - ASSERT_EQ(0, ret1); - ASSERT_NE(nullptr, setup_buffer1); - dvrBufferDestroy(setup_buffer1); - - DvrBuffer* setup_buffer2 = nullptr; - int ret2 = dvrSetupGlobalBuffer(buffer_key2, 10, 0, &setup_buffer2); - ASSERT_EQ(0, ret2); - ASSERT_NE(nullptr, setup_buffer2); - dvrBufferDestroy(setup_buffer2); - - DvrBuffer* buffer1 = nullptr; - int e1 = dvrGetGlobalBuffer(buffer_key1, &buffer1); - ASSERT_NE(nullptr, buffer1); - ASSERT_EQ(0, e1); - dvrBufferDestroy(buffer1); - - DvrBuffer* buffer2 = nullptr; - int e2 = dvrGetGlobalBuffer(buffer_key2, &buffer2); - ASSERT_NE(nullptr, buffer2); - ASSERT_EQ(0, e2); - dvrBufferDestroy(buffer2); -} - -TEST(DvrGlobalBufferTest, TestGlobalBufferUsage) { - const DvrGlobalBufferKey buffer_key = 100; - - // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because - // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to - // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different. - // If all is good, when we get the AHardwareBuffer, it should be converted - // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. - const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE; - - DvrBuffer* setup_buffer = nullptr; - int e1 = dvrSetupGlobalBuffer(buffer_key, 10, usage, &setup_buffer); - ASSERT_NE(nullptr, setup_buffer); - ASSERT_EQ(0, e1); - - AHardwareBuffer* hardware_buffer = nullptr; - int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); - ASSERT_EQ(0, e2); - ASSERT_NE(nullptr, hardware_buffer); - - AHardwareBuffer_Desc desc = {}; - AHardwareBuffer_describe(hardware_buffer, &desc); - ASSERT_EQ(usage, desc.usage); - - dvrBufferDestroy(setup_buffer); - AHardwareBuffer_release(hardware_buffer); -} - -TEST(DvrGlobalBufferTest, TestGlobalBufferCarriesData) { - const DvrGlobalBufferKey buffer_name = 110; - - uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; - constexpr size_t size = 1024 * sizeof(uint64_t); - constexpr uint64_t value = 0x123456787654321; - - { - // Allocate some data and set it to something. - DvrBuffer* setup_buffer = nullptr; - int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer); - ASSERT_NE(nullptr, setup_buffer); - ASSERT_EQ(0, e1); - - AHardwareBuffer* hardware_buffer = nullptr; - int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); - ASSERT_EQ(0, e2); - ASSERT_NE(nullptr, hardware_buffer); - - void* buffer; - int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer); - ASSERT_EQ(0, e3); - ASSERT_NE(nullptr, buffer); - // Verify that the buffer pointer is at least 16 byte aligned. - ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1)); - - uint64_t* data = static_cast<uint64_t*>(buffer); - constexpr size_t num_values = size / sizeof(uint64_t); - for (size_t i = 0; i < num_values; ++i) { - data[i] = value; - } - - int32_t fence = -1; - int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence); - ASSERT_EQ(0, e4); - - dvrBufferDestroy(setup_buffer); - AHardwareBuffer_release(hardware_buffer); - } - - { - // Get the buffer and check that all the data is still present. - DvrBuffer* setup_buffer = nullptr; - int e1 = dvrGetGlobalBuffer(buffer_name, &setup_buffer); - ASSERT_NE(nullptr, setup_buffer); - ASSERT_EQ(0, e1); - - AHardwareBuffer* hardware_buffer = nullptr; - int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); - ASSERT_EQ(0, e2); - ASSERT_NE(nullptr, hardware_buffer); - - void* buffer; - int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer); - ASSERT_EQ(0, e3); - ASSERT_NE(nullptr, buffer); - // Verify that the buffer pointer is at least 16 byte aligned. - ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1)); - - uint64_t* data = static_cast<uint64_t*>(buffer); - constexpr size_t num_values = size / sizeof(uint64_t); - bool is_equal = true; - for (size_t i = 0; i < num_values; ++i) { - is_equal &= (data[i] == value); - } - ASSERT_TRUE(is_equal); - - int32_t fence = -1; - int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence); - ASSERT_EQ(0, e4); - - dvrBufferDestroy(setup_buffer); - AHardwareBuffer_release(hardware_buffer); - } -} - -TEST(DvrGlobalBufferTest, TestGlobalBufferZeroed) { - const DvrGlobalBufferKey buffer_name = 120; - - // Allocate 1MB and check that it is all zeros. - uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; - constexpr size_t size = 1024 * 1024; - DvrBuffer* setup_buffer = nullptr; - int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer); - ASSERT_NE(nullptr, setup_buffer); - ASSERT_EQ(0, e1); - - AHardwareBuffer* hardware_buffer = nullptr; - int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); - ASSERT_EQ(0, e2); - ASSERT_NE(nullptr, hardware_buffer); - - void* buffer; - int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer); - ASSERT_EQ(0, e3); - ASSERT_NE(nullptr, buffer); - // Verify that the buffer pointer is at least 16 byte aligned. - ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1)); - - uint64_t* data = static_cast<uint64_t*>(buffer); - constexpr size_t num_values = size / sizeof(uint64_t); - uint64_t zero = 0; - for (size_t i = 0; i < num_values; ++i) { - zero |= data[i]; - } - ASSERT_EQ(0U, zero); - - int32_t fence = -1; - int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence); - ASSERT_EQ(0, e4); - - dvrBufferDestroy(setup_buffer); - AHardwareBuffer_release(hardware_buffer); -} - -TEST(DvrGlobalBufferTest, TestVrflingerConfigBuffer) { - const DvrGlobalBufferKey buffer_name = - DvrGlobalBuffers::kVrFlingerConfigBufferKey; - - // First delete any existing buffer so we can test the failure case. - dvrDeleteGlobalBuffer(buffer_name); - - const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | - AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY; - - size_t correct_size = DvrConfigRing::MemorySize(); - size_t wrong_size = DvrConfigRing::MemorySize(0); - - // Setup an invalid config buffer (too small) and assert that it fails. - DvrBuffer* setup_buffer = nullptr; - int e1 = dvrSetupGlobalBuffer(buffer_name, wrong_size, usage, &setup_buffer); - ASSERT_EQ(nullptr, setup_buffer); - ASSERT_GT(0, e1); - - // Setup a correct config buffer. - int e2 = - dvrSetupGlobalBuffer(buffer_name, correct_size, usage, &setup_buffer); - ASSERT_NE(nullptr, setup_buffer); - ASSERT_EQ(0, e2); - - dvrBufferDestroy(setup_buffer); -} - -} // namespace - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp deleted file mode 100644 index 3b6d6e1ec4..0000000000 --- a/libs/vr/libdvr/tests/dvr_tracking-test.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include <android/log.h> -#include <gtest/gtest.h> - -#include "dvr_api_test.h" - -namespace { - -class DvrTrackingTest : public DvrApiTest {}; - -#if DVR_TRACKING_IMPLEMENTED - -TEST_F(DvrTrackingTest, Implemented) { - ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); - ASSERT_TRUE(api_.TrackingCameraStart != nullptr); - ASSERT_TRUE(api_.TrackingCameraStop != nullptr); - - ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); -} - -TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) { - int ret; - ret = api_.TrackingCameraCreate(nullptr); - EXPECT_EQ(ret, -EINVAL); - - DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42); - ret = api_.TrackingCameraCreate(&camera); - EXPECT_EQ(ret, -EINVAL); -} - -TEST_F(DvrTrackingTest, CameraCreateDestroy) { - DvrTrackingCamera* camera = nullptr; - int ret = api_.TrackingCameraCreate(&camera); - - EXPECT_EQ(ret, 0); - ASSERT_TRUE(camera != nullptr); - - api_.TrackingCameraDestroy(camera); -} - -TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) { - int ret; - ret = api_.TrackingFeatureExtractorCreate(nullptr); - EXPECT_EQ(ret, -EINVAL); - - DvrTrackingFeatureExtractor* camera = - reinterpret_cast<DvrTrackingFeatureExtractor*>(42); - ret = api_.TrackingFeatureExtractorCreate(&camera); - EXPECT_EQ(ret, -EINVAL); -} - -TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) { - DvrTrackingFeatureExtractor* camera = nullptr; - int ret = api_.TrackingFeatureExtractorCreate(&camera); - - EXPECT_EQ(ret, 0); - ASSERT_TRUE(camera != nullptr); - - api_.TrackingFeatureExtractorDestroy(camera); -} - -#else // !DVR_TRACKING_IMPLEMENTED - -TEST_F(DvrTrackingTest, NotImplemented) { - ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); - ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr); - ASSERT_TRUE(api_.TrackingCameraStart != nullptr); - ASSERT_TRUE(api_.TrackingCameraStop != nullptr); - - EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS); - EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS); - EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS); - - ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); - ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); - - EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS); - EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr), - -ENOSYS); - EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS); - EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr, - nullptr, nullptr), - -ENOSYS); - - ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr); - ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr); - ASSERT_TRUE(api_.TrackingSensorsStart != nullptr); - ASSERT_TRUE(api_.TrackingSensorsStop != nullptr); - - EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS); - EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS); - EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS); -} - -#endif // DVR_TRACKING_IMPLEMENTED - -} // namespace diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp index c1f6da3b10..c95603bcf7 100644 --- a/libs/vr/libpdx/Android.bp +++ b/libs/vr/libpdx/Android.bp @@ -16,7 +16,6 @@ cc_library_headers { cc_library_static { name: "libpdx", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -42,7 +41,6 @@ cc_library_static { cc_test { name: "pdx_tests", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -72,7 +70,6 @@ cc_test { // Code analysis target. cc_test { name: "pdx_encoder_performance_test", - clang: true, cflags: [ "-Wall", "-Wextra", diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp index cc32b1822b..ac831ceda0 100644 --- a/libs/vr/libpdx/fuzz/Android.bp +++ b/libs/vr/libpdx/fuzz/Android.bp @@ -9,7 +9,6 @@ package { cc_fuzz { name: "libpdx_service_dispatcher_fuzzer", - clang: true, srcs: [ "service_dispatcher_fuzzer.cpp", ], @@ -24,13 +23,12 @@ cc_fuzz { shared_libs: [ "libutils", "liblog", - "libcutils" + "libcutils", ], } cc_fuzz { name: "libpdx_message_fuzzer", - clang: true, srcs: [ "message_fuzzer.cpp", ], @@ -45,13 +43,12 @@ cc_fuzz { shared_libs: [ "libutils", "liblog", - "libcutils" + "libcutils", ], } cc_fuzz { name: "libpdx_serialization_fuzzer", - clang: true, srcs: [ "serialization_fuzzer.cpp", ], @@ -66,6 +63,6 @@ cc_fuzz { shared_libs: [ "libutils", "liblog", - "libcutils" + "libcutils", ], } diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index 804685747e..a5758b589f 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -9,7 +9,6 @@ package { cc_defaults { name: "pdx_default_transport_compiler_defaults", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -26,7 +25,10 @@ cc_defaults { cc_defaults { name: "pdx_use_transport_servicefs", export_include_dirs: ["private/servicefs"], - whole_static_libs: ["libpdx_servicefs", "libservicefs"], + whole_static_libs: [ + "libpdx_servicefs", + "libservicefs", + ], } cc_defaults { diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp index 216ca9f236..7f88dafc81 100644 --- a/libs/vr/libpdx_uds/Android.bp +++ b/libs/vr/libpdx_uds/Android.bp @@ -9,7 +9,6 @@ package { cc_library_static { name: "libpdx_uds", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -41,7 +40,6 @@ cc_library_static { cc_test { name: "libpdx_uds_tests", - clang: true, cflags: [ "-Wall", "-Wextra", diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp deleted file mode 100644 index 40a5099177..0000000000 --- a/libs/vr/libvrsensor/Android.bp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2015 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"], -} - -sourceFiles = [ - "pose_client.cpp", - "latency_model.cpp", -] - -includeFiles = [ - "include", -] - -staticLibraries = [ - "libdisplay", - "libdvrcommon", - "libbroadcastring", -] - -sharedLibraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libhardware", - "liblog", - "libutils", - "libui", - "libpdx_default_transport", -] - -cc_library { - srcs: sourceFiles, - cflags: [ - "-Wall", - "-Werror", - "-Wno-macro-redefined", - ], - export_include_dirs: includeFiles, - static_libs: staticLibraries, - shared_libs: sharedLibraries, - header_libs: ["libdvr_headers"], - name: "libvrsensor", -} diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg deleted file mode 100644 index 2f8a3c018c..0000000000 --- a/libs/vr/libvrsensor/include/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -filter=-build/header_guard diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h deleted file mode 100644 index b663a67cea..0000000000 --- a/libs/vr/libvrsensor/include/dvr/pose_client.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef ANDROID_DVR_POSE_CLIENT_H_ -#define ANDROID_DVR_POSE_CLIENT_H_ - -#ifdef __ARM_NEON -#include <arm_neon.h> -#else -#ifndef __FLOAT32X4T_86 -#define __FLOAT32X4T_86 -typedef float float32x4_t __attribute__ ((__vector_size__ (16))); -typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t; -#endif -#endif - -#include <stdbool.h> -#include <stdint.h> - -#include <dvr/dvr_pose.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct DvrPoseClient DvrPoseClient; - -// Returned by the async pose ring buffer access API. -typedef struct DvrPoseRingBufferInfo { - // Read-only pointer to the pose ring buffer. The current pose is in this - // buffer at element buffer[current_frame & (buffer_size - 1)]. The next - // frame's forecasted pose is at element - // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are - // predicted for when 50% of the corresponding frame's pixel data is visible - // to the user. - // The last value returned by dvrPresent is the count for the next frame, - // which is the earliest that the application could display something if they - // were to render promptly. (TODO(jbates) move this comment to dvrPresent). - volatile const DvrPoseAsync* buffer; - // Minimum number of accurate forecasted poses including the current frame's - // pose. This is the number of poses that are udpated by the pose service. - // If the application reads past this count, they will get a stale prediction - // from a previous frame. Guaranteed to be at least 2. - uint32_t min_future_count; - // Number of elements in buffer. At least 8 and greater than min_future_count. - // Guaranteed to be a power of two. The total size of the buffer in bytes is: - // total_count * sizeof(DvrPoseAsync) - uint32_t total_count; -} DvrPoseRingBufferInfo; - -typedef enum DvrPoseMode { - DVR_POSE_MODE_6DOF = 0, - DVR_POSE_MODE_3DOF, - DVR_POSE_MODE_MOCK_FROZEN, - DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW, - DVR_POSE_MODE_MOCK_HEAD_TURN_FAST, - DVR_POSE_MODE_MOCK_ROTATE_SLOW, - DVR_POSE_MODE_MOCK_ROTATE_MEDIUM, - DVR_POSE_MODE_MOCK_ROTATE_FAST, - DVR_POSE_MODE_MOCK_CIRCLE_STRAFE, - DVR_POSE_MODE_FLOAT, - DVR_POSE_MODE_MOCK_MOTION_SICKNESS, - - // Always last. - DVR_POSE_MODE_COUNT, -} DvrPoseMode; - -typedef enum DvrControllerId { - DVR_CONTROLLER_0 = 0, - DVR_CONTROLLER_1 = 1, -} DvrControllerId; - -// Creates a new pose client. -// -// @return Pointer to the created pose client, nullptr on failure. -DvrPoseClient* dvrPoseClientCreate(); - -// Destroys a pose client. -// -// @param client Pointer to the pose client to be destroyed. -void dvrPoseClientDestroy(DvrPoseClient* client); - -// Gets the pose for the given vsync count. -// -// @param client Pointer to the pose client. -// @param vsync_count Vsync that this pose should be forward-predicted to. -// Typically this is the count returned by dvrGetNextVsyncCount. -// @param out_pose Struct to store pose state. -// @return Zero on success, negative error code on failure. -int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count, - DvrPoseAsync* out_pose); - -// Gets the current vsync count. -uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client); - -// Gets the pose for the given controller at the given vsync count. -// -// @param client Pointer to the pose client. -// @param controller_id The controller id. -// @param vsync_count Vsync that this pose should be forward-predicted to. -// Typically this is the count returned by dvrGetNextVsyncCount. -// @param out_pose Struct to store pose state. -// @return Zero on success, negative error code on failure. -int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id, - uint32_t vsync_count, DvrPoseAsync* out_pose); - -// Enables/disables logging for the controller fusion. -// -// @param client Pointer to the pose client. -// @param enable True starts logging, False stops. -// @return Zero on success, negative error code on failure. -int dvrPoseClientLogController(DvrPoseClient* client, bool enable); - -// DEPRECATED -// Polls current pose state. -// -// @param client Pointer to the pose client. -// @param state Struct to store polled state. -// @return Zero on success, negative error code on failure. -int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state); - -// Freezes the pose to the provided state. -// -// Future poll operations will return this state until a different state is -// frozen or dvrPoseClientModeSet() is called with a different mode. The timestamp is -// not frozen. -// -// @param client Pointer to the pose client. -// @param frozen_state State pose to be frozen to. -// @return Zero on success, negative error code on failure. -int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state); - -// Sets the pose service mode. -// -// @param mode The requested pose mode. -// @return Zero on success, negative error code on failure. -int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode); - -// Gets the pose service mode. -// -// @param mode Return value for the current pose mode. -// @return Zero on success, negative error code on failure. -int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode); - -// Get access to the shared memory pose ring buffer. -// A future pose at vsync <current> + <offset> is accessed at index: -// index = (<current> + <offset>) % out_buffer_size -// Where <current> was the last value returned by dvrPresent and -// <offset> is less than or equal to |out_min_future_count|. -// |out_buffer| will be set to a pointer to the buffer. -// |out_fd| will be set to the gralloc buffer file descriptor, which is -// required for binding this buffer for GPU use. -// Returns 0 on success. -int dvrPoseClientGetRingBuffer(DvrPoseClient* client, - DvrPoseRingBufferInfo* out_info); - -// Sets enabled state for sensors pose processing. -// -// @param enabled Whether sensors are enabled or disabled. -// @return Zero on success -int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled); - -// Requests a burst of data samples from pose service. The data samples are -// passed through a shared memory buffer obtained by calling -// dvrPoseClientGetDataReader(). -// -// @param DvrPoseDataCaptureRequest Parameters on how to capture data. -// @return Zero on success. -int dvrPoseClientDataCapture(DvrPoseClient* client, - const DvrPoseDataCaptureRequest* request); - -// Destroys the write buffer queue for the given |data_type|. -int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // ANDROID_DVR_POSE_CLIENT_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h deleted file mode 100644 index bf0e687b7f..0000000000 --- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ANDROID_DVR_LATENCY_MODEL_H_ -#define ANDROID_DVR_LATENCY_MODEL_H_ - -#include <vector> - -namespace android { -namespace dvr { - -// This class models the latency from sensors. It will look at the first -// window_size measurements and return their average after that. -class LatencyModel { - public: - explicit LatencyModel(size_t window_size); - ~LatencyModel() = default; - - void AddLatency(int64_t latency_ns); - int64_t CurrentLatencyEstimate() const { return latency_; } - - private: - size_t window_size_; - int64_t latency_sum_ = 0; - size_t num_summed_ = 0; - int64_t latency_ = 0; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_LATENCY_MODEL_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h deleted file mode 100644 index 7bf1cd4d29..0000000000 --- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ANDROID_DVR_POSE_IPC_H_ -#define ANDROID_DVR_POSE_IPC_H_ - -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define DVR_POSE_SERVICE_BASE "system/vr/pose" -#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client") - -enum { - DVR_POSE_FREEZE = 0, - DVR_POSE_SET_MODE, - DVR_POSE_GET_MODE, - DVR_POSE_GET_CONTROLLER_RING_BUFFER, - DVR_POSE_LOG_CONTROLLER, - DVR_POSE_SENSORS_ENABLE, - DVR_POSE_GET_TANGO_READER, - DVR_POSE_DATA_CAPTURE, - DVR_POSE_TANGO_READER_DESTROY, -}; - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // ANDROID_DVR_POSE_IPC_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h deleted file mode 100644 index 39592bb15d..0000000000 --- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ -#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ - -#include <private/dvr/buffer_hub_queue_client.h> - -using android::dvr::ConsumerQueue; - -typedef struct DvrPoseClient DvrPoseClient; - -namespace android { -namespace dvr { - -int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type, - ConsumerQueue **queue_out); - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp deleted file mode 100644 index d3a45210a7..0000000000 --- a/libs/vr/libvrsensor/latency_model.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <private/dvr/latency_model.h> - -#include <cmath> - -namespace android { -namespace dvr { - -LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {} - -void LatencyModel::AddLatency(int64_t latency_ns) { - // Not enough samples yet? - if (num_summed_ < window_size_) { - // Accumulate. - latency_sum_ += latency_ns; - - // Have enough samples for latency estimate? - if (++num_summed_ == window_size_) { - latency_ = latency_sum_ / window_size_; - } - } -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp deleted file mode 100644 index 4ff6a0912c..0000000000 --- a/libs/vr/libvrsensor/pose_client.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#define LOG_TAG "PoseClient" -#include <dvr/dvr_shared_buffers.h> -#include <dvr/pose_client.h> - -#include <stdint.h> - -#include <log/log.h> -#include <pdx/client.h> -#include <pdx/default_transport/client_channel_factory.h> -#include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/display_client.h> -#include <private/dvr/pose-ipc.h> -#include <private/dvr/shared_buffer_helpers.h> - -using android::dvr::ConsumerQueue; -using android::pdx::LocalHandle; -using android::pdx::LocalChannelHandle; -using android::pdx::Status; -using android::pdx::Transaction; - -namespace android { -namespace dvr { -namespace { - -typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing; - -constexpr static int32_t MAX_CONTROLLERS = 2; -} // namespace - -// PoseClient is a remote interface to the pose service in sensord. -class PoseClient : public pdx::ClientBase<PoseClient> { - public: - ~PoseClient() override {} - - // Casts C handle into an instance of this class. - static PoseClient* FromC(DvrPoseClient* client) { - return reinterpret_cast<PoseClient*>(client); - } - - // Polls the pose service for the current state and stores it in *state. - // Returns zero on success, a negative error code otherwise. - int Poll(DvrPose* state) { - // Allocate the helper class to access the sensor pose buffer. - if (sensor_pose_buffer_ == nullptr) { - sensor_pose_buffer_ = std::make_unique<SensorPoseRing>( - DvrGlobalBuffers::kSensorPoseBuffer, CPUUsageMode::READ_RARELY); - } - - if (state) { - if (sensor_pose_buffer_->GetNewest(state)) { - return 0; - } else { - return -EAGAIN; - } - } - - return -EINVAL; - } - - int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) { - const auto vsync_buffer = GetVsyncBuffer(); - if (vsync_buffer) { - *out_pose = - vsync_buffer - ->vsync_poses[vsync_count & DvrVsyncPoseBuffer::kIndexMask]; - return 0; - } else { - return -EAGAIN; - } - } - - uint32_t GetVsyncCount() { - const auto vsync_buffer = GetVsyncBuffer(); - if (vsync_buffer) { - return vsync_buffer->vsync_count; - } - - return 0; - } - - int GetControllerPose(int32_t controller_id, uint32_t vsync_count, - DvrPoseAsync* out_pose) { - if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) { - return -EINVAL; - } - if (!controllers_[controller_id].mapped_pose_buffer) { - int ret = GetControllerRingBuffer(controller_id); - if (ret < 0) - return ret; - } - *out_pose = - controllers_[controller_id] - .mapped_pose_buffer[vsync_count & DvrVsyncPoseBuffer::kIndexMask]; - return 0; - } - - int LogController(bool enable) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable, - sizeof(enable), nullptr, 0); - ALOGE_IF(!status, "Pose LogController() failed because: %s", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Freezes the pose to the provided state. Future poll operations will return - // this state until a different state is frozen or SetMode() is called with a - // different mode. - // Returns zero on success, a negative error code otherwise. - int Freeze(const DvrPose& frozen_state) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state, - sizeof(frozen_state), nullptr, 0); - ALOGE_IF(!status, "Pose Freeze() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Sets the data mode for the pose service. - int SetMode(DvrPoseMode mode) { - Transaction trans{*this}; - Status<int> status = - trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0); - ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Gets the data mode for the pose service. - int GetMode(DvrPoseMode* out_mode) { - int mode; - Transaction trans{*this}; - Status<int> status = - trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode)); - ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s", - status.GetErrorMessage().c_str()); - if (status) - *out_mode = DvrPoseMode(mode); - return ReturnStatusOrError(status); - } - - int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) { - // Get buffer. - Transaction trans{*this}; - Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>( - DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0); - - if (!status) { - ALOGE("PoseClient GetTangoReaderHandle() failed because: %s", - status.GetErrorMessage().c_str()); - *queue_out = nullptr; - return -status.error(); - } - - std::unique_ptr<ConsumerQueue> consumer_queue = - ConsumerQueue::Import(status.take()); - *queue_out = consumer_queue.release(); - return 0; - } - - int DataCapture(const DvrPoseDataCaptureRequest* request) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request, - sizeof(*request), nullptr, 0); - ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - int DataReaderDestroy(uint64_t data_type) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY, - &data_type, sizeof(data_type), nullptr, - 0); - ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Enables or disables all pose processing from sensors - int EnableSensors(bool enabled) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_SENSORS_ENABLE, &enabled, - sizeof(enabled), nullptr, 0); - ALOGE_IF(!status, "Pose EnableSensors() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - int GetRingBuffer(DvrPoseRingBufferInfo* out_info) { - // First time mapping the buffer? - const auto vsync_buffer = GetVsyncBuffer(); - if (vsync_buffer) { - if (out_info) { - out_info->min_future_count = DvrVsyncPoseBuffer::kMinFutureCount; - out_info->total_count = DvrVsyncPoseBuffer::kSize; - out_info->buffer = vsync_buffer->vsync_poses; - } - return -EINVAL; - } - - return -EAGAIN; - } - - int GetControllerRingBuffer(int32_t controller_id) { - if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) { - return -EINVAL; - } - ControllerClientState& client_state = controllers_[controller_id]; - if (client_state.pose_buffer.get()) { - return 0; - } - - Transaction trans{*this}; - Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>( - DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id, - sizeof(controller_id), nullptr, 0); - if (!status) { - return -status.error(); - } - - auto buffer = ConsumerBuffer::Import(status.take()); - if (!buffer) { - ALOGE("Pose failed to import ring buffer"); - return -EIO; - } - constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync); - void* addr = nullptr; - int ret = buffer->GetBlobReadWritePointer(size, &addr); - if (ret < 0 || !addr) { - ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr); - return -EIO; - } - client_state.pose_buffer.swap(buffer); - client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr); - ALOGI( - "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f", - controller_id, client_state.mapped_pose_buffer[0].position[0], - client_state.mapped_pose_buffer[0].position[1], - client_state.mapped_pose_buffer[0].position[2], - client_state.mapped_pose_buffer[0].orientation[0], - client_state.mapped_pose_buffer[0].orientation[1], - client_state.mapped_pose_buffer[0].orientation[2], - client_state.mapped_pose_buffer[0].orientation[3]); - return 0; - } - - private: - friend BASE; - - // Set up a channel to the pose service. - PoseClient() - : BASE(pdx::default_transport::ClientChannelFactory::Create( - DVR_POSE_SERVICE_CLIENT)) { - // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't - // block while waiting for the pose service to come back up. - EnableAutoReconnect(kInfiniteTimeout); - } - - PoseClient(const PoseClient&) = delete; - PoseClient& operator=(const PoseClient&) = delete; - - const DvrVsyncPoseBuffer* GetVsyncBuffer() { - if (mapped_vsync_pose_buffer_ == nullptr) { - if (vsync_pose_buffer_ == nullptr) { - // The constructor tries mapping it so we do not need TryMapping after. - vsync_pose_buffer_ = std::make_unique<CPUMappedBuffer>( - DvrGlobalBuffers::kVsyncPoseBuffer, CPUUsageMode::READ_OFTEN); - } else if (vsync_pose_buffer_->IsMapped() == false) { - vsync_pose_buffer_->TryMapping(); - } - - if (vsync_pose_buffer_->IsMapped()) { - mapped_vsync_pose_buffer_ = - static_cast<DvrVsyncPoseBuffer*>(vsync_pose_buffer_->Address()); - } - } - - return mapped_vsync_pose_buffer_; - } - - // The vsync pose buffer if already mapped. - std::unique_ptr<CPUMappedBuffer> vsync_pose_buffer_; - - // The direct sensor pose buffer. - std::unique_ptr<SensorPoseRing> sensor_pose_buffer_; - - const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr; - - struct ControllerClientState { - std::unique_ptr<ConsumerBuffer> pose_buffer; - const DvrPoseAsync* mapped_pose_buffer = nullptr; - }; - ControllerClientState controllers_[MAX_CONTROLLERS]; -}; - -int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type, - ConsumerQueue** queue_out) { - return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out); -} - -} // namespace dvr -} // namespace android - -using android::dvr::PoseClient; - -extern "C" { - -DvrPoseClient* dvrPoseClientCreate() { - auto* client = PoseClient::Create().release(); - return reinterpret_cast<DvrPoseClient*>(client); -} - -void dvrPoseClientDestroy(DvrPoseClient* client) { - delete PoseClient::FromC(client); -} - -int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count, - DvrPoseAsync* out_pose) { - return PoseClient::FromC(client)->GetPose(vsync_count, out_pose); -} - -uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client) { - return PoseClient::FromC(client)->GetVsyncCount(); -} - -int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id, - uint32_t vsync_count, DvrPoseAsync* out_pose) { - return PoseClient::FromC(client)->GetControllerPose(controller_id, - vsync_count, out_pose); -} - -int dvrPoseClientLogController(DvrPoseClient* client, bool enable) { - return PoseClient::FromC(client)->LogController(enable); -} - -int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state) { - return PoseClient::FromC(client)->Poll(state); -} - -int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state) { - return PoseClient::FromC(client)->Freeze(*frozen_state); -} - -int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode) { - return PoseClient::FromC(client)->SetMode(mode); -} - -int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) { - return PoseClient::FromC(client)->GetMode(mode); -} - -int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) { - return PoseClient::FromC(client)->EnableSensors(enabled); -} - -int dvrPoseClientDataCapture(DvrPoseClient* client, - const DvrPoseDataCaptureRequest* request) { - return PoseClient::FromC(client)->DataCapture(request); -} - -int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) { - return PoseClient::FromC(client)->DataReaderDestroy(data_type); -} - -} // extern "C" diff --git a/opengl/OWNERS b/opengl/OWNERS index a47fb9a573..379f7638f0 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -6,7 +6,6 @@ ianelliott@google.com jessehall@google.com lfy@google.com lpy@google.com -timvp@google.com romanl@google.com vantablack@google.com yuxinhu@google.com diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index c9fce8ad52..62cf2555ca 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -12,7 +12,10 @@ cc_library { name: "libETC1", srcs: ["ETC1/etc1.cpp"], host_supported: true, - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], target: { android: { @@ -37,6 +40,9 @@ ndk_library { symbol_file: "libEGL.map.txt", first_version: "9", unversioned_until: "current", + export_header_libs: [ + "libEGL_headers", + ], } ndk_library { @@ -44,6 +50,9 @@ ndk_library { symbol_file: "libGLESv1_CM.map.txt", first_version: "9", unversioned_until: "current", + export_header_libs: [ + "libGLESv1_CM_headers", + ], } ndk_library { @@ -51,6 +60,9 @@ ndk_library { symbol_file: "libGLESv2.map.txt", first_version: "9", unversioned_until: "current", + export_header_libs: [ + "libGLESv2_headers", + ], } ndk_library { @@ -58,6 +70,9 @@ ndk_library { symbol_file: "libGLESv3.map.txt", first_version: "18", unversioned_until: "current", + export_header_libs: [ + "libGLESv3_headers", + ], } cc_defaults { @@ -108,7 +123,6 @@ cc_defaults { // In particular, DO NOT add libutils nor anything "above" libui "libgraphicsenv", "libnativewindow", - "libbacktrace", "libbase", ], } @@ -165,12 +179,17 @@ cc_library_shared { "libnativeloader_lazy", "libutils", "libSurfaceFlingerProp", + "libunwindstack", ], static_libs: [ "libEGL_getProcAddress", "libEGL_blobCache", ], - ldflags: ["-Wl,--exclude-libs=ALL,--Bsymbolic-functions"], + ldflags: [ + "-Wl,--exclude-libs=libEGL_getProcAddress.a", + "-Wl,--exclude-libs=libEGL_blobCache.a", + "-Wl,--Bsymbolic-functions", + ], export_include_dirs: ["EGL/include"], stubs: { symbol_file: "libEGL.map.txt", diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index 86c788d3b3..aecfc6b077 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -231,7 +231,7 @@ int BlobCache::flatten(void* buffer, size_t size) const { int BlobCache::unflatten(void const* buffer, size_t size) { // All errors should result in the BlobCache being in an empty state. - mCacheEntries.clear(); + clear(); // Read the cache header if (size < sizeof(Header)) { @@ -258,7 +258,7 @@ int BlobCache::unflatten(void const* buffer, size_t size) { size_t numEntries = header->mNumEntries; for (size_t i = 0; i < numEntries; i++) { if (byteOffset + sizeof(EntryHeader) > size) { - mCacheEntries.clear(); + clear(); ALOGE("unflatten: not enough room for cache entry headers"); return -EINVAL; } @@ -270,7 +270,7 @@ int BlobCache::unflatten(void const* buffer, size_t size) { size_t totalSize = align4(entrySize); if (byteOffset + totalSize > size) { - mCacheEntries.clear(); + clear(); ALOGE("unflatten: not enough room for cache entry headers"); return -EINVAL; } diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h index ff03d30099..52078ff5fd 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -117,7 +117,10 @@ public: // clear flushes out all contents of the cache then the BlobCache, leaving // it in an empty state. - void clear() { mCacheEntries.clear(); } + void clear() { + mCacheEntries.clear(); + mTotalSize = 0; + } protected: // mMaxTotalSize is the maximum size that all cache entries can occupy. This diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp index ceea0fb979..450c12837b 100644 --- a/opengl/libs/EGL/BlobCache_test.cpp +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -466,4 +466,31 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); } +// Test for a divide by zero bug (b/239862516). Before the fix, unflatten() would not reset +// mTotalSize when it encountered an error, which would trigger division by 0 in clean() in the +// right conditions. +TEST_F(BlobCacheFlattenTest, SetAfterFailedUnflatten) { + // isCleanable() must be true, so mTotalSize must be > mMaxTotalSize / 2 after unflattening + // after one entry is lost. To make this the case, MaxTotalSize is 30 and three 10 sized + // entries are used. One of those entries is lost, resulting in mTotalSize=20 + const size_t kMaxKeySize = 10; + const size_t kMaxValueSize = 10; + const size_t kMaxTotalSize = 30; + mBC.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize)); + mBC2.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize)); + mBC->set("aaaaa", 5, "aaaaa", 5); + mBC->set("bbbbb", 5, "bbbbb", 5); + mBC->set("ccccc", 5, "ccccc", 5); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size - 10)); + delete[] flat; + + // This line will trigger clean() which caused a crash. + mBC2->set("dddddddddd", 10, "dddddddddd", 10); +} + } // namespace android diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h index b7fdf97cbe..96437c37d9 100644 --- a/opengl/libs/EGL/CallStack.h +++ b/opengl/libs/EGL/CallStack.h @@ -16,8 +16,8 @@ #pragma once -#include <backtrace/Backtrace.h> #include <log/log.h> +#include <unwindstack/AndroidUnwinder.h> #include <memory> @@ -26,12 +26,15 @@ public: // Create a callstack with the current thread's stack trace. // Immediately dump it to logcat using the given logtag. static void log(const char* logtag) noexcept { - std::unique_ptr<Backtrace> backtrace( - Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - if (backtrace->Unwind(2)) { - for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) { + unwindstack::AndroidLocalUnwinder unwinder; + unwindstack::AndroidUnwinderData data; + if (unwinder.Unwind(data)) { + for (size_t i = 2, c = data.frames.size(); i < c; i++) { + auto& frame = data.frames[i]; + // Trim the first two frames. + frame.num -= 2; __android_log_print(ANDROID_LOG_DEBUG, logtag, "%s", - backtrace->FormatFrameData(i).c_str()); + unwinder.FormatFrame(frame).c_str()); } } } diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 76fd7f0f3f..dd14bcfb55 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -207,7 +207,8 @@ void* Loader::open(egl_connection_t* cnx) ATRACE_CALL(); const nsecs_t openTime = systemTime(); - if (should_unload_system_driver(cnx)) { + if (!android::GraphicsEnv::getInstance().angleIsSystemDriver() && + should_unload_system_driver(cnx)) { unload_system_driver(cnx); } @@ -216,8 +217,13 @@ void* Loader::open(egl_connection_t* cnx) return cnx->dso; } - // Firstly, try to load ANGLE driver. - driver_t* hnd = attempt_to_load_angle(cnx); + // Firstly, try to load ANGLE driver, unless we know that we shouldn't. + bool shouldForceLegacyDriver = android::GraphicsEnv::getInstance().shouldForceLegacyDriver(); + driver_t* hnd = nullptr; + if (!shouldForceLegacyDriver) { + hnd = attempt_to_load_angle(cnx); + } + if (!hnd) { // Secondly, try to load from driver apk. hnd = attempt_to_load_updated_driver(cnx); @@ -230,21 +236,29 @@ void* Loader::open(egl_connection_t* cnx) LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s", android::GraphicsEnv::getInstance().getDriverPath().c_str()); } - // Finally, try to load system driver, start by searching for the library name appended by - // the system properties of the GLES userspace driver in both locations. - // i.e.: - // libGLES_${prop}.so, or: - // libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so - for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { - auto prop = base::GetProperty(key, ""); - if (prop.empty()) { - continue; - } - hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true); - if (hnd) { - break; - } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) { - failToLoadFromDriverSuffixProperty = true; + // Finally, try to load system driver. If ANGLE is the system driver + // (i.e. we are forcing the legacy system driver instead of ANGLE), use + // the driver suffix that was passed down from above. + if (shouldForceLegacyDriver) { + std::string suffix = android::GraphicsEnv::getInstance().getLegacySuffix(); + hnd = attempt_to_load_system_driver(cnx, suffix.c_str(), true); + } else { + // Start by searching for the library name appended by the system + // properties of the GLES userspace driver in both locations. + // i.e.: + // libGLES_${prop}.so, or: + // libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so + for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { + auto prop = base::GetProperty(key, ""); + if (prop.empty()) { + continue; + } + hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true); + if (hnd) { + break; + } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) { + failToLoadFromDriverSuffixProperty = true; + } } } } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index f4dbe499eb..7619a5071c 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1453,7 +1453,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri setError(EGL_BAD_SURFACE, EGL_FALSE); } int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (attribute == EGL_TIMESTAMPS_ANDROID) { @@ -1463,7 +1467,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri return EGL_TRUE; } int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (s->setSmpte2086Attribute(attribute, value)) { diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index b3d6f74e86..e0ba946260 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -118,70 +118,27 @@ namespace android { : "rax", "cc" \ ); -#elif defined(__mips64) - - #define API_ENTRY(_api) __attribute__((noinline)) _api - - #define CALL_GL_EXTENSION_API(_api, ...) \ - register unsigned int _t0 asm("$12"); \ - register unsigned int _fn asm("$25"); \ - register unsigned int _tls asm("$3"); \ - asm volatile( \ - ".set push\n\t" \ - ".set noreorder\n\t" \ - "rdhwr %[tls], $29\n\t" \ - "ld %[t0], %[OPENGL_API](%[tls])\n\t" \ - "beqz %[t0], 1f\n\t" \ - " move %[fn], $ra\n\t" \ - "ld %[t0], %[API](%[t0])\n\t" \ - "beqz %[t0], 1f\n\t" \ - " nop\n\t" \ - "move %[fn], %[t0]\n\t" \ - "1:\n\t" \ - "jalr $0, %[fn]\n\t" \ - " nop\n\t" \ - ".set pop\n\t" \ - : [fn] "=c"(_fn), \ - [tls] "=&r"(_tls), \ - [t0] "=&r"(_t0) \ - : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \ - [API] "I"(__builtin_offsetof(gl_hooks_t, \ - ext.extensions[_api])) \ - : \ - ); +#elif defined(__riscv) + #define API_ENTRY(_api) __attribute__((noinline)) _api -#elif defined(__mips__) - - #define API_ENTRY(_api) __attribute__((noinline)) _api - - #define CALL_GL_EXTENSION_API(_api, ...) \ - register unsigned int _t0 asm("$8"); \ - register unsigned int _fn asm("$25"); \ - register unsigned int _tls asm("$3"); \ - asm volatile( \ - ".set push\n\t" \ - ".set noreorder\n\t" \ - ".set mips32r2\n\t" \ - "rdhwr %[tls], $29\n\t" \ - "lw %[t0], %[OPENGL_API](%[tls])\n\t" \ - "beqz %[t0], 1f\n\t" \ - " move %[fn], $ra\n\t" \ - "lw %[t0], %[API](%[t0])\n\t" \ - "beqz %[t0], 1f\n\t" \ - " nop\n\t" \ - "move %[fn], %[t0]\n\t" \ - "1:\n\t" \ - "jalr $0, %[fn]\n\t" \ - " nop\n\t" \ - ".set pop\n\t" \ - : [fn] "=c"(_fn), \ - [tls] "=&r"(_tls), \ - [t0] "=&r"(_t0) \ - : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \ - [API] "I"(__builtin_offsetof(gl_hooks_t, \ - ext.extensions[_api])) \ - : \ - ); + #define CALL_GL_EXTENSION_API(_api) \ + asm volatile( \ + "mv t0, tp\n" \ + "li t1, %[tls]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "beqz t0, 1f\n" \ + "li t1, %[api]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "jalr x0, t0\n" \ + "1: ret\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, \ + ext.extensions[_api])) \ + : "t0", "t1" \ + ); #endif diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index 65f50f54fb..5bd5c141b6 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -191,73 +191,44 @@ using namespace android; : \ ); -#elif defined(__mips64) +#elif defined(__riscv) #define API_ENTRY(_api) __attribute__((naked,noinline)) _api - // t0: $12 - // fn: $25 - // tls: $3 - // v0: $2 - #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ - asm volatile( \ - ".set push\n\t" \ - ".set noreorder\n\t" \ - "rdhwr $3, $29\n\t" \ - "ld $12, %[OPENGL_API]($3)\n\t" \ - "beqz $12, 1f\n\t" \ - " move $25, $ra\n\t" \ - "ld $12, %[API]($12)\n\t" \ - "beqz $12, 1f\n\t" \ - " nop\n\t" \ - "move $25, $12\n\t" \ - "1:\n\t" \ - "jalr $0, $25\n\t" \ - " move $2, $0\n\t" \ - ".set pop\n\t" \ - : \ - : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\ - [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \ - : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", \ - "$10", "$11", "$12", "$25" \ - ); - - #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE - #define CALL_GL_API_INTERNAL_DO_RETURN - -#elif defined(__mips__) - - #define API_ENTRY(_api) __attribute__((naked,noinline)) _api - - // t0: $8 - // fn: $25 - // tls: $3 - // v0: $2 #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ asm volatile( \ - ".set push\n\t" \ - ".set noreorder\n\t" \ - ".set mips32r2\n\t" \ - "rdhwr $3, $29\n\t" \ - "lw $3, %[OPENGL_API]($3)\n\t" \ - "beqz $3, 1f\n\t" \ - " move $25,$ra\n\t" \ - "lw $3, %[API]($3)\n\t" \ - "beqz $3, 1f\n\t" \ - " nop\n\t" \ - "move $25, $3\n\t" \ - "1:\n\t" \ - "jalr $0, $25\n\t" \ - " move $2, $0\n\t" \ - ".set pop\n\t" \ + "mv t0, tp\n" \ + "li t1, %[tls]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "beqz t0, 1f\n" \ + "li t1, %[api]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "jalr x0, t0\n" \ + "1:\n" \ : \ - : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \ - [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \ - : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25" \ + : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \ + [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \ + "a5", "t6", "t3", "t4", "t5", "t6" \ ); - #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE - #define CALL_GL_API_INTERNAL_DO_RETURN + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "li a0, 0\n" \ + : \ + : \ + : "a0" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "ret\n" \ + : \ + : \ + : \ + ); #endif diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp index bacd4b4bc8..64c0f97708 100644 --- a/opengl/libs/GLES_CM/gl.cpp +++ b/opengl/libs/GLES_CM/gl.cpp @@ -247,73 +247,44 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, : \ ); -#elif defined(__mips64) +#elif defined(__riscv) #define API_ENTRY(_api) __attribute__((naked,noinline)) _api - // t0: $12 - // fn: $25 - // tls: $3 - // v0: $2 - #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ - asm volatile( \ - ".set push\n\t" \ - ".set noreorder\n\t" \ - "rdhwr $3, $29\n\t" \ - "ld $12, %[OPENGL_API]($3)\n\t" \ - "beqz $12, 1f\n\t" \ - " move $25, $ra\n\t" \ - "ld $12, %[API]($12)\n\t" \ - "beqz $12, 1f\n\t" \ - " nop\n\t" \ - "move $25, $12\n\t" \ - "1:\n\t" \ - "jalr $0, $25\n\t" \ - " move $2, $0\n\t" \ - ".set pop\n\t" \ - : \ - : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\ - [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \ - : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", \ - "$10", "$11", "$12", "$25" \ - ); - - #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE - #define CALL_GL_API_INTERNAL_DO_RETURN - -#elif defined(__mips__) - - #define API_ENTRY(_api) __attribute__((naked,noinline)) _api - - // t0: $8 - // fn: $25 - // tls: $3 - // v0: $2 #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ asm volatile( \ - ".set push\n\t" \ - ".set noreorder\n\t" \ - ".set mips32r2\n\t" \ - "rdhwr $3, $29\n\t" \ - "lw $3, %[OPENGL_API]($3)\n\t" \ - "beqz $3, 1f\n\t" \ - " move $25,$ra\n\t" \ - "lw $3, %[API]($3)\n\t" \ - "beqz $3, 1f\n\t" \ - " nop\n\t" \ - "move $25, $3\n\t" \ - "1:\n\t" \ - "jalr $0, $25\n\t" \ - " move $2, $0\n\t" \ - ".set pop\n\t" \ + "mv t0, tp\n" \ + "li t1, %[tls]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "beqz t0, 1f\n" \ + "li t1, %[api]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "jalr x0, t0\n" \ + "1:\n" \ : \ - : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \ - [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \ - : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25" \ + : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \ + [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \ + "a5", "t6", "t3", "t4", "t5", "t6" \ ); - #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE - #define CALL_GL_API_INTERNAL_DO_RETURN + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "li a0, 0\n" \ + : \ + : \ + : "a0" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "ret\n" \ + : \ + : \ + : \ + ); #endif diff --git a/opengl/tests/gl2_cameraeye/AndroidManifest.xml b/opengl/tests/gl2_cameraeye/AndroidManifest.xml index c53f7be0b0..a4674e129d 100644 --- a/opengl/tests/gl2_cameraeye/AndroidManifest.xml +++ b/opengl/tests/gl2_cameraeye/AndroidManifest.xml @@ -26,7 +26,7 @@ <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:glEsVersion="0x00020000" /> <application android:label="@string/gl2cameraeye_name"> - <activity android:name="GL2CameraEye"> + <activity android:name="GL2CameraEye" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> diff --git a/opengl/tests/gl2_java/AndroidManifest.xml b/opengl/tests/gl2_java/AndroidManifest.xml index 8bb6840a16..500adb5c14 100644 --- a/opengl/tests/gl2_java/AndroidManifest.xml +++ b/opengl/tests/gl2_java/AndroidManifest.xml @@ -22,7 +22,8 @@ <activity android:name="GL2JavaActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/opengl/tests/gl2_jni/AndroidManifest.xml b/opengl/tests/gl2_jni/AndroidManifest.xml index 1827e5f377..b4ce99b102 100644 --- a/opengl/tests/gl2_jni/AndroidManifest.xml +++ b/opengl/tests/gl2_jni/AndroidManifest.xml @@ -21,7 +21,8 @@ <activity android:name="GL2JNIActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/opengl/tests/gl_jni/AndroidManifest.xml b/opengl/tests/gl_jni/AndroidManifest.xml index 5d0ec966f4..bedab56659 100644 --- a/opengl/tests/gl_jni/AndroidManifest.xml +++ b/opengl/tests/gl_jni/AndroidManifest.xml @@ -24,7 +24,8 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml index 6c23d422f5..d766be9ed5 100644 --- a/opengl/tests/lighting1709/AndroidManifest.xml +++ b/opengl/tests/lighting1709/AndroidManifest.xml @@ -2,7 +2,7 @@ package="com.android.lightingtest"> <application> - <activity android:name="ClearActivity" android:label="LightingTest"> + <activity android:name="ClearActivity" android:label="LightingTest" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/opengl/tests/testPauseResume/AndroidManifest.xml b/opengl/tests/testPauseResume/AndroidManifest.xml index 1879bc3217..ae82a8286a 100644 --- a/opengl/tests/testPauseResume/AndroidManifest.xml +++ b/opengl/tests/testPauseResume/AndroidManifest.xml @@ -24,7 +24,8 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/rustfmt.toml b/rustfmt.toml new file mode 120000 index 0000000000..ee92d9efc5 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +../../build/soong/scripts/rustfmt.toml
\ No newline at end of file diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp index 72bd29254d..614a78ecc7 100644 --- a/services/automotive/display/Android.bp +++ b/services/automotive/display/Android.bp @@ -53,4 +53,6 @@ cc_binary { vintf_fragments: [ "manifest_android.frameworks.automotive.display@1.0.xml", ], + + system_ext_specific: true, } diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc index 5c7f344486..ea1077a555 100644 --- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc +++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc @@ -1,4 +1,4 @@ -service automotive_display /system/bin/android.frameworks.automotive.display@1.0-service +service automotive_display /system_ext/bin/android.frameworks.automotive.display@1.0-service class hal user graphics group automotive_evs diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h index 178bc29510..bf6189d7af 100644 --- a/services/batteryservice/include/batteryservice/BatteryService.h +++ b/services/batteryservice/include/batteryservice/BatteryService.h @@ -34,6 +34,10 @@ enum { BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS + BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY + BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE + BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE + BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH }; struct BatteryProperties { diff --git a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h index 8a90a12f2b..2d7072d3c2 100644 --- a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h +++ b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h @@ -1,7 +1,5 @@ -// This file is autogenerated by hidl-gen. Do not edit manually. - -#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_ -#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_ +#ifndef AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_ +#define AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_ #ifdef __cplusplus extern "C" { @@ -15,6 +13,8 @@ enum { BATTERY_STATUS_FULL = 5, }; +// must be kept in sync with definitions in +// hardware/interfaces/health/aidl/android/hardware/health/BatteryHealth.aidl enum { BATTERY_HEALTH_UNKNOWN = 1, BATTERY_HEALTH_GOOD = 2, @@ -23,10 +23,23 @@ enum { BATTERY_HEALTH_OVER_VOLTAGE = 5, BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, BATTERY_HEALTH_COLD = 7, + BATTERY_HEALTH_FAIR = 8, + BATTERY_HEALTH_NOT_AVAILABLE = 11, + BATTERY_HEALTH_INCONSISTENT = 12, +}; + +// must be kept in sync with definitions in +// hardware/interfaces/health/aidl/android/hardware/health/BatteryChargingState.aidl +enum { + BATTERY_STATUS_NORMAL = 1, + BATTERY_STATUS_TOO_COLD = 2, + BATTERY_STATUS_TOO_HOT = 3, + BATTERY_STATUS_LONG_LIFE = 4, + BATTERY_STATUS_ADAPTIVE = 5, }; #ifdef __cplusplus } #endif -#endif // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_ +#endif // AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_ diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index 5b4ee21b42..fba64c7569 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -99,7 +99,7 @@ cc_binary { init_rc: ["gpuservice.rc"], required: [ "bpfloader", - "gpu_mem.o", + "gpuMem.o", ], srcs: [":gpuservice_binary_sources"], shared_libs: [ diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk index 482fc6dfc3..c51f6aa5f0 100644 --- a/services/gpuservice/CleanSpec.mk +++ b/services/gpuservice/CleanSpec.mk @@ -44,9 +44,9 @@ #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) -# Remove gpu_mem.o +# Remove gpuMem.o $(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpuMem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpuMem.o_gpuMem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpuMem.o) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpuMem.o-timestamp) diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp index 076affda5a..680b291fe3 100644 --- a/services/gpuservice/bpfprogs/Android.bp +++ b/services/gpuservice/bpfprogs/Android.bp @@ -22,8 +22,8 @@ package { } bpf { - name: "gpu_mem.o", - srcs: ["gpu_mem.c"], + name: "gpuMem.o", + srcs: ["gpuMem.c"], btf: true, cflags: [ "-Wall", diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpuMem.c index 16e1e8a1ef..16e1e8a1ef 100644 --- a/services/gpuservice/bpfprogs/gpu_mem.c +++ b/services/gpuservice/bpfprogs/gpuMem.c diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h index de691e2b75..7588b54818 100644 --- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h +++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h @@ -57,9 +57,9 @@ private: static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total"; // pinned gpu memory total bpf c program path in bpf sysfs static constexpr char kGpuMemTotalProgPath[] = - "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"; + "/sys/fs/bpf/prog_gpuMem_tracepoint_gpu_mem_gpu_mem_total"; // pinned gpu memory total bpf map path in bpf sysfs - static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"; + static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map"; // 30 seconds timeout for trying to attach bpf program to tracepoint static constexpr int kGpuWaitTimeout = 30; }; diff --git a/services/gpuservice/gpuwork/Android.bp b/services/gpuservice/gpuwork/Android.bp index a9a59bc023..e20404044a 100644 --- a/services/gpuservice/gpuwork/Android.bp +++ b/services/gpuservice/gpuwork/Android.bp @@ -55,6 +55,6 @@ cc_library_shared { ], required: [ "bpfloader", - "gpu_work.o", + "gpuWork.o", ], } diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp index 974ae38b13..fd703239e9 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -42,7 +42,7 @@ #include <unordered_set> #include <vector> -#include "gpuwork/gpu_work.h" +#include "gpuwork/gpuWork.h" #define ONE_MS_IN_NS (10000000) @@ -128,11 +128,11 @@ void GpuWork::initialize() { { std::lock_guard<std::mutex> lock(mMutex); - if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_map", &mGpuWorkMap)) { + if (!getBpfMap("/sys/fs/bpf/map_gpuWork_gpu_work_map", &mGpuWorkMap)) { return; } - if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_global_data", &mGpuWorkGlobalDataMap)) { + if (!getBpfMap("/sys/fs/bpf/map_gpuWork_gpu_work_global_data", &mGpuWorkGlobalDataMap)) { return; } @@ -140,7 +140,7 @@ void GpuWork::initialize() { } // Attach the tracepoint. - if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power", + if (!attachTracepoint("/sys/fs/bpf/prog_gpuWork_tracepoint_power_gpu_work_period", "power", "gpu_work_period")) { return; } diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp index b3c4eff847..fe45c98494 100644 --- a/services/gpuservice/gpuwork/bpfprogs/Android.bp +++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp @@ -17,8 +17,8 @@ package { } bpf { - name: "gpu_work.o", - srcs: ["gpu_work.c"], + name: "gpuWork.o", + srcs: ["gpuWork.c"], cflags: [ "-Wall", "-Werror", diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c index d73fff4e39..f4701896b6 100644 --- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c +++ b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "include/gpuwork/gpu_work.h" +#include "include/gpuwork/gpuWork.h" #include <linux/bpf.h> #include <stddef.h> diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpuWork.h index 2affb60a0c..2affb60a0c 100644 --- a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h +++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpuWork.h diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h index acecd569aa..cece9999c6 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -27,7 +27,7 @@ #include <functional> #include <thread> -#include "gpuwork/gpu_work.h" +#include "gpuwork/gpuWork.h" namespace android { namespace gpuwork { diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 4fb0d2e734..86f6c7febb 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -35,6 +35,7 @@ cc_test { header_libs: ["bpf_headers"], shared_libs: [ "libbase", + "libbinder", "libbpf_bcc", "libcutils", "libgfxstats", diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp index e916221c2e..8dabe4fbdb 100644 --- a/services/gpuservice/tests/unittests/GpuMemTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "gpuservice_unittest" #include <android-base/stringprintf.h> +#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING #include <bpf/BpfMap.h> #include <gmock/gmock.h> #include <gpumem/GpuMem.h> @@ -65,11 +66,11 @@ public: mTestableGpuMem = TestableGpuMem(mGpuMem.get()); mTestableGpuMem.setInitialized(); errno = 0; - mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, - BPF_F_NO_PREALLOC); + mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, + TEST_MAP_SIZE, + BPF_F_NO_PREALLOC)); EXPECT_EQ(0, errno); - EXPECT_LE(0, mTestMap.getMap().get()); EXPECT_TRUE(mTestMap.isValid()); } @@ -89,8 +90,8 @@ TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) { EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(), - "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"); - EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"); + "/sys/fs/bpf/prog_gpuMem_tracepoint_gpu_mem_gpu_mem_total"); + EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map"); } TEST_F(GpuMemTest, bpfInitializationFailed) { diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp index d76f039a6d..5c042102b2 100644 --- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "gpuservice_unittest" +#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING #include <bpf/BpfMap.h> #include <gpumem/GpuMem.h> #include <gtest/gtest.h> @@ -64,11 +65,11 @@ public: mTestableGpuMem = TestableGpuMem(mGpuMem.get()); errno = 0; - mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, - BPF_F_NO_PREALLOC); + mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, + TEST_MAP_SIZE, + BPF_F_NO_PREALLOC)); EXPECT_EQ(0, errno); - EXPECT_LE(0, mTestMap.getMap().get()); EXPECT_TRUE(mTestMap.isValid()); } diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp index 20c8ccf9bf..7ea22888f8 100644 --- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp +++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp @@ -18,12 +18,14 @@ #define LOG_TAG "gpuservice_unittest" #include <unistd.h> +#include <binder/ProcessState.h> #include <cutils/properties.h> #include <gmock/gmock.h> #include <gpustats/GpuStats.h> #include <gtest/gtest.h> #include <stats_pull_atom_callback.h> #include <statslog.h> +#include <utils/Looper.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -61,8 +63,9 @@ enum InputCommand : int32_t { // clang-format on class GpuStatsTest : public testing::Test { + sp<android::Looper> looper; public: - GpuStatsTest() { + GpuStatsTest() : looper(Looper::prepare(0 /* opts */)) { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); @@ -72,6 +75,16 @@ public: const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + + // This is required for test due to GpuStats instance spawns binder transactions + // in its destructor. After the gtest destructor test exits immidiatelly. + // It results in binder thread not able to process above binder transactions and memory leak + // occures. Binder thread needs time to process callbacks transactions. + // It leads to GpuStats instance destructor needs to be called in advance. + mGpuStats.reset(nullptr); + // performs all pending callbacks until all data has been consumed + // gives time to process binder transactions by thread pool + looper->pollAll(1000); } std::string inputCommand(InputCommand cmd); @@ -79,6 +92,10 @@ public: void SetUp() override { mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0); mGlesVersion = property_get_int32("ro.opengles.version", 0); + + // start the thread pool + sp<ProcessState> ps(ProcessState::self()); + ps->startThreadPool(); } std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>(); diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 41878e3487..18d670ae38 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -147,6 +147,7 @@ cc_defaults { srcs: [":libinputflinger_base_sources"], shared_libs: [ "libbase", + "libbinder", "libcutils", "libinput", "liblog", diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS index 82c6ee12c7..c88bfe97ca 100644 --- a/services/inputflinger/OWNERS +++ b/services/inputflinger/OWNERS @@ -1,3 +1 @@ -lzye@google.com -michaelwr@google.com -svv@google.com +include platform/frameworks/base:/INPUT_OWNERS diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index b4b617e188..8d5d8839e8 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -15,6 +15,9 @@ "name": "inputflinger_tests" }, { + "name": "libchrome-gestures_test" + }, + { "name": "libpalmrejection_test" }, { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4ad9f42cdb..caf7101e25 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3641,6 +3641,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + const bool wasEmpty = connection->outboundQueue.empty(); + for (size_t i = 0; i < cancelationEvents.size(); i++) { std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]); switch (cancelationEventEntry->type) { @@ -3675,7 +3677,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( InputTarget::FLAG_DISPATCH_AS_IS); } - startDispatchCycleLocked(currentTime, connection); + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty && !connection->outboundQueue.empty()) { + startDispatchCycleLocked(currentTime, connection); + } } void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( @@ -3709,6 +3714,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( target.inputChannel = connection->inputChannel; target.flags = targetFlags; + const bool wasEmpty = connection->outboundQueue.empty(); + for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { switch (downEventEntry->type) { case EventEntry::Type::MOTION: { @@ -3734,8 +3741,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target, InputTarget::FLAG_DISPATCH_AS_IS); } - - startDispatchCycleLocked(currentTime, connection); + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty && !connection->outboundQueue.empty()) { + startDispatchCycleLocked(currentTime, connection); + } } void InputDispatcher::synthesizeCancelationEventsForWindowLocked( @@ -4731,10 +4740,13 @@ void InputDispatcher::setInputWindowsLocked( updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); - if (mLastHoverWindowHandle && - std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == - windowHandles.end()) { - mLastHoverWindowHandle = nullptr; + if (mLastHoverWindowHandle) { + const WindowInfo* lastHoverWindowInfo = mLastHoverWindowHandle->getInfo(); + if (lastHoverWindowInfo->displayId == displayId && + std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == + windowHandles.end()) { + mLastHoverWindowHandle = nullptr; + } } std::optional<FocusResolver::FocusChanges> changes = diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 3bd3275bb1..01146a3c8b 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -71,7 +71,7 @@ cc_defaults { "libstatslog", "libui", "libutils", - "PlatformProperties", + "libPlatformProperties", ], static_libs: [ "libc++fs", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index d6a6bd214e..20baa42a20 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -452,8 +452,7 @@ bool EventHub::Device::hasKeycodeLocked(int keycode) const { return false; } - std::vector<int32_t> scanCodes; - keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + std::vector<int32_t> scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode); const size_t N = scanCodes.size(); for (size_t i = 0; i < N && i <= KEY_MAX; i++) { int32_t sc = scanCodes[i]; @@ -548,10 +547,10 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } - int32_t scanCode; - if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) { - *outScanCode = scanCode; + std::optional<int32_t> scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led); + if (scanCode.has_value()) { + if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) { + *outScanCode = *scanCode; return NO_ERROR; } } @@ -865,8 +864,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); + std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode); if (scanCodes.size() != 0) { if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { for (size_t i = 0; i < scanCodes.size(); i++) { @@ -890,8 +888,8 @@ int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKey device->keyMap.keyLayoutMap == nullptr) { return AKEYCODE_UNKNOWN; } - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes); + std::vector<int32_t> scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode); if (scanCodes.empty()) { ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input" "device %d", @@ -960,20 +958,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], - &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (device->keyBitmask.test(scanCodes[sc])) { - outFlags[codeIndex] = 1; - break; - } + std::vector<int32_t> scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]); + + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (device->keyBitmask.test(scanCodes[sc])) { + outFlags[codeIndex] = 1; + break; } } } @@ -1027,14 +1021,15 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } + if (device == nullptr || !device->keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; } - - return NAME_NOT_FOUND; + std::optional<AxisInfo> info = device->keyMap.keyLayoutMap->mapAxis(scanCode); + if (!info.has_value()) { + return NAME_NOT_FOUND; + } + *outAxisInfo = *info; + return NO_ERROR; } base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId, @@ -1314,7 +1309,8 @@ static std::string generateDescriptor(InputDeviceIdentifier& identifier) { if (!identifier.uniqueId.empty()) { rawDescriptor += "uniqueId:"; rawDescriptor += identifier.uniqueId; - } else if (identifier.nonce != 0) { + } + if (identifier.nonce != 0) { rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce); } @@ -1342,16 +1338,20 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { // of Android. In practice we sometimes get devices that cannot be uniquely // identified. In this case we enforce uniqueness between connected devices. // Ideally, we also want the descriptor to be short and relatively opaque. + // Note that we explicitly do not use the path or location for external devices + // as their path or location will change as they are plugged/unplugged or moved + // to different ports. We do fallback to using name and location in the case of + // internal devices which are detected by the vendor and product being 0 in + // generateDescriptor. If two identical descriptors are detected we will fallback + // to using a 'nonce' and incrementing it until the new descriptor no longer has + // a match with any existing descriptors. identifier.nonce = 0; std::string rawDescriptor = generateDescriptor(identifier); - if (identifier.uniqueId.empty()) { - // If it didn't have a unique id check for conflicts and enforce - // uniqueness if necessary. - while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { - identifier.nonce++; - rawDescriptor = generateDescriptor(identifier); - } + // Enforce that the generated descriptor is unique. + while (hasDeviceWithDescriptorLocked(identifier.descriptor)) { + identifier.nonce++; + rawDescriptor = generateDescriptor(identifier); } ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(), identifier.descriptor.c_str()); @@ -1426,13 +1426,22 @@ std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) { return vibrators; } -EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const { +/** + * Checks both mDevices and mOpeningDevices for a device with the descriptor passed. + */ +bool EventHub::hasDeviceWithDescriptorLocked(const std::string& descriptor) const { + for (const auto& device : mOpeningDevices) { + if (descriptor == device->identifier.descriptor) { + return true; + } + } + for (const auto& [id, device] : mDevices) { if (descriptor == device->identifier.descriptor) { - return device.get(); + return true; } } - return nullptr; + return false; } EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { @@ -1476,25 +1485,35 @@ EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const { } std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const { - std::scoped_lock _l(mLock); + std::filesystem::path batteryPath; + { + // Do not read the sysfs node to get the battery state while holding + // the EventHub lock. For some peripheral devices, reading battery state + // can be broken and take 5+ seconds. Holding the lock in this case would + // block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and read the sysfs node outside + // the lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + + const auto infos = getBatteryInfoLocked(deviceId); + auto it = infos.find(batteryId); + if (it == infos.end()) { + return std::nullopt; + } + batteryPath = it->second.path; + } // release lock - const auto infos = getBatteryInfoLocked(deviceId); - auto it = infos.find(batteryId); - if (it == infos.end()) { - return std::nullopt; - } std::string buffer; // Some devices report battery capacity as an integer through the "capacity" file - if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY), + if (base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::CAPACITY), &buffer)) { return std::stoi(base::Trim(buffer)); } // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX // These values are taken from kernel source code include/linux/power_supply.h - if (base::ReadFileToString(it->second.path / - BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL), + if (base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL), &buffer)) { // Remove any white space such as trailing new line const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer)); @@ -1507,15 +1526,27 @@ std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t ba } std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const { - std::scoped_lock _l(mLock); - const auto infos = getBatteryInfoLocked(deviceId); - auto it = infos.find(batteryId); - if (it == infos.end()) { - return std::nullopt; - } + std::filesystem::path batteryPath; + { + // Do not read the sysfs node to get the battery state while holding + // the EventHub lock. For some peripheral devices, reading battery state + // can be broken and take 5+ seconds. Holding the lock in this case would + // block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and read the sysfs node outside + // the lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + + const auto infos = getBatteryInfoLocked(deviceId); + auto it = infos.find(batteryId); + if (it == infos.end()) { + return std::nullopt; + } + batteryPath = it->second.path; + } // release lock + std::string buffer; - if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS), + if (!base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::STATUS), &buffer)) { ALOGE("Failed to read sysfs battery info: %s", strerror(errno)); return std::nullopt; diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 989700f6cf..3f26aaaebe 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -552,14 +552,6 @@ void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) { for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); }); } -std::optional<int32_t> InputDevice::getBatteryCapacity() { - return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt; -} - -std::optional<int32_t> InputDevice::getBatteryStatus() { - return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt; -} - bool InputDevice::setLightColor(int32_t lightId, int32_t color) { return mController ? mController->setLightColor(lightId, color) : false; } @@ -627,6 +619,10 @@ void InputDevice::updateLedState(bool reset) { for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); }); } +std::optional<int32_t> InputDevice::getBatteryEventHubId() const { + return mController ? std::make_optional(mController->getEventHubId()) : std::nullopt; +} + InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId) : mDevice(device), mContext(device.getContext()), diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 9bcf463c36..905b348caa 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -38,6 +38,26 @@ using android::base::StringPrintf; namespace android { +/** + * Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices + * that expose multiple input device paths such a keyboard that also has a touchpad input. + * These are separate devices with unique descriptors in EventHub, but InputReader should + * create a single InputDevice for them. + * Sub-devices are detected by the following criteria: + * 1. The vendor, product, bus, version, and unique id match + * 2. The location matches. The location is used to distinguish a single device with multiple + * inputs versus the same device plugged into multiple ports. + */ + +static bool isSubDevice(const InputDeviceIdentifier& identifier1, + const InputDeviceIdentifier& identifier2) { + return (identifier1.vendor == identifier2.vendor && + identifier1.product == identifier2.product && identifier1.bus == identifier2.bus && + identifier1.version == identifier2.version && + identifier1.uniqueId == identifier2.uniqueId && + identifier1.location == identifier2.location); +} + // --- InputReader --- InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, @@ -271,8 +291,9 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { std::shared_ptr<InputDevice> InputReader::createDeviceLocked( int32_t eventHubId, const InputDeviceIdentifier& identifier) { auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) { - return devicePair.second->getDescriptor().size() && identifier.descriptor.size() && - devicePair.second->getDescriptor() == identifier.descriptor; + const InputDeviceIdentifier identifier2 = + devicePair.second->getDeviceInfo().getIdentifier(); + return isSubDevice(identifier, identifier2); }); std::shared_ptr<InputDevice> device; @@ -685,23 +706,43 @@ void InputReader::flushSensor(int32_t deviceId, InputDeviceSensorType sensorType } std::optional<int32_t> InputReader::getBatteryCapacity(int32_t deviceId) { - std::scoped_lock _l(mLock); + std::optional<int32_t> eventHubId; + { + // Do not query the battery state while holding the lock. For some peripheral devices, + // reading battery state can be broken and take 5+ seconds. Holding the lock in this case + // would block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and get the battery state outside the + // lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + InputDevice* device = findInputDeviceLocked(deviceId); + if (!device) return {}; + eventHubId = device->getBatteryEventHubId(); + } // release lock - InputDevice* device = findInputDeviceLocked(deviceId); - if (device) { - return device->getBatteryCapacity(); - } - return std::nullopt; + if (!eventHubId) return {}; + const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); + if (batteryIds.empty()) return {}; + return mEventHub->getBatteryCapacity(*eventHubId, batteryIds.front()); } std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) { - std::scoped_lock _l(mLock); + std::optional<int32_t> eventHubId; + { + // Do not query the battery state while holding the lock. For some peripheral devices, + // reading battery state can be broken and take 5+ seconds. Holding the lock in this case + // would block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and get the battery state outside the + // lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + InputDevice* device = findInputDeviceLocked(deviceId); + if (!device) return {}; + eventHubId = device->getBatteryEventHubId(); + } // release lock - InputDevice* device = findInputDeviceLocked(deviceId); - if (device) { - return device->getBatteryStatus(); - } - return std::nullopt; + if (!eventHubId) return {}; + const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); + if (batteryIds.empty()) return {}; + return mEventHub->getBatteryStatus(*eventHubId, batteryIds.front()); } std::vector<InputDeviceLightInfo> InputReader::getLights(int32_t deviceId) { diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 130c55639b..54c6810a33 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -650,7 +650,6 @@ private: void scanDevicesLocked() REQUIRES(mLock); status_t readNotifyLocked() REQUIRES(mLock); - Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock); Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock); Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock); /** @@ -660,6 +659,9 @@ private: Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock); int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock); + + bool hasDeviceWithDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock); + void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock); void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier, ftl::Flags<InputDeviceClass> classes) REQUIRES(mLock); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 728020eadc..b3a24af8a9 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -32,8 +32,6 @@ #include "InputReaderContext.h" namespace android { -// TODO b/180733860 support multiple battery in API and remove this. -constexpr int32_t DEFAULT_BATTERY_ID = 1; class PeripheralController; class PeripheralControllerInterface; @@ -100,8 +98,7 @@ public: void disableSensor(InputDeviceSensorType sensorType); void flushSensor(InputDeviceSensorType sensorType); - std::optional<int32_t> getBatteryCapacity(); - std::optional<int32_t> getBatteryStatus(); + std::optional<int32_t> getBatteryEventHubId() const; bool setLightColor(int32_t lightId, int32_t color); bool setLightPlayerId(int32_t lightId, int32_t playerId); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 8233682a32..f4f3ae95a1 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -69,7 +69,11 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext) {} -CursorInputMapper::~CursorInputMapper() {} +CursorInputMapper::~CursorInputMapper() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } +} uint32_t CursorInputMapper::getSources() const { return mSource; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 5a7016780a..ed3b0eccf0 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1017,9 +1017,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) : getInverseRotation(mViewport.orientation); // For orientation-aware devices that work in the un-rotated coordinate space, the // viewport update should be skipped if it is only a change in the orientation. - skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware && - mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight && - viewportOrientationChanged; + skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth && + mDisplayHeight == oldDisplayHeight && viewportOrientationChanged; // Apply the input device orientation for the device. mInputDeviceOrientation = @@ -1034,6 +1033,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mDisplayHeight = rawHeight; mInputDeviceOrientation = DISPLAY_ORIENTATION_0; } + // If displayId changed, do not skip viewport update. + skipViewportUpdate &= !viewportDisplayIdChanged; } // If moving between pointer modes, need to reset some state. @@ -1055,6 +1056,10 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } } else { + if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT && + !mConfig.showTouches) { + mPointerController->clearSpots(); + } mPointerController.reset(); } diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index ef68a84fdb..6ef6e4498c 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -180,6 +180,20 @@ void EventHubTest::assertNoMoreEvents() { } /** + * Ensure that two identical devices get assigned unique descriptors from EventHub. + */ +TEST_F(EventHubTest, DevicesWithMatchingUniqueIdsAreUnique) { + std::unique_ptr<UinputHomeKey> keyboard2 = createUinputDevice<UinputHomeKey>(); + int32_t deviceId2; + ASSERT_NO_FATAL_FAILURE(deviceId2 = waitForDeviceCreation()); + + ASSERT_NE(mEventHub->getDeviceIdentifier(mDeviceId).descriptor, + mEventHub->getDeviceIdentifier(deviceId2).descriptor); + keyboard2.reset(); + waitForDeviceClose(deviceId2); +} + +/** * Ensure that input_events are generated with monotonic clock. * That means input_event should receive a timestamp that is in the future of the time * before the event was sent. diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ca0ef2a43d..b2575fe025 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -2201,6 +2201,59 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); } +TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> windowDefaultDisplay = + sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay", + ADISPLAY_ID_DEFAULT); + windowDefaultDisplay->setFrame(Rect(0, 0, 600, 800)); + sp<FakeWindowHandle> windowSecondDisplay = + sp<FakeWindowHandle>::make(application, mDispatcher, "SecondDisplay", + SECOND_DISPLAY_ID); + windowSecondDisplay->setFrame(Rect(0, 0, 600, 800)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, + {SECOND_DISPLAY_ID, {windowSecondDisplay}}}); + + // Set cursor position in window in default display and check that hover enter and move + // events are generated. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .displayId(ADISPLAY_ID_DEFAULT) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(600)) + .build())); + windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + // Remove all windows in secondary display and check that no event happens on window in + // primary display. + mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); + windowDefaultDisplay->assertNoEvents(); + + // Move cursor position in window in default display and check that only hover move + // event is generated and not hover enter event. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, + {SECOND_DISPLAY_ID, {windowSecondDisplay}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .displayId(ADISPLAY_ID_DEFAULT) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(400) + .y(700)) + .build())); + windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowDefaultDisplay->assertNoEvents(); +} + TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8a97901926..03fbf07b16 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -246,7 +246,7 @@ private: mSpotsByDisplay[displayId] = newSpots; } - void clearSpots() override {} + void clearSpots() override { mSpotsByDisplay.clear(); } std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; }; @@ -1014,7 +1014,9 @@ private: return BATTERY_STATUS; } - const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; } + const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override { + return {DEFAULT_BATTERY}; + } std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) { return std::nullopt; @@ -6878,7 +6880,9 @@ TEST_F(SingleTouchInputMapperTest, NotifyMotionArgs motionArgs; // Down. - processDown(mapper, 100, 200); + int32_t x = 100; + int32_t y = 200; + processDown(mapper, x, y); processSync(mapper); // We should receive a down event @@ -8887,7 +8891,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Default device will reconfigure above, need additional reconfiguration for another device. device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); // Two fingers down at default display. int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; @@ -8914,6 +8919,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID); ASSERT_TRUE(iter != fakePointerController->getSpots().end()); ASSERT_EQ(size_t(2), iter->second.size()); + + // Disable the show touches configuration and ensure the spots are cleared. + mFakePolicy->setShowTouches(false); + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); + + ASSERT_TRUE(fakePointerController->getSpots().empty()); } TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { diff --git a/services/powermanager/tests/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp index b62be5f5d4..741495878a 100644 --- a/services/powermanager/tests/IThermalManagerTest.cpp +++ b/services/powermanager/tests/IThermalManagerTest.cpp @@ -33,26 +33,38 @@ using namespace android; using namespace android::os; using namespace std::chrono_literals; -class IThermalServiceTest : public testing::Test, - public BnThermalStatusListener{ +class IThermalServiceTestListener : public BnThermalStatusListener { + public: + virtual binder::Status onStatusChange(int status) override; + std::condition_variable mCondition; + int mListenerStatus = 0; + std::mutex mMutex; +}; + +binder::Status IThermalServiceTestListener::onStatusChange(int status) { + std::unique_lock<std::mutex> lock(mMutex); + mListenerStatus = status; + ALOGI("IThermalServiceTestListener::notifyListener %d", mListenerStatus); + mCondition.notify_all(); + return binder::Status::ok(); +} + +class IThermalServiceTest : public testing::Test { public: IThermalServiceTest(); void setThermalOverride(int level); - virtual binder::Status onStatusChange(int status) override; int getStatusFromService(); void SetUp() override; void TearDown() override; protected: sp<IThermalService> mThermalSvc; - std::condition_variable mCondition; - int mListenerStatus; int mServiceStatus; - std::mutex mMutex; + sp<IThermalServiceTestListener> mCallback; }; IThermalServiceTest::IThermalServiceTest() - : mListenerStatus(0), - mServiceStatus(0) { + : mServiceStatus(0), + mCallback(sp<IThermalServiceTestListener>::make()) { } void IThermalServiceTest::setThermalOverride(int level) { @@ -60,14 +72,6 @@ void IThermalServiceTest::setThermalOverride(int level) { system(cmdStr.c_str()); } -binder::Status IThermalServiceTest::onStatusChange(int status) { - std::unique_lock<std::mutex> lock(mMutex); - mListenerStatus = status; - ALOGI("IThermalServiceTest::notifyListener %d", mListenerStatus); - mCondition.notify_all(); - return binder::Status::ok(); -} - int IThermalServiceTest::getStatusFromService() { int status; binder::Status ret = mThermalSvc->getCurrentThermalStatus(&status); @@ -87,19 +91,19 @@ void IThermalServiceTest::SetUp() { mThermalSvc = interface_cast<IThermalService>(binder); EXPECT_NE(mThermalSvc, nullptr); // Lock mutex for operation, so listener will only be processed after wait_for is called - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mCallback->mMutex); bool success = false; - binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success); + binder::Status ret = mThermalSvc->registerThermalStatusListener(mCallback, &success); // Check the result ASSERT_TRUE(success); ASSERT_TRUE(ret.isOk()); // Wait for listener called after registration, shouldn't timeout - EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); + EXPECT_NE(mCallback->mCondition.wait_for(lock, 1s), std::cv_status::timeout); } void IThermalServiceTest::TearDown() { bool success = false; - binder::Status ret = mThermalSvc->unregisterThermalStatusListener(this, &success); + binder::Status ret = mThermalSvc->unregisterThermalStatusListener(mCallback, &success); ASSERT_TRUE(success); ASSERT_TRUE(ret.isOk()); } @@ -114,14 +118,14 @@ class IThermalListenerTest : public IThermalServiceTest, public testing::WithPar TEST_P(IThermalListenerTest, TestListener) { int level = GetParam(); // Lock mutex for operation, so listener will only be processed after wait_for is called - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mCallback->mMutex); // Set the override thermal status setThermalOverride(level); // Wait for listener called, shouldn't timeout - EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); + EXPECT_NE(mCallback->mCondition.wait_for(lock, 1s), std::cv_status::timeout); // Check the result - EXPECT_EQ(level, mListenerStatus); - ALOGI("Thermal listener status %d, expecting %d", mListenerStatus, level); + EXPECT_EQ(level, mCallback->mListenerStatus); + ALOGI("Thermal listener status %d, expecting %d", mCallback->mListenerStatus, level); } INSTANTIATE_TEST_SUITE_P(TestListenerLevels, IThermalListenerTest, testing::Range( @@ -158,4 +162,4 @@ int main(int argc, char **argv) { ALOGV("Test result = %d\n", status); return status; -}
\ No newline at end of file +} diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index f67c610550..f5b360f3b6 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -23,6 +23,7 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android-base/logging.h> #include <android/binder_manager.h> +#include <aidl/sensors/convert.h> using ::aidl::android::hardware::sensors::AdditionalInfo; using ::aidl::android::hardware::sensors::DynamicSensorInfo; @@ -34,469 +35,16 @@ using ::aidl::android::hardware::sensors::SensorType; using ::android::AidlMessageQueue; using ::android::hardware::EventFlag; using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT; +using ::android::hardware::sensors::implementation::convertToStatus; +using ::android::hardware::sensors::implementation::convertToSensor; +using ::android::hardware::sensors::implementation::convertToSensorEvent; +using ::android::hardware::sensors::implementation::convertFromSensorEvent; + namespace android { namespace { -status_t convertToStatus(ndk::ScopedAStatus status) { - if (status.isOk()) { - return OK; - } else { - switch (status.getExceptionCode()) { - case EX_ILLEGAL_ARGUMENT: { - return BAD_VALUE; - } - case EX_SECURITY: { - return PERMISSION_DENIED; - } - case EX_UNSUPPORTED_OPERATION: { - return INVALID_OPERATION; - } - case EX_SERVICE_SPECIFIC: { - switch (status.getServiceSpecificError()) { - case ISensors::ERROR_BAD_VALUE: { - return BAD_VALUE; - } - case ISensors::ERROR_NO_MEMORY: { - return NO_MEMORY; - } - default: { - return UNKNOWN_ERROR; - } - } - } - default: { - return UNKNOWN_ERROR; - } - } - } -} - -void convertToSensor(const SensorInfo &src, sensor_t *dst) { - dst->name = strdup(src.name.c_str()); - dst->vendor = strdup(src.vendor.c_str()); - dst->version = src.version; - dst->handle = src.sensorHandle; - dst->type = (int)src.type; - dst->maxRange = src.maxRange; - dst->resolution = src.resolution; - dst->power = src.power; - dst->minDelay = src.minDelayUs; - dst->fifoReservedEventCount = src.fifoReservedEventCount; - dst->fifoMaxEventCount = src.fifoMaxEventCount; - dst->stringType = strdup(src.typeAsString.c_str()); - dst->requiredPermission = strdup(src.requiredPermission.c_str()); - dst->maxDelay = src.maxDelayUs; - dst->flags = src.flags; - dst->reserved[0] = dst->reserved[1] = 0; -} - -void convertToSensorEvent(const Event &src, sensors_event_t *dst) { - *dst = {.version = sizeof(sensors_event_t), - .sensor = src.sensorHandle, - .type = (int32_t)src.sensorType, - .reserved0 = 0, - .timestamp = src.timestamp}; - - switch (src.sensorType) { - case SensorType::META_DATA: { - // Legacy HALs expect the handle reference in the meta data field. - // Copy it over from the handle of the event. - dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what; - dst->meta_data.sensor = src.sensorHandle; - // Set the sensor handle to 0 to maintain compatibility. - dst->sensor = 0; - break; - } - - case SensorType::ACCELEROMETER: - case SensorType::MAGNETIC_FIELD: - case SensorType::ORIENTATION: - case SensorType::GYROSCOPE: - case SensorType::GRAVITY: - case SensorType::LINEAR_ACCELERATION: { - dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x; - dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y; - dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z; - dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status; - break; - } - - case SensorType::GAME_ROTATION_VECTOR: { - dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x; - dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y; - dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z; - dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w; - break; - } - - case SensorType::ROTATION_VECTOR: - case SensorType::GEOMAGNETIC_ROTATION_VECTOR: { - dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0]; - dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1]; - dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2]; - dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3]; - dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4]; - break; - } - - case SensorType::MAGNETIC_FIELD_UNCALIBRATED: - case SensorType::GYROSCOPE_UNCALIBRATED: - case SensorType::ACCELEROMETER_UNCALIBRATED: { - dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x; - dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y; - dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z; - dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias; - dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias; - dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias; - break; - } - - case SensorType::HINGE_ANGLE: - case SensorType::DEVICE_ORIENTATION: - case SensorType::LIGHT: - case SensorType::PRESSURE: - case SensorType::PROXIMITY: - case SensorType::RELATIVE_HUMIDITY: - case SensorType::AMBIENT_TEMPERATURE: - case SensorType::SIGNIFICANT_MOTION: - case SensorType::STEP_DETECTOR: - case SensorType::TILT_DETECTOR: - case SensorType::WAKE_GESTURE: - case SensorType::GLANCE_GESTURE: - case SensorType::PICK_UP_GESTURE: - case SensorType::WRIST_TILT_GESTURE: - case SensorType::STATIONARY_DETECT: - case SensorType::MOTION_DETECT: - case SensorType::HEART_BEAT: - case SensorType::LOW_LATENCY_OFFBODY_DETECT: { - dst->data[0] = src.payload.get<Event::EventPayload::scalar>(); - break; - } - - case SensorType::STEP_COUNTER: { - dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>(); - break; - } - - case SensorType::HEART_RATE: { - dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm; - dst->heart_rate.status = - (int8_t)src.payload.get<Event::EventPayload::heartRate>().status; - break; - } - - case SensorType::POSE_6DOF: { // 15 floats - for (size_t i = 0; i < 15; ++i) { - dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i]; - } - break; - } - - case SensorType::DYNAMIC_SENSOR_META: { - dst->dynamic_sensor_meta.connected = - src.payload.get<Event::EventPayload::dynamic>().connected; - dst->dynamic_sensor_meta.handle = - src.payload.get<Event::EventPayload::dynamic>().sensorHandle; - dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later - - memcpy(dst->dynamic_sensor_meta.uuid, - src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16); - - break; - } - - case SensorType::ADDITIONAL_INFO: { - const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>(); - - additional_info_event_t *dstInfo = &dst->additional_info; - dstInfo->type = (int32_t)srcInfo.type; - dstInfo->serial = srcInfo.serial; - - switch (srcInfo.payload.getTag()) { - case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: { - const auto &values = - srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>() - .values; - CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32)); - memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32)); - break; - } - case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: { - const auto &values = - srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>() - .values; - CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float)); - memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float)); - break; - } - default: { - ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag()); - } - } - break; - } - - case SensorType::HEAD_TRACKER: { - const auto &ht = src.payload.get<Event::EventPayload::headTracker>(); - dst->head_tracker.rx = ht.rx; - dst->head_tracker.ry = ht.ry; - dst->head_tracker.rz = ht.rz; - dst->head_tracker.vx = ht.vx; - dst->head_tracker.vy = ht.vy; - dst->head_tracker.vz = ht.vz; - dst->head_tracker.discontinuity_count = ht.discontinuityCount; - break; - } - - case SensorType::ACCELEROMETER_LIMITED_AXES: - case SensorType::GYROSCOPE_LIMITED_AXES: - dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x; - dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y; - dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z; - dst->limited_axes_imu.x_supported = - src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported; - dst->limited_axes_imu.y_supported = - src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported; - dst->limited_axes_imu.z_supported = - src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported; - break; - - case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: - case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: - dst->limited_axes_imu_uncalibrated.x_uncalib = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x; - dst->limited_axes_imu_uncalibrated.y_uncalib = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y; - dst->limited_axes_imu_uncalibrated.z_uncalib = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z; - dst->limited_axes_imu_uncalibrated.x_bias = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias; - dst->limited_axes_imu_uncalibrated.y_bias = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias; - dst->limited_axes_imu_uncalibrated.z_bias = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias; - dst->limited_axes_imu_uncalibrated.x_supported = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported; - dst->limited_axes_imu_uncalibrated.y_supported = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported; - dst->limited_axes_imu_uncalibrated.z_supported = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported; - break; - - case SensorType::HEADING: - dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading; - dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy; - break; - - default: { - CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); - - memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(), - 16 * sizeof(float)); - break; - } - } -} - -void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { - *dst = { - .timestamp = src.timestamp, - .sensorHandle = src.sensor, - .sensorType = (SensorType)src.type, - }; - - switch (dst->sensorType) { - case SensorType::META_DATA: { - Event::EventPayload::MetaData meta; - meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what; - // Legacy HALs contain the handle reference in the meta data field. - // Copy that over to the handle of the event. In legacy HALs this - // field was expected to be 0. - dst->sensorHandle = src.meta_data.sensor; - dst->payload.set<Event::EventPayload::Tag::meta>(meta); - break; - } - - case SensorType::ACCELEROMETER: - case SensorType::MAGNETIC_FIELD: - case SensorType::ORIENTATION: - case SensorType::GYROSCOPE: - case SensorType::GRAVITY: - case SensorType::LINEAR_ACCELERATION: { - Event::EventPayload::Vec3 vec3; - vec3.x = src.acceleration.x; - vec3.y = src.acceleration.y; - vec3.z = src.acceleration.z; - vec3.status = (SensorStatus)src.acceleration.status; - dst->payload.set<Event::EventPayload::Tag::vec3>(vec3); - break; - } - - case SensorType::GAME_ROTATION_VECTOR: { - Event::EventPayload::Vec4 vec4; - vec4.x = src.data[0]; - vec4.y = src.data[1]; - vec4.z = src.data[2]; - vec4.w = src.data[3]; - dst->payload.set<Event::EventPayload::Tag::vec4>(vec4); - break; - } - - case SensorType::ROTATION_VECTOR: - case SensorType::GEOMAGNETIC_ROTATION_VECTOR: { - Event::EventPayload::Data data; - memcpy(data.values.data(), src.data, 5 * sizeof(float)); - dst->payload.set<Event::EventPayload::Tag::data>(data); - break; - } - - case SensorType::MAGNETIC_FIELD_UNCALIBRATED: - case SensorType::GYROSCOPE_UNCALIBRATED: - case SensorType::ACCELEROMETER_UNCALIBRATED: { - Event::EventPayload::Uncal uncal; - uncal.x = src.uncalibrated_gyro.x_uncalib; - uncal.y = src.uncalibrated_gyro.y_uncalib; - uncal.z = src.uncalibrated_gyro.z_uncalib; - uncal.xBias = src.uncalibrated_gyro.x_bias; - uncal.yBias = src.uncalibrated_gyro.y_bias; - uncal.zBias = src.uncalibrated_gyro.z_bias; - dst->payload.set<Event::EventPayload::Tag::uncal>(uncal); - break; - } - - case SensorType::DEVICE_ORIENTATION: - case SensorType::LIGHT: - case SensorType::PRESSURE: - case SensorType::PROXIMITY: - case SensorType::RELATIVE_HUMIDITY: - case SensorType::AMBIENT_TEMPERATURE: - case SensorType::SIGNIFICANT_MOTION: - case SensorType::STEP_DETECTOR: - case SensorType::TILT_DETECTOR: - case SensorType::WAKE_GESTURE: - case SensorType::GLANCE_GESTURE: - case SensorType::PICK_UP_GESTURE: - case SensorType::WRIST_TILT_GESTURE: - case SensorType::STATIONARY_DETECT: - case SensorType::MOTION_DETECT: - case SensorType::HEART_BEAT: - case SensorType::LOW_LATENCY_OFFBODY_DETECT: - case SensorType::HINGE_ANGLE: { - dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]); - break; - } - - case SensorType::STEP_COUNTER: { - dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter); - break; - } - - case SensorType::HEART_RATE: { - Event::EventPayload::HeartRate heartRate; - heartRate.bpm = src.heart_rate.bpm; - heartRate.status = (SensorStatus)src.heart_rate.status; - dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate); - break; - } - - case SensorType::POSE_6DOF: { // 15 floats - Event::EventPayload::Pose6Dof pose6DOF; - for (size_t i = 0; i < 15; ++i) { - pose6DOF.values[i] = src.data[i]; - } - dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF); - break; - } - - case SensorType::DYNAMIC_SENSOR_META: { - DynamicSensorInfo dynamic; - dynamic.connected = src.dynamic_sensor_meta.connected; - dynamic.sensorHandle = src.dynamic_sensor_meta.handle; - - memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16); - dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic); - break; - } - - case SensorType::ADDITIONAL_INFO: { - AdditionalInfo info; - const additional_info_event_t &srcInfo = src.additional_info; - info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type; - info.serial = srcInfo.serial; - - AdditionalInfo::AdditionalInfoPayload::Int32Values data; - CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32)); - memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32)); - info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data); - - dst->payload.set<Event::EventPayload::Tag::additional>(info); - break; - } - - case SensorType::HEAD_TRACKER: { - Event::EventPayload::HeadTracker headTracker; - headTracker.rx = src.head_tracker.rx; - headTracker.ry = src.head_tracker.ry; - headTracker.rz = src.head_tracker.rz; - headTracker.vx = src.head_tracker.vx; - headTracker.vy = src.head_tracker.vy; - headTracker.vz = src.head_tracker.vz; - headTracker.discontinuityCount = src.head_tracker.discontinuity_count; - - dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker); - break; - } - - case SensorType::ACCELEROMETER_LIMITED_AXES: - case SensorType::GYROSCOPE_LIMITED_AXES: { - Event::EventPayload::LimitedAxesImu limitedAxesImu; - limitedAxesImu.x = src.limited_axes_imu.x; - limitedAxesImu.y = src.limited_axes_imu.y; - limitedAxesImu.z = src.limited_axes_imu.z; - limitedAxesImu.xSupported = src.limited_axes_imu.x_supported; - limitedAxesImu.ySupported = src.limited_axes_imu.y_supported; - limitedAxesImu.zSupported = src.limited_axes_imu.z_supported; - dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu); - break; - } - - case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: - case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: { - Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal; - limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib; - limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib; - limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib; - limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias; - limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias; - limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias; - limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported; - limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported; - limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported; - dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal); - break; - } - - case SensorType::HEADING: { - Event::EventPayload::Heading heading; - heading.heading = src.heading.heading; - heading.accuracy = src.heading.accuracy; - dst->payload.set<Event::EventPayload::heading>(heading); - break; - } - - default: { - CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); - - Event::EventPayload::Data data; - memcpy(data.values.data(), src.data, 16 * sizeof(float)); - dst->payload.set<Event::EventPayload::Tag::data>(data); - break; - } - } -} - void serviceDied(void *cookie) { ALOGW("Sensors HAL died, attempting to reconnect."); ((AidlSensorHalWrapper *)cookie)->prepareForReconnect(); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 3c4f8d9bae..a2042d6451 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -75,6 +75,7 @@ cc_library_shared { static_libs: [ "libaidlcommonsupport", "android.hardware.sensors@1.0-convert", + "android.hardware.sensors-V1-convert", "android.hardware.sensors-V1-ndk", ], diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp new file mode 100644 index 0000000000..34d1de72f9 --- /dev/null +++ b/services/sensorservice/aidl/Android.bp @@ -0,0 +1,44 @@ +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 { + name: "libsensorserviceaidl", + srcs: [ + "EventQueue.cpp", + "DirectReportChannel.cpp", + "SensorManager.cpp", + "utils.cpp", + ], + host_supported: true, + cflags: [ + "-Wall", + "-Werror", + ], + header_libs: ["jni_headers"], + shared_libs: [ + "libbase", + "libutils", + "libcutils", + "libbinder_ndk", + "libsensor", + "android.frameworks.sensorservice-V1-ndk", + "android.hardware.sensors-V1-ndk", + ], + export_include_dirs: [ + "include/", + ], + static_libs: [ + "android.hardware.sensors-V1-convert", + ], + + export_header_lib_headers: ["jni_headers"], + local_include_dirs: [ + "include/sensorserviceaidl/", + ], +} diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp new file mode 100644 index 0000000000..cab53c17f5 --- /dev/null +++ b/services/sensorservice/aidl/DirectReportChannel.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DirectReportChannel.h" + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId) + : mManager(manager), mId(channelId) {} + +DirectReportChannel::~DirectReportChannel() { + mManager.destroyDirectChannel(mId); +} + +ndk::ScopedAStatus DirectReportChannel::configure( + int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate, + int32_t* _aidl_return) { + int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate)); + if (token <= 0) { + return ndk::ScopedAStatus::fromServiceSpecificError(token); + } + *_aidl_return = token; + return ndk::ScopedAStatus::ok(); +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/DirectReportChannel.h b/services/sensorservice/aidl/DirectReportChannel.h new file mode 100644 index 0000000000..d9ea73db9e --- /dev/null +++ b/services/sensorservice/aidl/DirectReportChannel.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <aidl/android/frameworks/sensorservice/BnDirectReportChannel.h> +#include <aidl/android/hardware/sensors/ISensors.h> +#include <sensor/SensorManager.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +class DirectReportChannel final + : public ::aidl::android::frameworks::sensorservice::BnDirectReportChannel { +public: + DirectReportChannel(::android::SensorManager& manager, int channelId); + ~DirectReportChannel(); + + ndk::ScopedAStatus configure(int32_t sensorHandle, + ::aidl::android::hardware::sensors::ISensors::RateLevel rate, + int32_t* _aidl_return) override; + +private: + ::android::SensorManager& mManager; + const int mId; +}; + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp new file mode 100644 index 0000000000..c3947098c3 --- /dev/null +++ b/services/sensorservice/aidl/EventQueue.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventQueue.h" +#include "utils.h" + +#include <android-base/logging.h> +#include <utils/Looper.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +using ::aidl::android::frameworks::sensorservice::IEventQueueCallback; +using ::aidl::android::hardware::sensors::Event; + +class EventQueueLooperCallback : public ::android::LooperCallback { +public: + EventQueueLooperCallback(sp<::android::SensorEventQueue> queue, + std::shared_ptr<IEventQueueCallback> callback) + : mQueue(queue), mCallback(callback) {} + + int handleEvent(int /* fd */, int /* events */, void* /* data */) { + ASensorEvent event; + ssize_t actual; + + auto internalQueue = mQueue.promote(); + if (internalQueue == nullptr) { + return 1; + } + + while ((actual = internalQueue->read(&event, 1)) > 0) { + internalQueue->sendAck(&event, actual); + ndk::ScopedAStatus ret = mCallback->onEvent(convertEvent(event)); + if (!ret.isOk()) { + LOG(ERROR) << "Failed to envoke EventQueueCallback: " << ret; + } + } + + return 1; // continue to receive callbacks + } + +private: + wp<::android::SensorEventQueue> mQueue; + std::shared_ptr<IEventQueueCallback> mCallback; +}; + +EventQueue::EventQueue(std::shared_ptr<IEventQueueCallback> callback, sp<::android::Looper> looper, + sp<::android::SensorEventQueue> internalQueue) + : mLooper(looper), mInternalQueue(internalQueue) { + mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, + new EventQueueLooperCallback(internalQueue, callback), nullptr); +} + +EventQueue::~EventQueue() { + mLooper->removeFd(mInternalQueue->getFd()); +} + +ndk::ScopedAStatus EventQueue::enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs, + int64_t in_maxBatchReportLatencyUs) { + return convertResult(mInternalQueue->enableSensor(in_sensorHandle, in_samplingPeriodUs, + in_maxBatchReportLatencyUs, 0)); +} + +ndk::ScopedAStatus EventQueue::disableSensor(int32_t in_sensorHandle) { + return convertResult(mInternalQueue->disableSensor(in_sensorHandle)); +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/EventQueue.h b/services/sensorservice/aidl/EventQueue.h new file mode 100644 index 0000000000..0ae1eba764 --- /dev/null +++ b/services/sensorservice/aidl/EventQueue.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "SensorManagerAidl.h" + +#include <aidl/android/frameworks/sensorservice/BnEventQueue.h> +#include <sensor/SensorManager.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +struct EventQueue final : public aidl::android::frameworks::sensorservice::BnEventQueue { + EventQueue( + std::shared_ptr<aidl::android::frameworks::sensorservice::IEventQueueCallback> callback, + sp<::android::Looper> looper, sp<::android::SensorEventQueue> internalQueue); + ~EventQueue(); + + ndk::ScopedAStatus enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs, + int64_t in_maxBatchReportLatencyUs) override; + ndk::ScopedAStatus disableSensor(int32_t sensorHandle) override; + +private: + friend class EventQueueLooperCallback; + sp<::android::Looper> mLooper; + sp<::android::SensorEventQueue> mInternalQueue; +}; + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp new file mode 100644 index 0000000000..9b0334443b --- /dev/null +++ b/services/sensorservice/aidl/SensorManager.cpp @@ -0,0 +1,254 @@ +/* + * 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. + */ + +// LOG_TAG defined via build flag. +#ifndef LOG_TAG +#define LOG_TAG "AidlSensorManager" +#endif + +#include "DirectReportChannel.h" +#include "EventQueue.h" +#include "SensorManagerAidl.h" +#include "utils.h" + +#include <aidl/android/hardware/sensors/ISensors.h> +#include <android-base/logging.h> +#include <android/binder_ibinder.h> +#include <sched.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +using ::aidl::android::frameworks::sensorservice::IDirectReportChannel; +using ::aidl::android::frameworks::sensorservice::IEventQueue; +using ::aidl::android::frameworks::sensorservice::IEventQueueCallback; +using ::aidl::android::frameworks::sensorservice::ISensorManager; +using ::aidl::android::hardware::common::Ashmem; +using ::aidl::android::hardware::sensors::ISensors; +using ::aidl::android::hardware::sensors::SensorInfo; +using ::aidl::android::hardware::sensors::SensorType; +using ::android::frameworks::sensorservice::implementation::SensorManagerAidl; + +static const char* POLL_THREAD_NAME = "aidl_ssvc_poll"; + +SensorManagerAidl::SensorManagerAidl(JavaVM* vm) + : mLooper(new Looper(false)), mStopThread(true), mJavaVm(vm) {} +SensorManagerAidl::~SensorManagerAidl() { + // Stops pollAll inside the thread. + std::lock_guard<std::mutex> lock(mThreadMutex); + + mStopThread = true; + if (mLooper != nullptr) { + mLooper->wake(); + } + if (mPollThread.joinable()) { + mPollThread.join(); + } +} + +ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type, + const native_handle_t* handle, + std::shared_ptr<IDirectReportChannel>* chan) { + int channelId = manager.createDirectChannel(size, type, handle); + if (channelId < 0) { + return convertResult(channelId); + } + if (channelId == 0) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + *chan = ndk::SharedRefBase::make<DirectReportChannel>(manager, channelId); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus SensorManagerAidl::createAshmemDirectChannel( + const Ashmem& in_mem, int64_t in_size, + std::shared_ptr<IDirectReportChannel>* _aidl_return) { + if (in_size > in_mem.size || in_size < ISensors::DIRECT_REPORT_SENSOR_EVENT_TOTAL_LENGTH) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE); + } + native_handle_t* handle = native_handle_create(1, 0); + handle->data[0] = dup(in_mem.fd.get()); + + auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_ASHMEM, + handle, _aidl_return); + int result = native_handle_close(handle); + CHECK(result == 0) << "Failed to close the native_handle_t: " << result; + result = native_handle_delete(handle); + CHECK(result == 0) << "Failed to delete the native_handle_t: " << result; + + return status; +} + +ndk::ScopedAStatus SensorManagerAidl::createGrallocDirectChannel( + const ndk::ScopedFileDescriptor& in_mem, int64_t in_size, + std::shared_ptr<IDirectReportChannel>* _aidl_return) { + native_handle_t* handle = native_handle_create(1, 0); + handle->data[0] = dup(in_mem.get()); + + auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_GRALLOC, + handle, _aidl_return); + int result = native_handle_close(handle); + CHECK(result == 0) << "Failed to close the native_handle_t: " << result; + result = native_handle_delete(handle); + CHECK(result == 0) << "Failed to delete the native_handle_t: " << result; + + return status; +} + +ndk::ScopedAStatus SensorManagerAidl::createEventQueue( + const std::shared_ptr<IEventQueueCallback>& in_callback, + std::shared_ptr<IEventQueue>* _aidl_return) { + if (in_callback == nullptr) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE); + } + + sp<::android::Looper> looper = getLooper(); + if (looper == nullptr) { + LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue cannot initialize looper"; + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + + String8 package(String8::format("aidl_client_pid_%d", AIBinder_getCallingPid())); + sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue(package); + if (internalQueue == nullptr) { + LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue returns nullptr."; + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + + *_aidl_return = ndk::SharedRefBase::make<EventQueue>(in_callback, looper, internalQueue); + + return ndk::ScopedAStatus::ok(); +} + +SensorInfo convertSensor(Sensor src) { + SensorInfo dst; + dst.sensorHandle = src.getHandle(); + dst.name = src.getName(); + dst.vendor = src.getVendor(); + dst.version = src.getVersion(); + dst.type = static_cast<SensorType>(src.getType()); + dst.typeAsString = src.getStringType(); + // maxRange uses maxValue because ::android::Sensor wraps the + // internal sensor_t in this way. + dst.maxRange = src.getMaxValue(); + dst.resolution = src.getResolution(); + dst.power = src.getPowerUsage(); + dst.minDelayUs = src.getMinDelay(); + dst.fifoReservedEventCount = src.getFifoReservedEventCount(); + dst.fifoMaxEventCount = src.getFifoMaxEventCount(); + dst.requiredPermission = src.getRequiredPermission(); + dst.maxDelayUs = src.getMaxDelay(); + dst.flags = src.getFlags(); + return dst; +} + +ndk::ScopedAStatus SensorManagerAidl::getDefaultSensor(SensorType in_type, + SensorInfo* _aidl_return) { + ::android::Sensor const* sensor = + getInternalManager().getDefaultSensor(static_cast<int>(in_type)); + if (!sensor) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_NOT_EXIST); + } + *_aidl_return = convertSensor(*sensor); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus SensorManagerAidl::getSensorList(std::vector<SensorInfo>* _aidl_return) { + Sensor const* const* list; + _aidl_return->clear(); + ssize_t count = getInternalManager().getSensorList(&list); + if (count < 0 || list == nullptr) { + LOG(ERROR) << "SensorMAanger::getSensorList failed with count: " << count; + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + _aidl_return->reserve(static_cast<size_t>(count)); + for (ssize_t i = 0; i < count; ++i) { + _aidl_return->push_back(convertSensor(*list[i])); + } + + return ndk::ScopedAStatus::ok(); +} + +::android::SensorManager& SensorManagerAidl::getInternalManager() { + std::lock_guard<std::mutex> lock(mInternalManagerMutex); + if (mInternalManager == nullptr) { + mInternalManager = &::android::SensorManager::getInstanceForPackage( + String16(ISensorManager::descriptor)); + } + return *mInternalManager; +} + +/* One global looper for all event queues created from this SensorManager. */ +sp<Looper> SensorManagerAidl::getLooper() { + std::lock_guard<std::mutex> lock(mThreadMutex); + + if (!mPollThread.joinable()) { + // if thread not initialized, start thread + mStopThread = false; + std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] { + struct sched_param p = {}; + p.sched_priority = 10; + if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) { + LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno); + } + + // set looper + Looper::setForThread(looper); + + // Attach the thread to JavaVM so that pollAll do not crash if the thread + // eventually calls into Java. + JavaVMAttachArgs args{.version = JNI_VERSION_1_2, + .name = POLL_THREAD_NAME, + .group = nullptr}; + JNIEnv* env; + if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) { + LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM."; + } + + LOG(INFO) << POLL_THREAD_NAME << " started."; + for (;;) { + int pollResult = looper->pollAll(-1 /* timeout */); + if (pollResult == Looper::POLL_WAKE) { + if (stopThread == true) { + LOG(INFO) << POLL_THREAD_NAME << ": requested to stop"; + break; + } else { + LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work"; + } + } else { + LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected " + << pollResult; + break; + } + } + + if (javaVm->DetachCurrentThread() != JNI_OK) { + LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM."; + } + + LOG(INFO) << POLL_THREAD_NAME << " is terminated."; + }}; + mPollThread = std::move(pollThread); + } + return mLooper; +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp new file mode 100644 index 0000000000..0d6e476e70 --- /dev/null +++ b/services/sensorservice/aidl/fuzzer/Android.bp @@ -0,0 +1,52 @@ +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_fuzz { + name: "libsensorserviceaidl_fuzzer", + defaults: [ + "service_fuzzer_defaults", + ], + host_supported: true, + static_libs: [ + "libsensorserviceaidl", + "libpermission", + "android.frameworks.sensorservice-V1-ndk", + "android.hardware.sensors-V1-convert", + "android.hardware.sensors-V1-ndk", + "android.hardware.common-V2-ndk", + "libsensor", + "libfakeservicemanager", + "libcutils", + "liblog", + ], + srcs: [ + "fuzzer.cpp", + ], + fuzz_config: { + cc: [ + "android-sensors@google.com", + "devinmoore@google.com", + ], + }, + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + diag: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + }, + address: true, + integer_overflow: true, + }, + +} diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp new file mode 100644 index 0000000000..1b63d76953 --- /dev/null +++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <fuzzbinder/libbinder_ndk_driver.h> +#include <fuzzer/FuzzedDataProvider.h> + +#include <ServiceManager.h> +#include <android-base/logging.h> +#include <android/binder_interface_utils.h> +#include <fuzzbinder/random_binder.h> +#include <sensorserviceaidl/SensorManagerAidl.h> + +using android::fuzzService; +using android::frameworks::sensorservice::implementation::SensorManagerAidl; +using ndk::SharedRefBase; + +[[clang::no_destroy]] static std::once_flag gSmOnce; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager(); + std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); }); + fakeServiceManager->clear(); + + FuzzedDataProvider fdp(data, size); + android::sp<android::IBinder> binder = android::getRandomBinder(&fdp); + if (binder == nullptr) { + // Nothing to do if we get a null binder. It will cause SensorManager to + // hang while trying to get sensorservice. + return 0; + } + + CHECK(android::NO_ERROR == fakeServiceManager->addService(android::String16("sensorservice"), + binder)); + + std::shared_ptr<SensorManagerAidl> sensorService = + ndk::SharedRefBase::make<SensorManagerAidl>(nullptr); + + fuzzService(sensorService->asBinder().get(), std::move(fdp)); + + return 0; +} diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h new file mode 100644 index 0000000000..c77ee880dd --- /dev/null +++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <aidl/android/frameworks/sensorservice/BnSensorManager.h> +#include <jni.h> +#include <sensor/SensorManager.h> +#include <utils/Looper.h> +#include <mutex> +#include <thread> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +class SensorManagerAidl : public ::aidl::android::frameworks::sensorservice::BnSensorManager { +public: + explicit SensorManagerAidl(JavaVM* vm); + ~SensorManagerAidl(); + + ::ndk::ScopedAStatus createAshmemDirectChannel( + const ::aidl::android::hardware::common::Ashmem& in_mem, int64_t in_size, + std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>* + _aidl_return) override; + ::ndk::ScopedAStatus createEventQueue( + const std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueueCallback>& + in_callback, + std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueue>* _aidl_return) + override; + ::ndk::ScopedAStatus createGrallocDirectChannel( + const ::ndk::ScopedFileDescriptor& in_buffer, int64_t in_size, + std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>* + _aidl_return) override; + ::ndk::ScopedAStatus getDefaultSensor( + ::aidl::android::hardware::sensors::SensorType in_type, + ::aidl::android::hardware::sensors::SensorInfo* _aidl_return) override; + ::ndk::ScopedAStatus getSensorList( + std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override; + +private: + // Block until ::android::SensorManager is initialized. + ::android::SensorManager& getInternalManager(); + sp<Looper> getLooper(); + + std::mutex mInternalManagerMutex; + ::android::SensorManager* mInternalManager = nullptr; // does not own + sp<Looper> mLooper; + + volatile bool mStopThread; + std::mutex mThreadMutex; // protects mPollThread + std::thread mPollThread; + + JavaVM* mJavaVm; +}; + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp new file mode 100644 index 0000000000..beb38b9f55 --- /dev/null +++ b/services/sensorservice/aidl/utils.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" + +#include <aidl/android/frameworks/sensorservice/ISensorManager.h> +#include <aidl/sensors/convert.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +ndk::ScopedAStatus convertResult(status_t src) { + using ::aidl::android::frameworks::sensorservice::ISensorManager; + + int err = 0; + switch (src) { + case OK: + return ndk::ScopedAStatus::ok(); + case NAME_NOT_FOUND: + err = ISensorManager::RESULT_NOT_EXIST; + break; + case NO_MEMORY: + err = ISensorManager::RESULT_NO_MEMORY; + break; + case NO_INIT: + err = ISensorManager::RESULT_NO_INIT; + break; + case PERMISSION_DENIED: + err = ISensorManager::RESULT_PERMISSION_DENIED; + break; + case BAD_VALUE: + err = ISensorManager::RESULT_BAD_VALUE; + break; + case INVALID_OPERATION: + err = ISensorManager::RESULT_INVALID_OPERATION; + break; + default: + err = ISensorManager::RESULT_UNKNOWN_ERROR; + } + return ndk::ScopedAStatus::fromServiceSpecificError(err); +} + +::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) { + ::aidl::android::hardware::sensors::Event dst; + ::android::hardware::sensors::implementation:: + convertFromASensorEvent(src, &dst); + return dst; +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/utils.h b/services/sensorservice/aidl/utils.h new file mode 100644 index 0000000000..06ba59e5aa --- /dev/null +++ b/services/sensorservice/aidl/utils.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <aidl/android/hardware/sensors/Event.h> +#include <sensor/Sensor.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +::ndk::ScopedAStatus convertResult(status_t status); +::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& event); + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp index 2f9e922b59..5fa594d01d 100644 --- a/services/sensorservice/hidl/utils.cpp +++ b/services/sensorservice/hidl/utils.cpp @@ -76,8 +76,8 @@ Result convertResult(status_t status) { ::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) { ::android::hardware::sensors::V1_0::Event dst; - ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent( - reinterpret_cast<const sensors_event_t&>(src), &dst); + ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent( + src, &dst); return dst; } diff --git a/services/stats/.clang-format b/services/stats/.clang-format new file mode 100644 index 0000000000..cead3a0794 --- /dev/null +++ b/services/stats/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: Google +AllowShortIfStatementsOnASingleLine: true +AllowShortFunctionsOnASingleLine: false +AllowShortLoopsOnASingleLine: true +BinPackArguments: true +BinPackParameters: true +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +IndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 +AccessModifierOffset: -4 +IncludeCategories: + - Regex: '^"Log\.h"' + Priority: -1 diff --git a/services/stats/Android.bp b/services/stats/Android.bp index 7fea6161ff..7d358e1bd2 100644 --- a/services/stats/Android.bp +++ b/services/stats/Android.bp @@ -13,10 +13,13 @@ cc_library_shared { "StatsAidl.cpp", "StatsHal.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "android.frameworks.stats@1.0", - "android.frameworks.stats-V1-ndk", + "android.frameworks.stats-V2-ndk", "libbinder_ndk", "libhidlbase", "liblog", @@ -29,10 +32,12 @@ cc_library_shared { ], export_shared_lib_headers: [ "android.frameworks.stats@1.0", - "android.frameworks.stats-V1-ndk", + "android.frameworks.stats-V2-ndk", ], local_include_dirs: [ "include/stats", ], - vintf_fragments: ["android.frameworks.stats@1.0-service.xml"] + vintf_fragments: [ + "android.frameworks.stats-service.xml", + ], } diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp index a3b68f1dab..0f01507c9f 100644 --- a/services/stats/StatsAidl.cpp +++ b/services/stats/StatsAidl.cpp @@ -14,63 +14,245 @@ * limitations under the License. */ -#define DEBUG false // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #define LOG_TAG "StatsAidl" +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__); + +#include "StatsAidl.h" + #include <log/log.h> +#include <stats_annotations.h> +#include <stats_event.h> #include <statslog.h> -#include "StatsAidl.h" +#include <unordered_map> namespace aidl { namespace android { namespace frameworks { namespace stats { -StatsHal::StatsHal() {} +template <typename E> +constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept { + return static_cast<typename std::underlying_type<E>::type>(e); +} + +StatsHal::StatsHal() { +} + +bool write_annotation(AStatsEvent* event, const Annotation& annotation) { + switch (annotation.value.getTag()) { + case AnnotationValue::boolValue: { + AStatsEvent_addBoolAnnotation(event, to_underlying(annotation.annotationId), + annotation.value.get<AnnotationValue::boolValue>()); + break; + } + case AnnotationValue::intValue: { + AStatsEvent_addInt32Annotation(event, to_underlying(annotation.annotationId), + annotation.value.get<AnnotationValue::intValue>()); + break; + } + default: { + return false; + } + } + return true; +} + +bool write_atom_annotations(AStatsEvent* event, + const std::vector<std::optional<Annotation>>& annotations) { + for (const auto& atomAnnotation : annotations) { + if (!atomAnnotation) { + return false; + } + if (!write_annotation(event, *atomAnnotation)) { + return false; + } + } + return true; +} + +bool write_field_annotations(AStatsEvent* event, const std::vector<Annotation>& annotations) { + for (const auto& fieldAnnotation : annotations) { + if (!write_annotation(event, fieldAnnotation)) { + return false; + } + } + return true; +} ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { - std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName; if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) { - ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId); + ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( - -1, "Not a valid vendor atom ID"); + -1, "Not a valid vendor atom ID"); } - if (reverseDomainName.length() > 50) { - ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str()); + if (vendorAtom.reverseDomainName.length() > 50) { + ALOGE("Vendor atom reverse domain name %s is too long.", + vendorAtom.reverseDomainName.c_str()); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( - -1, "Vendor atom reverse domain name is too long"); + -1, "Vendor atom reverse domain name is too long"); } AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, vendorAtom.atomId); + + if (vendorAtom.atomAnnotations) { + if (!write_atom_annotations(event, *vendorAtom.atomAnnotations)) { + ALOGE("Atom ID %ld has incompatible atom level annotation", (long)vendorAtom.atomId); + AStatsEvent_release(event); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "invalid atom annotation"); + } + } + + // populate map for quickier access for VendorAtomValue associated annotations by value index + std::unordered_map<int, int> fieldIndexToAnnotationSetMap; + if (vendorAtom.valuesAnnotations) { + const std::vector<std::optional<AnnotationSet>>& valuesAnnotations = + *vendorAtom.valuesAnnotations; + for (int i = 0; i < valuesAnnotations.size(); i++) { + if (valuesAnnotations[i]) { + fieldIndexToAnnotationSetMap[valuesAnnotations[i]->valueIndex] = i; + } + } + } + AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str()); + size_t atomValueIdx = 0; for (const auto& atomValue : vendorAtom.values) { switch (atomValue.getTag()) { case VendorAtomValue::intValue: - AStatsEvent_writeInt32(event, - atomValue.get<VendorAtomValue::intValue>()); + AStatsEvent_writeInt32(event, atomValue.get<VendorAtomValue::intValue>()); break; case VendorAtomValue::longValue: - AStatsEvent_writeInt64(event, - atomValue.get<VendorAtomValue::longValue>()); + AStatsEvent_writeInt64(event, atomValue.get<VendorAtomValue::longValue>()); break; case VendorAtomValue::floatValue: - AStatsEvent_writeFloat(event, - atomValue.get<VendorAtomValue::floatValue>()); + AStatsEvent_writeFloat(event, atomValue.get<VendorAtomValue::floatValue>()); break; case VendorAtomValue::stringValue: AStatsEvent_writeString(event, - atomValue.get<VendorAtomValue::stringValue>().c_str()); + atomValue.get<VendorAtomValue::stringValue>().c_str()); + break; + case VendorAtomValue::boolValue: + AStatsEvent_writeBool(event, atomValue.get<VendorAtomValue::boolValue>()); + break; + case VendorAtomValue::repeatedIntValue: { + const std::optional<std::vector<int>>& repeatedIntValue = + atomValue.get<VendorAtomValue::repeatedIntValue>(); + if (!repeatedIntValue) { + AStatsEvent_writeInt32Array(event, {}, 0); + break; + } + AStatsEvent_writeInt32Array(event, repeatedIntValue->data(), + repeatedIntValue->size()); + break; + } + case VendorAtomValue::repeatedLongValue: { + const std::optional<std::vector<int64_t>>& repeatedLongValue = + atomValue.get<VendorAtomValue::repeatedLongValue>(); + if (!repeatedLongValue) { + AStatsEvent_writeInt64Array(event, {}, 0); + break; + } + AStatsEvent_writeInt64Array(event, repeatedLongValue->data(), + repeatedLongValue->size()); + break; + } + case VendorAtomValue::repeatedFloatValue: { + const std::optional<std::vector<float>>& repeatedFloatValue = + atomValue.get<VendorAtomValue::repeatedFloatValue>(); + if (!repeatedFloatValue) { + AStatsEvent_writeFloatArray(event, {}, 0); + break; + } + AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(), + repeatedFloatValue->size()); + break; + } + case VendorAtomValue::repeatedStringValue: { + const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue = + atomValue.get<VendorAtomValue::repeatedStringValue>(); + if (!repeatedStringValue) { + AStatsEvent_writeStringArray(event, {}, 0); + break; + } + const std::vector<std::optional<std::string>>& repeatedStringVector = + *repeatedStringValue; + const char* cStringArray[repeatedStringVector.size()]; + + for (int i = 0; i < repeatedStringVector.size(); ++i) { + cStringArray[i] = repeatedStringVector[i].has_value() + ? repeatedStringVector[i]->c_str() + : ""; + } + + AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size()); + break; + } + case VendorAtomValue::repeatedBoolValue: { + const std::optional<std::vector<bool>>& repeatedBoolValue = + atomValue.get<VendorAtomValue::repeatedBoolValue>(); + if (!repeatedBoolValue) { + AStatsEvent_writeBoolArray(event, {}, 0); + break; + } + const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue; + bool boolArray[repeatedBoolValue->size()]; + + for (int i = 0; i < repeatedBoolVector.size(); ++i) { + boolArray[i] = repeatedBoolVector[i]; + } + + AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size()); + break; + } + case VendorAtomValue::byteArrayValue: { + const std::optional<std::vector<uint8_t>>& byteArrayValue = + atomValue.get<VendorAtomValue::byteArrayValue>(); + if (!byteArrayValue) { + AStatsEvent_writeByteArray(event, {}, 0); + break; + } + AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size()); break; + } + default: { + AStatsEvent_release(event); + ALOGE("Atom ID %ld has invalid atomValue.getTag", (long)vendorAtom.atomId); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "invalid atomValue.getTag"); + break; + } + } + + const auto& valueAnnotationIndex = fieldIndexToAnnotationSetMap.find(atomValueIdx); + if (valueAnnotationIndex != fieldIndexToAnnotationSetMap.end()) { + const std::vector<Annotation>& fieldAnnotations = + (*vendorAtom.valuesAnnotations)[valueAnnotationIndex->second]->annotations; + VLOG("Atom ID %ld has %ld annotations for field #%ld", (long)vendorAtom.atomId, + (long)fieldAnnotations.size(), (long)atomValueIdx + 2); + if (!write_field_annotations(event, fieldAnnotations)) { + ALOGE("Atom ID %ld has incompatible field level annotation for field #%ld", + (long)vendorAtom.atomId, (long)atomValueIdx + 2); + AStatsEvent_release(event); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "invalid atom field annotation"); + } } + atomValueIdx++; } AStatsEvent_build(event); const int ret = AStatsEvent_write(event); AStatsEvent_release(event); - - return ret <= 0 ? - ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, "report atom failed") : - ndk::ScopedAStatus::ok(); + if (ret <= 0) { + ALOGE("Error writing Atom ID %ld. Result: %d", (long)vendorAtom.atomId, ret); + } + return ret <= 0 ? ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, + "report atom failed") + : ndk::ScopedAStatus::ok(); } } // namespace stats diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp index ae0a9843f6..19176d9aaf 100644 --- a/services/stats/StatsHal.cpp +++ b/services/stats/StatsHal.cpp @@ -14,42 +14,42 @@ * limitations under the License. */ -#define DEBUG false // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #define LOG_TAG "StatsHal" +#include "StatsHal.h" + #include <log/log.h> #include <statslog.h> -#include "StatsHal.h" - namespace android { namespace frameworks { namespace stats { namespace V1_0 { namespace implementation { -StatsHal::StatsHal() {} +StatsHal::StatsHal() { +} -hardware::Return<void> StatsHal::reportSpeakerImpedance( - const SpeakerImpedance& speakerImpedance) { +hardware::Return<void> StatsHal::reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) { android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED, - speakerImpedance.speakerLocation, speakerImpedance.milliOhms); + speakerImpedance.speakerLocation, speakerImpedance.milliOhms); return hardware::Void(); } hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) { android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), - hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); + hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); return hardware::Void(); } hardware::Return<void> StatsHal::reportPhysicalDropDetected( const PhysicalDropDetected& physicalDropDetected) { - android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED, - int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak, - physicalDropDetected.freefallDuration); + android::util::stats_write( + android::util::PHYSICAL_DROP_DETECTED, int32_t(physicalDropDetected.confidencePctg), + physicalDropDetected.accelPeak, physicalDropDetected.freefallDuration); return hardware::Void(); } @@ -58,20 +58,21 @@ hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCy std::vector<int32_t> buckets = chargeCycles.cycleBucket; int initialSize = buckets.size(); for (int i = 0; i < 10 - initialSize; i++) { - buckets.push_back(0); // Push 0 for buckets that do not exist. + buckets.push_back(0); // Push 0 for buckets that do not exist. } android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], - buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], - buckets[9]); + buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], + buckets[7], buckets[8], buckets[9]); return hardware::Void(); } hardware::Return<void> StatsHal::reportBatteryHealthSnapshot( const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) { - android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT, - int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC, - batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA, + android::util::stats_write( + android::util::BATTERY_HEALTH_SNAPSHOT, int32_t(batteryHealthSnapshotArgs.type), + batteryHealthSnapshotArgs.temperatureDeciC, batteryHealthSnapshotArgs.voltageMicroV, + batteryHealthSnapshotArgs.currentMicroA, batteryHealthSnapshotArgs.openCircuitVoltageMicroV, batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent); @@ -87,14 +88,15 @@ hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) { hardware::Return<void> StatsHal::reportBatteryCausedShutdown( const BatteryCausedShutdown& batteryCausedShutdown) { android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN, - batteryCausedShutdown.voltageMicroV); + batteryCausedShutdown.voltageMicroV); return hardware::Void(); } hardware::Return<void> StatsHal::reportUsbPortOverheatEvent( const UsbPortOverheatEvent& usbPortOverheatEvent) { - android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, + android::util::stats_write( + android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC, usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis, usbPortOverheatEvent.timeToInactive); @@ -102,23 +104,22 @@ hardware::Return<void> StatsHal::reportUsbPortOverheatEvent( return hardware::Void(); } -hardware::Return<void> StatsHal::reportSpeechDspStat( - const SpeechDspStat& speechDspStat) { +hardware::Return<void> StatsHal::reportSpeechDspStat(const SpeechDspStat& speechDspStat) { android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED, - speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, - speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); + speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, + speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); return hardware::Void(); } hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { - std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName; if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) { - ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId); + ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId); return hardware::Void(); } - if (reverseDomainName.length() > 50) { - ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str()); + if (vendorAtom.reverseDomainName.size() > 50) { + ALOGE("Vendor atom reverse domain name %s is too long.", + vendorAtom.reverseDomainName.c_str()); return hardware::Void(); } AStatsEvent* event = AStatsEvent_obtain(); diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats-service.xml index c564b7be53..7e2635e406 100644 --- a/services/stats/android.frameworks.stats@1.0-service.xml +++ b/services/stats/android.frameworks.stats-service.xml @@ -11,7 +11,7 @@ <hal format="aidl"> <name>android.frameworks.stats</name> - <version>1</version> + <version>2</version> <fqname>IStats/default</fqname> </hal> </manifest> diff --git a/services/stats/include/stats/StatsAidl.h b/services/stats/include/stats/StatsAidl.h index 219e71e7b0..340b53955d 100644 --- a/services/stats/include/stats/StatsAidl.h +++ b/services/stats/include/stats/StatsAidl.h @@ -28,8 +28,7 @@ public: /** * Binder call to get vendor atom. */ - virtual ndk::ScopedAStatus reportVendorAtom( - const VendorAtom& in_vendorAtom) override; + virtual ndk::ScopedAStatus reportVendorAtom(const VendorAtom& in_vendorAtom) override; }; } // namespace stats diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h index 071e54f07c..864ad1457f 100644 --- a/services/stats/include/stats/StatsHal.h +++ b/services/stats/include/stats/StatsHal.h @@ -16,7 +16,6 @@ #include <android/frameworks/stats/1.0/IStats.h> #include <android/frameworks/stats/1.0/types.h> - #include <stats_event.h> using namespace android::frameworks::stats::V1_0; @@ -30,8 +29,8 @@ namespace implementation { using android::hardware::Return; /** -* Implements the Stats HAL -*/ + * Implements the Stats HAL + */ class StatsHal : public IStats { public: StatsHal(); @@ -50,12 +49,12 @@ public: * Binder call to get PhysicalDropDetected atom. */ virtual Return<void> reportPhysicalDropDetected( - const PhysicalDropDetected& physicalDropDetected) override; + const PhysicalDropDetected& physicalDropDetected) override; /** * Binder call to get ChargeCyclesReported atom. */ - virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override; + virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override; /** * Binder call to get BatteryHealthSnapshot atom. @@ -83,8 +82,7 @@ public: /** * Binder call to get Speech DSP state atom. */ - virtual Return<void> reportSpeechDspStat( - const SpeechDspStat& speechDspStat) override; + virtual Return<void> reportSpeechDspStat(const SpeechDspStat& speechDspStat) override; /** * Binder call to get vendor atom. diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 000a2cb0d3..cbb95f963c 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -280,7 +280,7 @@ cc_library_shared { "liblog", ], static_libs: [ - "SurfaceFlingerProperties", + "libSurfaceFlingerProperties", ], export_shared_lib_headers: [ "android.hardware.graphics.common@1.2", @@ -288,6 +288,6 @@ cc_library_shared { "libui", ], export_static_lib_headers: [ - "SurfaceFlingerProperties", + "libSurfaceFlingerProperties", ], } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index 8e4e9af48c..24a7744542 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -150,8 +150,6 @@ public: bool hasSolidColorLayers() const; private: - CachedSet() = default; - const NonBufferHash mFingerprint; std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now(); std::vector<Layer> mLayers; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 79dcd159d3..36512311d1 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -264,17 +264,21 @@ std::string AidlComposer::dumpDebugInfo() { } std::string str; + // Use other thread to read pipe to prevent + // pipe is full, making HWC be blocked in writing. + std::thread t([&]() { + base::ReadFdToString(pipefds[0], &str); + }); const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0); // Close the write-end of the pipe to make sure that when reading from the // read-end we will get eof instead of blocking forever close(pipefds[1]); - if (status == STATUS_OK) { - base::ReadFdToString(pipefds[0], &str); - } else { + if (status != STATUS_OK) { ALOGE("dumpDebugInfo: dump failed: %d", status); } + t.join(); close(pipefds[0]); return str; } diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index c52e96d146..8364ed9d28 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -826,25 +826,24 @@ Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, mHdrMetadata.cta8613.maxFrameAverageLightLevel}}); } - Error error = static_cast<Error>( - mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas)); + const Error error = static_cast<Error>( + mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas)); + if (error != Error::NONE) { + return error; + } + std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs; if (validTypes & HdrMetadata::HDR10PLUS) { if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) { return Error::BAD_PARAMETER; } - std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs; perFrameMetadataBlobs.push_back( {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus}); - Error setMetadataBlobsError = - static_cast<Error>(mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId, - perFrameMetadataBlobs)); - if (error == Error::NONE) { - return setMetadataBlobsError; - } } - return error; + + return static_cast<Error>( + mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId, perFrameMetadataBlobs)); } Error Layer::setDisplayFrame(const Rect& frame) diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index a2305af554..3611deabce 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -314,12 +314,12 @@ public: virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0; // Sets the max number of display frames that can be stored. Called by SF backdoor. - virtual void setMaxDisplayFrames(uint32_t size); + virtual void setMaxDisplayFrames(uint32_t size) = 0; // Computes the historical fps for the provided set of layer IDs // The fps is compted from the linear timeline of present timestamps for DisplayFrames // containing at least one layer ID. - virtual float computeFps(const std::unordered_set<int32_t>& layerIds); + virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0; // Restores the max number of display frames to default. Called by SF backdoor. virtual void reset() = 0; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index aff94d132e..c2b0a112d6 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1587,8 +1587,10 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; + const float parentShadowRadius = + newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius; child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, - newParent->mEffectiveShadowRadius); + parentShadowRadius); } } diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 2ece51c3dc..6011d0d0f7 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -2,6 +2,7 @@ adyabr@google.com alecmouri@google.com chaviw@google.com lpy@google.com +pdwilliams@google.com racarr@google.com scroggo@google.com -vishnun@google.com
\ No newline at end of file +vishnun@google.com diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 13cd304a5f..e23945dc8a 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "VSyncReactor" //#define LOG_NDEBUG 0 +#include <assert.h> #include <cutils/properties.h> #include <log/log.h> #include <utils/Trace.h> diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 494aa2c2cf..52b7620ed4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -328,8 +328,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mCompositionEngine(mFactory.createCompositionEngine()), mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)), mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()), - mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)), mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)), + mInternalDisplayDensity( + getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)), mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)), mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make(*this)) { ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str()); @@ -1960,8 +1961,16 @@ SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() { const auto now = systemTime(); const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod; const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod; - return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0] - : mPreviousPresentFences[1]; + + size_t shift = 0; + if (!expectedPresentTimeIsTheNextVsync) { + shift = static_cast<size_t>((mExpectedPresentTime - now) / vsyncPeriod); + if (shift >= mPreviousPresentFences.size()) { + shift = mPreviousPresentFences.size() - 1; + } + } + ATRACE_FORMAT("previousFrameFence shift=%zu", shift); + return mPreviousPresentFences[shift]; } bool SurfaceFlinger::previousFramePending(int graceTimeMs) { @@ -2129,8 +2138,16 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected bool needsTraversal = false; if (clearTransactionFlags(eTransactionFlushNeeded)) { + // Locking: + // 1. to prevent onHandleDestroyed from being called while the state lock is held, + // we must keep a copy of the transactions (specifically the composer + // states) around outside the scope of the lock + // 2. Transactions and created layers do not share a lock. To prevent applying + // transactions with layers still in the createdLayer queue, flush the transactions + // before committing the created layers. + std::vector<TransactionState> transactions = flushTransactions(); needsTraversal |= commitCreatedLayers(); - needsTraversal |= flushTransactionQueues(vsyncId); + needsTraversal |= applyTransactions(transactions, vsyncId); } const bool shouldCommit = @@ -2447,7 +2464,10 @@ void SurfaceFlinger::postComposition() { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - mPreviousPresentFences[1] = mPreviousPresentFences[0]; + for (size_t i = mPreviousPresentFences.size()-1; i >= 1; i--) { + mPreviousPresentFences[i] = mPreviousPresentFences[i-1]; + } + mPreviousPresentFences[0].fence = display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE; mPreviousPresentFences[0].fenceTime = @@ -3792,7 +3812,7 @@ int SurfaceFlinger::flushPendingTransactionQueues( return transactionsPendingBarrier; } -bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { +std::vector<TransactionState> SurfaceFlinger::flushTransactions() { // to prevent onHandleDestroyed from being called while the lock is held, // we must keep a copy of the transactions (specifically the composer // states) around outside the scope of the lock @@ -3886,14 +3906,25 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { flushUnsignaledPendingTransactionQueues(transactions, bufferLayersReadyToPresent, applyTokensWithUnsignaledTransactions); } - - return applyTransactions(transactions, vsyncId); } } + return transactions; +} + +// for test only +bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { + std::vector<TransactionState> transactions = flushTransactions(); + return applyTransactions(transactions, vsyncId); } bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId) { + Mutex::Autolock _l(mStateLock); + return applyTransactionsLocked(transactions, vsyncId); +} + +bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions, + int64_t vsyncId) { bool needsTraversal = false; // Now apply all transactions. for (auto& transaction : transactions) { @@ -7618,7 +7649,7 @@ status_t SurfaceComposerAIDL::checkControlDisplayBrightnessPermission() { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if ((uid != AID_GRAPHICS) && + if ((uid != AID_GRAPHICS) && (uid != AID_SYSTEM) && !PermissionCache::checkPermission(sControlDisplayBrightness, pid, uid)) { ALOGE("Permission Denial: can't control brightness pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 3a45229ed3..7c44bf45cc 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -773,6 +773,9 @@ private: REQUIRES(mStateLock); // flush pending transaction that was presented after desiredPresentTime. bool flushTransactionQueues(int64_t vsyncId); + + std::vector<TransactionState> flushTransactions(); + // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); @@ -821,7 +824,8 @@ private: size_t totalTXapplied) const; bool stopTransactionProcessing(const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions) const; - bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId) + bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId); + bool applyTransactionsLocked(std::vector<TransactionState>& transactions, int64_t vsyncId) REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) @@ -1228,7 +1232,8 @@ private: std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames; // Tracks layers that need to update a display's dirty region. std::vector<sp<Layer>> mLayersPendingRefresh; - std::array<FenceWithFenceTime, 2> mPreviousPresentFences; + // size should be longest sf-duration / shortest vsync period and round up + std::array<FenceWithFenceTime, 5> mPreviousPresentFences; // currently consider 166hz. // True if in the previous frame at least one layer was composed via the GPU. bool mHadClientComposition = false; // True if in the previous frame at least one layer was composed via HW Composer. @@ -1377,8 +1382,8 @@ private: sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter; ui::DisplayPrimaries mInternalDisplayPrimaries; - const float mInternalDisplayDensity; const float mEmulatedDisplayDensity; + const float mInternalDisplayDensity; // Should only be accessed by the main thread. sp<os::IInputFlinger> mInputFlinger; diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index ec180548e1..883766b334 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -139,11 +139,6 @@ int main(int, char**) { set_sched_policy(0, SP_FOREGROUND); - // Put most SurfaceFlinger threads in the system-background cpuset - // Keeps us from unnecessarily using big cores - // Do this after the binder thread pool init - if (cpusets_enabled()) set_cpuset_policy(0, SP_SYSTEM); - // initialize before clients can connect flinger->init(); diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index ceddf2727a..507601b4e0 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -63,7 +63,7 @@ cc_test { "android.hardware.graphics.composer@2.1", ], shared_libs: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.common@1.2", "libandroid", "libbase", diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index d33bc1080c..434c297cc9 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -52,19 +52,12 @@ const String8 SURFACE_NAME("Test Surface Name"); #pragma clang diagnostic ignored "-Wconversion" class CredentialsTest : public ::testing::Test { protected: - void SetUp() override { - // Start the tests as root. - seteuid(AID_ROOT); - - ASSERT_NO_FATAL_FAILURE(initClient()); - } + void SetUp() override { ASSERT_NO_FATAL_FAILURE(initClient()); } void TearDown() override { mComposerClient->dispose(); mBGSurfaceControl.clear(); mComposerClient.clear(); - // Finish the tests as root. - seteuid(AID_ROOT); } sp<IBinder> mDisplay; @@ -99,31 +92,6 @@ protected: } /** - * Sets UID to imitate Graphic's process. - */ - void setGraphicsUID() { - seteuid(AID_ROOT); - seteuid(AID_GRAPHICS); - } - - /** - * Sets UID to imitate System's process. - */ - void setSystemUID() { - seteuid(AID_ROOT); - seteuid(AID_SYSTEM); - } - - /** - * Sets UID to imitate a process that doesn't have any special privileges in - * our code. - */ - void setBinUID() { - seteuid(AID_ROOT); - seteuid(AID_BIN); - } - - /** * Template function the check a condition for different types of users: root * graphics, system, and non-supported user. Root, graphics, and system should * always equal privilegedValue, and non-supported user should equal unprivilegedValue. @@ -131,24 +99,34 @@ protected: template <typename T> void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) { // Check with root. - seteuid(AID_ROOT); - ASSERT_EQ(privilegedValue, condition()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_EQ(privilegedValue, condition()); + } // Check as a Graphics user. - setGraphicsUID(); - ASSERT_EQ(privilegedValue, condition()); + { + UIDFaker f(AID_GRAPHICS); + ASSERT_EQ(privilegedValue, condition()); + } // Check as a system user. - setSystemUID(); - ASSERT_EQ(privilegedValue, condition()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_EQ(privilegedValue, condition()); + } // Check as a non-supported user. - setBinUID(); - ASSERT_EQ(unprivilegedValue, condition()); + { + UIDFaker f(AID_BIN); + ASSERT_EQ(unprivilegedValue, condition()); + } // Check as shell since shell has some additional permissions - seteuid(AID_SHELL); - ASSERT_EQ(unprivilegedValue, condition()); + { + UIDFaker f(AID_SHELL); + ASSERT_EQ(privilegedValue, condition()); + } } }; @@ -157,17 +135,23 @@ TEST_F(CredentialsTest, ClientInitTest) { ASSERT_NO_FATAL_FAILURE(initClient()); // Graphics can init the client. - setGraphicsUID(); - ASSERT_NO_FATAL_FAILURE(initClient()); + { + UIDFaker f(AID_GRAPHICS); + ASSERT_NO_FATAL_FAILURE(initClient()); + } // System can init the client. - setSystemUID(); - ASSERT_NO_FATAL_FAILURE(initClient()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_NO_FATAL_FAILURE(initClient()); + } // Anyone else can init the client. - setBinUID(); - mComposerClient = new SurfaceComposerClient; - ASSERT_NO_FATAL_FAILURE(initClient()); + { + UIDFaker f(AID_BIN); + mComposerClient = new SurfaceComposerClient; + ASSERT_NO_FATAL_FAILURE(initClient()); + } } TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { @@ -181,7 +165,7 @@ TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { TEST_F(CredentialsTest, AllowedGetterMethodsTest) { // The following methods are tested with a UID that is not root, graphics, // or system, to show that anyone can access them. - setBinUID(); + UIDFaker f(AID_BIN); const auto display = SurfaceComposerClient::getInternalDisplayToken(); ASSERT_TRUE(display != nullptr); @@ -250,24 +234,34 @@ TEST_F(CredentialsTest, CreateDisplayTest) { }; // Check with root. - seteuid(AID_ROOT); - ASSERT_FALSE(condition()); + { + UIDFaker f(AID_ROOT); + ASSERT_FALSE(condition()); + } // Check as a Graphics user. - setGraphicsUID(); - ASSERT_TRUE(condition()); + { + UIDFaker f(AID_GRAPHICS); + ASSERT_TRUE(condition()); + } // Check as a system user. - setSystemUID(); - ASSERT_TRUE(condition()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_TRUE(condition()); + } // Check as a non-supported user. - setBinUID(); - ASSERT_FALSE(condition()); + { + UIDFaker f(AID_BIN); + ASSERT_FALSE(condition()); + } // Check as shell since shell has some additional permissions - seteuid(AID_SHELL); - ASSERT_FALSE(condition()); + { + UIDFaker f(AID_SHELL); + ASSERT_FALSE(condition()); + } condition = [=]() { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); @@ -313,17 +307,22 @@ TEST_F(CredentialsTest, GetLayerDebugInfo) { // is called when we call dumpsys. I don't see a reason why we should change this. std::vector<LayerDebugInfo> outLayers; // Check with root. - seteuid(AID_ROOT); - ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + { + UIDFaker f(AID_ROOT); + ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + } // Check as a shell. - seteuid(AID_SHELL); - ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + { + UIDFaker f(AID_SHELL); + ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + } // Check as anyone else. - seteuid(AID_ROOT); - seteuid(AID_BIN); - ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers)); + { + UIDFaker f(AID_BIN); + ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers)); + } } TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) { diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp index fb4458a27e..916267447b 100644 --- a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp +++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp @@ -80,14 +80,6 @@ void toggleOverlay(bool enabled) { } // namespace -TEST(RefreshRateOverlayTest, enableOverlay) { - toggleOverlay(true); -} - -TEST(RefreshRateOverlayTest, disableOverlay) { - toggleOverlay(false); -} - TEST(RefreshRateOverlayTest, enableAndDisableOverlay) { toggleOverlay(true); toggleOverlay(false); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 004f31c562..339d746fac 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -138,7 +138,7 @@ cc_defaults { static_libs: [ "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", diff --git a/services/utils/tests/Android.bp b/services/utils/tests/Android.bp index 54cf5b7404..cfa8a08c66 100644 --- a/services/utils/tests/Android.bp +++ b/services/utils/tests/Android.bp @@ -34,5 +34,4 @@ cc_test { "libgmock", "libserviceutils", ], - clang: true, } diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp index 2002bdf628..5403baf2e7 100644 --- a/services/vibratorservice/Android.bp +++ b/services/vibratorservice/Android.bp @@ -59,6 +59,12 @@ cc_library_shared { "-Wunreachable-code", ], + // FIXME: Workaround LTO build breakage + // http://b/241699694 + lto: { + never: true, + }, + local_include_dirs: ["include"], export_include_dirs: ["include"], diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp deleted file mode 100644 index 80e9a3c3b1..0000000000 --- a/services/vr/hardware_composer/Android.bp +++ /dev/null @@ -1,134 +0,0 @@ -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_shared { - name: "libvr_hwc-hal", - - system_ext_specific: true, - - srcs: [ - "impl/vr_hwc.cpp", - "impl/vr_composer_client.cpp", - ], - - static_libs: [ - "libbroadcastring", - "libdisplay", - ], - - shared_libs: [ - "android.frameworks.vr.composer@2.0", - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.2", - "android.hardware.graphics.composer@2.3", - "android.hardware.graphics.composer@2.1-resources", - "android.hardware.graphics.mapper@2.0", - "android.hardware.graphics.mapper@3.0", - "android.hardware.graphics.mapper@4.0", - "libbase", - "libbufferhubqueue", - "libbinder", - "libcutils", - "libfmq", - "libhardware", - "libhidlbase", - "liblog", - "libsync", - "libui", - "libutils", - "libpdx_default_transport", - ], - - header_libs: [ - "android.hardware.graphics.composer@2.1-command-buffer", - "android.hardware.graphics.composer@2.3-hal", - ], - - export_header_lib_headers: [ - "android.hardware.graphics.composer@2.3-hal", - ], - - export_static_lib_headers: [ - "libdisplay", - ], - - export_shared_lib_headers: [ - "android.frameworks.vr.composer@2.0", - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.2", - "android.hardware.graphics.composer@2.3", - ], - - export_include_dirs: ["."], - - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-Wall", - "-Werror", - "-Wno-error=unused-private-field", - // Warnings in vr_hwc.cpp to be fixed after sync of goog/master. - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - -} - -cc_library_static { - name: "libvr_hwc-impl", - srcs: [ - "vr_composer.cpp", - ], - static_libs: [ - "libvr_hwc-binder", - ], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libui", - "libutils", - "libvr_hwc-hal", - ], - export_shared_lib_headers: [ - "libvr_hwc-hal", - ], - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-Wall", - "-Werror", - ], -} - -cc_test { - name: "vr_hwc_test", - gtest: true, - srcs: ["tests/vr_composer_test.cpp"], - static_libs: [ - "libgtest", - "libvr_hwc-impl", - // NOTE: This needs to be included after the *-impl lib otherwise the - // symbols in the *-binder library get optimized out. - "libvr_hwc-binder", - ], - cflags: [ - "-Wall", - "-Werror", - // warnings in vr_composer_test.cpp to be fixed after merge of goog/master - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libui", - "libutils", - ], -} diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp deleted file mode 100644 index fa71ed7633..0000000000 --- a/services/vr/hardware_composer/aidl/Android.bp +++ /dev/null @@ -1,36 +0,0 @@ -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_static { - name: "libvr_hwc-binder", - srcs: [ - "android/dvr/IVrComposer.aidl", - "android/dvr/IVrComposerCallback.aidl", - "android/dvr/parcelable_composer_frame.cpp", - "android/dvr/parcelable_composer_layer.cpp", - "android/dvr/parcelable_unique_fd.cpp", - ], - aidl: { - local_include_dirs: ["."], - export_aidl_headers: true, - }, - export_include_dirs: ["."], - - cflags: [ - "-Wall", - "-Werror", - ], - - shared_libs: [ - "libbinder", - "libui", - "libutils", - "libvr_hwc-hal", - ], -} diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl deleted file mode 100644 index be1ec5b2a3..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl +++ /dev/null @@ -1,25 +0,0 @@ -package android.dvr; - -import android.dvr.IVrComposerCallback; - -/** - * Service interface exposed by VR HWC exposed to system apps which allows one - * system app to connect to get SurfaceFlinger's outputs (all displays). This - * is active when SurfaceFlinger is in VR mode, where all 2D output is - * redirected to VR HWC. - * - * @hide */ -interface IVrComposer -{ - const String SERVICE_NAME = "vr_hwc"; - - /** - * Registers a callback used to receive frame notifications. - */ - void registerObserver(in IVrComposerCallback callback); - - /** - * Clears a previously registered frame notification callback. - */ - void clearObserver(); -} diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl deleted file mode 100644 index aa70de1645..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl +++ /dev/null @@ -1,22 +0,0 @@ -package android.dvr; - -import android.dvr.ParcelableComposerFrame; -import android.dvr.ParcelableUniqueFd; - -/** - * A system app will implement and register this callback with VRComposer - * to receive the layers SurfaceFlinger presented when in VR mode. - * - * @hide */ -interface IVrComposerCallback { - /** - * Called by the VR HWC service when a new frame is ready to be presented. - * - * @param frame The new frame VR HWC wants to present. - * @return A fence FD used to signal when the previous frame is no longer - * used by the client. This may be an invalid fence (-1) if the client is not - * using the previous frame, in which case the previous frame may be re-used - * at any point in time. - */ - ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame); -} diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl deleted file mode 100644 index 84abc19c23..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.dvr; - -parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h"; diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl deleted file mode 100644 index a200345fbd..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.dvr; - -parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h"; diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl deleted file mode 100644 index eee9d138ba..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.dvr; - -parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h"; diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp deleted file mode 100644 index db7d5dc225..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "aidl/android/dvr/parcelable_composer_frame.h" - -#include <binder/Parcel.h> - -#include "aidl/android/dvr/parcelable_composer_layer.h" - -namespace android { -namespace dvr { - -ParcelableComposerFrame::ParcelableComposerFrame() {} - -ParcelableComposerFrame::ParcelableComposerFrame( - const ComposerView::Frame& frame) - : frame_(frame) {} - -ParcelableComposerFrame::~ParcelableComposerFrame() {} - -status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const { - status_t ret = parcel->writeUint64(frame_.display_id); - if (ret != OK) return ret; - - ret = parcel->writeInt32(frame_.display_width); - if (ret != OK) return ret; - - ret = parcel->writeInt32(frame_.display_height); - if (ret != OK) return ret; - - ret = parcel->writeBool(frame_.removed); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config)); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode)); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode)); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled)); - if (ret != OK) return ret; - - ret = parcel->writeInt32(frame_.color_transform_hint); - if (ret != OK) return ret; - - for(size_t i = 0; i < 16; i++) { - ret = parcel->writeFloat(frame_.color_transform[i]); - if (ret != OK) return ret; - } - - std::vector<ParcelableComposerLayer> layers; - for (size_t i = 0; i < frame_.layers.size(); ++i) - layers.push_back(ParcelableComposerLayer(frame_.layers[i])); - - ret = parcel->writeParcelableVector(layers); - - return ret; -} - -status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) { - status_t ret = parcel->readUint64(&frame_.display_id); - if (ret != OK) return ret; - - ret = parcel->readInt32(&frame_.display_width); - if (ret != OK) return ret; - - ret = parcel->readInt32(&frame_.display_height); - if (ret != OK) return ret; - - ret = parcel->readBool(&frame_.removed); - if (ret != OK) return ret; - - uint32_t value; - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.active_config = static_cast<Config>(value); - - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.color_mode = static_cast<ColorMode>(value); - - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.power_mode = static_cast<IComposerClient::PowerMode>(value); - - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value); - - ret = parcel->readInt32(&frame_.color_transform_hint); - if (ret != OK) return ret; - - for(size_t i = 0; i < 16; i++) { - ret = parcel->readFloat(&frame_.color_transform[i]); - if (ret != OK) return ret; - } - - std::vector<ParcelableComposerLayer> layers; - ret = parcel->readParcelableVector(&layers); - if (ret != OK) return ret; - - frame_.layers.clear(); - for (size_t i = 0; i < layers.size(); ++i) - frame_.layers.push_back(layers[i].layer()); - - return ret; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h deleted file mode 100644 index a82df7f2e7..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H -#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H - -#include <binder/Parcelable.h> -#include <impl/vr_hwc.h> - -namespace android { -namespace dvr { - -class ParcelableComposerFrame : public Parcelable { - public: - ParcelableComposerFrame(); - explicit ParcelableComposerFrame(const ComposerView::Frame& frame); - ~ParcelableComposerFrame() override; - - ComposerView::Frame frame() const { return frame_; } - - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - private: - ComposerView::Frame frame_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp deleted file mode 100644 index c3621ebf0f..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include "aidl/android/dvr/parcelable_composer_layer.h" - -#include <binder/Parcel.h> -#include <ui/Fence.h> -#include <ui/GraphicBuffer.h> -#include <ui/GraphicBufferMapper.h> - -namespace android { -namespace dvr { - -ParcelableComposerLayer::ParcelableComposerLayer() {} - -ParcelableComposerLayer::ParcelableComposerLayer( - const ComposerView::ComposerLayer& layer) : layer_(layer) {} - -ParcelableComposerLayer::~ParcelableComposerLayer() {} - -status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const { - status_t ret = parcel->writeUint64(layer_.id); - if (ret != OK) return ret; - - ret = parcel->write(*layer_.buffer); - if (ret != OK) return ret; - - ret = parcel->writeBool(layer_.fence->isValid()); - if (ret != OK) return ret; - - if (layer_.fence->isValid()) { - ret = parcel->writeFileDescriptor(layer_.fence->dup(), true); - if (ret != OK) return ret; - } - - ret = parcel->writeInt32(layer_.display_frame.left); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.display_frame.top); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.display_frame.right); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.display_frame.bottom); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.left); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.top); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.right); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.bottom); - if (ret != OK) return ret; - - ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode)); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.alpha); - if (ret != OK) return ret; - - ret = parcel->writeUint32(layer_.type); - if (ret != OK) return ret; - - ret = parcel->writeUint32(layer_.app_id); - if (ret != OK) return ret; - - ret = parcel->writeUint32(layer_.z_order); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.cursor_x); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.cursor_y); - if (ret != OK) return ret; - - uint32_t color = layer_.color.r | - (static_cast<uint32_t>(layer_.color.g) << 8) | - (static_cast<uint32_t>(layer_.color.b) << 16) | - (static_cast<uint32_t>(layer_.color.a) << 24); - ret = parcel->writeUint32(color); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.dataspace); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.transform); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size())); - if (ret != OK) return ret; - - for (auto& rect: layer_.visible_regions) { - ret = parcel->writeInt32(rect.left); - ret = parcel->writeInt32(rect.top); - ret = parcel->writeInt32(rect.right); - ret = parcel->writeInt32(rect.bottom); - if (ret != OK) return ret; - } - - ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size())); - if (ret != OK) return ret; - - for (auto& rect: layer_.damaged_regions) { - ret = parcel->writeInt32(rect.left); - ret = parcel->writeInt32(rect.top); - ret = parcel->writeInt32(rect.right); - ret = parcel->writeInt32(rect.bottom); - if (ret != OK) return ret; - } - - return OK; -} - -status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) { - status_t ret = parcel->readUint64(&layer_.id); - if (ret != OK) return ret; - - layer_.buffer = new GraphicBuffer(); - ret = parcel->read(*layer_.buffer); - if (ret != OK) { - layer_.buffer.clear(); - return ret; - } - - bool has_fence = 0; - ret = parcel->readBool(&has_fence); - if (ret != OK) return ret; - - if (has_fence) - layer_.fence = new Fence(dup(parcel->readFileDescriptor())); - else - layer_.fence = new Fence(); - - ret = parcel->readInt32(&layer_.display_frame.left); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.display_frame.top); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.display_frame.right); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.display_frame.bottom); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.left); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.top); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.right); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.bottom); - if (ret != OK) return ret; - - ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode)); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.alpha); - if (ret != OK) return ret; - - ret = parcel->readUint32(&layer_.type); - if (ret != OK) return ret; - - ret = parcel->readUint32(&layer_.app_id); - if (ret != OK) return ret; - - ret = parcel->readUint32(&layer_.z_order); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.cursor_x); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.cursor_y); - if (ret != OK) return ret; - - uint32_t color; - ret = parcel->readUint32(&color); - if (ret != OK) return ret; - layer_.color.r = color & 0xFF; - layer_.color.g = (color >> 8) & 0xFF; - layer_.color.b = (color >> 16) & 0xFF; - layer_.color.a = (color >> 24) & 0xFF; - - ret = parcel->readInt32(&layer_.dataspace); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.transform); - if (ret != OK) return ret; - - uint32_t size; - ret = parcel->readUint32(&size); - if (ret != OK) return ret; - - for(size_t i = 0; i < size; i++) { - hwc_rect_t rect; - ret = parcel->readInt32(&rect.left); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.top); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.right); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.bottom); - if (ret != OK) return ret; - - layer_.visible_regions.push_back(rect); - } - - ret = parcel->readUint32(&size); - if (ret != OK) return ret; - - for(size_t i = 0; i < size; i++) { - hwc_rect_t rect; - ret = parcel->readInt32(&rect.left); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.top); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.right); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.bottom); - if (ret != OK) return ret; - - layer_.damaged_regions.push_back(rect); - } - - return OK; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h deleted file mode 100644 index 6d2ac097e5..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H -#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H - -#include <binder/Parcelable.h> -#include <impl/vr_hwc.h> - -#include <memory> - -namespace android { -namespace dvr { - -class ParcelableComposerLayer : public Parcelable { - public: - ParcelableComposerLayer(); - explicit ParcelableComposerLayer(const ComposerView::ComposerLayer& layer); - ~ParcelableComposerLayer() override; - - ComposerView::ComposerLayer layer() const { return layer_; } - - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - private: - ComposerView::ComposerLayer layer_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp deleted file mode 100644 index 9486f3c919..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "android/dvr/parcelable_unique_fd.h" - -#include <binder/Parcel.h> - -namespace android { -namespace dvr { - -ParcelableUniqueFd::ParcelableUniqueFd() {} - -ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence) - : fence_(dup(fence.get())) {} - -ParcelableUniqueFd::~ParcelableUniqueFd() {} - -status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const { - status_t ret = parcel->writeBool(fence_.get() >= 0); - if (ret != OK) return ret; - - if (fence_.get() >= 0) - ret = parcel->writeUniqueFileDescriptor(fence_); - - return ret; -} - -status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) { - bool has_fence = 0; - status_t ret = parcel->readBool(&has_fence); - if (ret != OK) return ret; - - if (has_fence) - ret = parcel->readUniqueFileDescriptor(&fence_); - - return ret; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h deleted file mode 100644 index c4216f6212..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H -#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H - -#include <android-base/unique_fd.h> -#include <binder/Parcelable.h> - -namespace android { -namespace dvr { - -// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the -// case where the FD is invalid (-1), unlike FileDescriptor which expects a -// valid FD. -class ParcelableUniqueFd : public Parcelable { - public: - ParcelableUniqueFd(); - explicit ParcelableUniqueFd(const base::unique_fd& fence); - ~ParcelableUniqueFd() override; - - void set_fence(const base::unique_fd& fence) { - fence_.reset(dup(fence.get())); - } - base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); } - - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - private: - base::unique_fd fence_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp deleted file mode 100644 index dd1603d4a9..0000000000 --- a/services/vr/hardware_composer/impl/vr_composer_client.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2016 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 <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#include <hardware/gralloc.h> -#include <hardware/gralloc1.h> -#include <log/log.h> - -#include <memory> - -#include "impl/vr_hwc.h" -#include "impl/vr_composer_client.h" - -namespace android { -namespace dvr { - -using android::frameworks::vr::composer::V2_0::IVrComposerClient; - -VrComposerClient::VrComposerClient(dvr::VrHwc& hal) - : ComposerClient(&hal), mVrHal(hal) { - if (!init()) { - LOG_ALWAYS_FATAL("failed to initialize VrComposerClient"); - } -} - -VrComposerClient::~VrComposerClient() {} - -std::unique_ptr<ComposerCommandEngine> -VrComposerClient::createCommandEngine() { - return std::make_unique<VrCommandEngine>(*this); -} - -VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client) - : ComposerCommandEngine(client.mHal, client.mResources.get()), - mVrHal(client.mVrHal) {} - -VrComposerClient::VrCommandEngine::~VrCommandEngine() {} - -bool VrComposerClient::VrCommandEngine::executeCommand( - hardware::graphics::composer::V2_1::IComposerClient::Command command, - uint16_t length) { - IVrComposerClient::VrCommand vrCommand = - static_cast<IVrComposerClient::VrCommand>(command); - switch (vrCommand) { - case IVrComposerClient::VrCommand::SET_LAYER_INFO: - return executeSetLayerInfo(length); - case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA: - return executeSetClientTargetMetadata(length); - case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA: - return executeSetLayerBufferMetadata(length); - default: - return ComposerCommandEngine::executeCommand(command, length); - } -} - -bool VrComposerClient::VrCommandEngine::executeSetLayerInfo(uint16_t length) { - if (length != 2) { - return false; - } - - auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read()); - if (err != Error::NONE) { - mWriter->setError(getCommandLoc(), err); - } - - return true; -} - -bool VrComposerClient::VrCommandEngine::executeSetClientTargetMetadata( - uint16_t length) { - if (length != 7) - return false; - - auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata()); - if (err != Error::NONE) - mWriter->setError(getCommandLoc(), err); - - return true; -} - -bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata( - uint16_t length) { - if (length != 7) - return false; - - auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer, - readBufferMetadata()); - if (err != Error::NONE) - mWriter->setError(getCommandLoc(), err); - - return true; -} - -IVrComposerClient::BufferMetadata -VrComposerClient::VrCommandEngine::readBufferMetadata() { - IVrComposerClient::BufferMetadata metadata = { - .width = read(), - .height = read(), - .stride = read(), - .layerCount = read(), - .format = - static_cast<android::hardware::graphics::common::V1_2::PixelFormat>( - readSigned()), - .usage = read64(), - }; - return metadata; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h deleted file mode 100644 index 1b2b5f4f56..0000000000 --- a/services/vr/hardware_composer/impl/vr_composer_client.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2017 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. - */ - -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H -#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H - -#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#include <composer-command-buffer/2.3/ComposerCommandBuffer.h> -#include <composer-hal/2.1/ComposerClient.h> -#include <composer-hal/2.1/ComposerCommandEngine.h> -#include <composer-hal/2.2/ComposerClient.h> -#include <composer-hal/2.3/ComposerClient.h> - -namespace android { -namespace dvr { - -class VrHwc; - -using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine; -using hardware::graphics::composer::V2_3::hal::ComposerHal; -using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl; - -using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>; - -class VrComposerClient : public ComposerClient { - public: - explicit VrComposerClient(android::dvr::VrHwc& hal); - virtual ~VrComposerClient(); - - private: - class VrCommandEngine : public ComposerCommandEngine { - public: - explicit VrCommandEngine(VrComposerClient& client); - ~VrCommandEngine() override; - - bool executeCommand( - hardware::graphics::composer::V2_1::IComposerClient::Command command, - uint16_t length) override; - - private: - bool executeSetLayerInfo(uint16_t length); - bool executeSetClientTargetMetadata(uint16_t length); - bool executeSetLayerBufferMetadata(uint16_t length); - - IVrComposerClient::BufferMetadata readBufferMetadata(); - - android::dvr::VrHwc& mVrHal; - - VrCommandEngine(const VrCommandEngine&) = delete; - void operator=(const VrCommandEngine&) = delete; - }; - - VrComposerClient(const VrComposerClient&) = delete; - void operator=(const VrComposerClient&) = delete; - - std::unique_ptr<ComposerCommandEngine> createCommandEngine() override; - dvr::VrHwc& mVrHal; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp deleted file mode 100644 index e530b16b1b..0000000000 --- a/services/vr/hardware_composer/impl/vr_hwc.cpp +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * Copyright 2016 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 "impl/vr_hwc.h" - -#include "android-base/stringprintf.h" -#include <binder/IServiceManager.h> -#include <cutils/properties.h> -#include <private/dvr/display_client.h> -#include <ui/Fence.h> -#include <utils/Trace.h> - -#include <mutex> - -#include "vr_composer_client.h" - -using namespace android::hardware::graphics::common::V1_0; -using namespace android::hardware::graphics::composer::V2_3; - -using android::base::StringPrintf; -using android::hardware::hidl_handle; -using android::hardware::hidl_string; -using android::hardware::hidl_vec; -using android::hardware::Return; -using android::hardware::Void; - -namespace types = android::hardware::graphics::common; - -namespace android { -namespace dvr { -namespace { - -const Display kDefaultDisplayId = 1; -const Config kDefaultConfigId = 1; - -sp<GraphicBuffer> CreateGraphicBuffer( - const native_handle_t* handle, - const IVrComposerClient::BufferMetadata& metadata) { - sp<GraphicBuffer> buffer = new GraphicBuffer( - handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height, - static_cast<int32_t>(metadata.format), metadata.layerCount, - metadata.usage, metadata.stride); - if (buffer->initCheck() != OK) { - ALOGE("Failed to create graphic buffer"); - return nullptr; - } - - return buffer; -} - -void GetPrimaryDisplaySize(int32_t* width, int32_t* height) { - *width = 1080; - *height = 1920; - - int error = 0; - auto display_client = display::DisplayClient::Create(&error); - if (!display_client) { - ALOGE("Could not connect to display service : %s(%d)", strerror(error), - error); - return; - } - - auto status = display_client->GetDisplayMetrics(); - if (!status) { - ALOGE("Could not get display metrics from display service : %s(%d)", - status.GetErrorMessage().c_str(), status.error()); - return; - } - - *width = status.get().display_width; - *height = status.get().display_height; -} - -} // namespace - -HwcDisplay::HwcDisplay(int32_t width, int32_t height) - : width_(width), height_(height) {} - -HwcDisplay::~HwcDisplay() {} - -bool HwcDisplay::SetClientTarget(const native_handle_t* handle, - base::unique_fd fence) { - if (handle) - buffer_ = CreateGraphicBuffer(handle, buffer_metadata_); - - fence_ = new Fence(fence.release()); - return true; -} - -void HwcDisplay::SetClientTargetMetadata( - const IVrComposerClient::BufferMetadata& metadata) { - buffer_metadata_ = metadata; -} - -HwcLayer* HwcDisplay::CreateLayer() { - uint64_t layer_id = layer_ids_++; - layers_.push_back(HwcLayer(layer_id)); - return &layers_.back(); -} - -HwcLayer* HwcDisplay::GetLayer(Layer id) { - for (size_t i = 0; i < layers_.size(); ++i) - if (layers_[i].info.id == id) - return &layers_[i]; - - return nullptr; -} - -bool HwcDisplay::DestroyLayer(Layer id) { - for (auto it = layers_.begin(); it != layers_.end(); ++it) { - if (it->info.id == id) { - layers_.erase(it); - return true; - } - } - - return false; -} - -void HwcDisplay::GetChangedCompositionTypes( - std::vector<Layer>* layer_ids, - std::vector<IComposerClient::Composition>* types) { - std::sort(layers_.begin(), layers_.end(), - [](const auto& lhs, const auto& rhs) { - return lhs.info.z_order < rhs.info.z_order; - }); - - const size_t no_layer = std::numeric_limits<size_t>::max(); - size_t first_client_layer = no_layer, last_client_layer = no_layer; - for (size_t i = 0; i < layers_.size(); ++i) { - switch (layers_[i].composition_type) { - case IComposerClient::Composition::SOLID_COLOR: - case IComposerClient::Composition::CURSOR: - case IComposerClient::Composition::SIDEBAND: - if (first_client_layer == no_layer) - first_client_layer = i; - - last_client_layer = i; - break; - default: - break; - } - } - - for (size_t i = 0; i < layers_.size(); ++i) { - if (i >= first_client_layer && i <= last_client_layer) { - if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) { - layer_ids->push_back(layers_[i].info.id); - types->push_back(IComposerClient::Composition::CLIENT); - layers_[i].composition_type = IComposerClient::Composition::CLIENT; - } - - continue; - } - - if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) { - layer_ids->push_back(layers_[i].info.id); - types->push_back(IComposerClient::Composition::DEVICE); - layers_[i].composition_type = IComposerClient::Composition::DEVICE; - } - } -} - -Error HwcDisplay::GetFrame( - std::vector<ComposerView::ComposerLayer>* out_frames) { - bool queued_client_target = false; - std::vector<ComposerView::ComposerLayer> frame; - for (const auto& layer : layers_) { - if (layer.composition_type == IComposerClient::Composition::CLIENT) { - if (queued_client_target) - continue; - - if (!buffer_.get()) { - ALOGE("Client composition requested but no client target buffer"); - return Error::BAD_LAYER; - } - - ComposerView::ComposerLayer client_target_layer = { - .buffer = buffer_, - .fence = fence_.get() ? fence_ : new Fence(-1), - .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()), - static_cast<int32_t>(buffer_->getHeight())}, - .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()), - static_cast<float>(buffer_->getHeight())}, - .blend_mode = IComposerClient::BlendMode::NONE, - }; - - frame.push_back(client_target_layer); - queued_client_target = true; - } else { - if (!layer.info.buffer.get() || !layer.info.fence.get()) { - ALOGV("Layer requested without valid buffer"); - continue; - } - - frame.push_back(layer.info); - } - } - - out_frames->swap(frame); - return Error::NONE; -} - -std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() { - std::vector<Layer> last_frame_layers; - last_frame_layers.swap(last_frame_layers_ids_); - - for (const auto& layer : layers_) - last_frame_layers_ids_.push_back(layer.info.id); - - return last_frame_layers; -} - -void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) { - color_transform_hint_ = hint; - if (matrix) - memcpy(color_transform_, matrix, sizeof(color_transform_)); -} - -void HwcDisplay::dumpDebugInfo(std::string* result) const { - if (!result) { - return; - } - *result += StringPrintf("HwcDisplay: width: %d, height: %d, layers size: %zu, colormode: %d\ - , config: %d\n", width_, height_, layers_.size(), color_mode_, active_config_); - *result += StringPrintf("HwcDisplay buffer metadata: width: %d, height: %d, stride: %d,\ - layerCount: %d, pixelFormat: %d\n", buffer_metadata_.width, buffer_metadata_.height, - buffer_metadata_.stride, buffer_metadata_.layerCount, buffer_metadata_.format); - for (const auto& layer : layers_) { - layer.dumpDebugInfo(result); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// VrHwcClient - -VrHwc::VrHwc() { - vsync_callback_ = new VsyncCallback; -} - -VrHwc::~VrHwc() { - vsync_callback_->SetEventCallback(nullptr); -} - -bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; } - -void VrHwc::registerEventCallback(EventCallback* callback) { - std::unique_lock<std::mutex> lock(mutex_); - event_callback_ = callback; - int32_t width, height; - GetPrimaryDisplaySize(&width, &height); - // Create the primary display late to avoid initialization issues between - // VR HWC and SurfaceFlinger. - displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height)); - - // Surface flinger will make calls back into vr_hwc when it receives the - // onHotplug() call, so it's important to release mutex_ here. - lock.unlock(); - event_callback_->onHotplug(kDefaultDisplayId, - hardware::graphics::composer::V2_1:: - IComposerCallback::Connection::CONNECTED); - lock.lock(); - UpdateVsyncCallbackEnabledLocked(); -} - -void VrHwc::unregisterEventCallback() { - std::lock_guard<std::mutex> guard(mutex_); - event_callback_ = nullptr; - UpdateVsyncCallbackEnabledLocked(); -} - -uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; } - -Error VrHwc::destroyVirtualDisplay(Display display) { - std::lock_guard<std::mutex> guard(mutex_); - if (display == kDefaultDisplayId || displays_.erase(display) == 0) - return Error::BAD_DISPLAY; - ComposerView::Frame frame; - frame.display_id = display; - frame.removed = true; - if (observer_) - observer_->OnNewFrame(frame); - return Error::NONE; -} - -Error VrHwc::createLayer(Display display, Layer* outLayer) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* layer = display_ptr->CreateLayer(); - *outLayer = layer->info.id; - return Error::NONE; -} - -Error VrHwc::destroyLayer(Display display, Layer layer) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) { - return Error::BAD_DISPLAY; - } - - return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER; -} - -Error VrHwc::getActiveConfig(Display display, Config* outConfig) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - *outConfig = kDefaultConfigId; - return Error::NONE; -} - -Error VrHwc::getDisplayAttribute(Display display, Config config, - IComposerClient::Attribute attribute, - int32_t* outValue) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) { - return Error::BAD_DISPLAY; - } - if (config != kDefaultConfigId) { - return Error::BAD_CONFIG; - } - - switch (attribute) { - case IComposerClient::Attribute::WIDTH: - *outValue = display_ptr->width(); - break; - case IComposerClient::Attribute::HEIGHT: - *outValue = display_ptr->height(); - break; - case IComposerClient::Attribute::VSYNC_PERIOD: - { - int error = 0; - auto display_client = display::DisplayClient::Create(&error); - if (!display_client) { - ALOGE("Could not connect to display service : %s(%d)", - strerror(error), error); - // Return a default value of 30 fps - *outValue = 1000 * 1000 * 1000 / 30; - } else { - auto metrics = display_client->GetDisplayMetrics(); - *outValue = metrics.get().vsync_period_ns; - } - } - break; - case IComposerClient::Attribute::DPI_X: - case IComposerClient::Attribute::DPI_Y: - { - constexpr int32_t kDefaultDPI = 300; - int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI); - if (dpi <= 0) { - dpi = kDefaultDPI; - } - *outValue = 1000 * dpi; - } - break; - default: - return Error::BAD_PARAMETER; - } - - return Error::NONE; -} - -Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - std::vector<Config> configs(1, kDefaultConfigId); - *outConfigs = hidl_vec<Config>(configs); - return Error::NONE; -} - -Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) { - *outName = hidl_string(); - return Error::NONE; -} - -Error VrHwc::getDisplayType(Display display, - IComposerClient::DisplayType* outType) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) { - *outType = IComposerClient::DisplayType::INVALID; - return Error::BAD_DISPLAY; - } - - if (display == kDefaultDisplayId) - *outType = IComposerClient::DisplayType::PHYSICAL; - else - *outType = IComposerClient::DisplayType::VIRTUAL; - - return Error::NONE; -} - -Error VrHwc::getDozeSupport(Display display, bool* outSupport) { - *outSupport = false; - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - return Error::NONE; -} - -Error VrHwc::setActiveConfig(Display display, Config config) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - if (config != kDefaultConfigId) - return Error::BAD_CONFIG; - - display_ptr->set_active_config(config); - return Error::NONE; -} - -Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (enabled != IComposerClient::Vsync::ENABLE && - enabled != IComposerClient::Vsync::DISABLE) { - return Error::BAD_PARAMETER; - } - - Error set_vsync_result = Error::NONE; - if (display == kDefaultDisplayId) { - sp<IVsyncService> vsync_service = interface_cast<IVsyncService>( - defaultServiceManager()->getService( - String16(IVsyncService::GetServiceName()))); - if (vsync_service == nullptr) { - ALOGE("Failed to get vsync service"); - return Error::NO_RESOURCES; - } - - if (enabled == IComposerClient::Vsync::ENABLE) { - ALOGI("Enable vsync"); - display_ptr->set_vsync_enabled(true); - status_t result = vsync_service->registerCallback(vsync_callback_); - if (result != OK) { - ALOGE("%s service registerCallback() failed: %s (%d)", - IVsyncService::GetServiceName(), strerror(-result), result); - set_vsync_result = Error::NO_RESOURCES; - } - } else if (enabled == IComposerClient::Vsync::DISABLE) { - ALOGI("Disable vsync"); - display_ptr->set_vsync_enabled(false); - status_t result = vsync_service->unregisterCallback(vsync_callback_); - if (result != OK) { - ALOGE("%s service unregisterCallback() failed: %s (%d)", - IVsyncService::GetServiceName(), strerror(-result), result); - set_vsync_result = Error::NO_RESOURCES; - } - } - - UpdateVsyncCallbackEnabledLocked(); - } - - return set_vsync_result; -} - -Error VrHwc::setColorTransform(Display display, const float* matrix, - int32_t hint) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - display_ptr->SetColorTransform(matrix, hint); - return Error::NONE; -} - -Error VrHwc::setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t /* dataspace */, - const std::vector<hwc_rect_t>& /* damage */) { - base::unique_fd fence(acquireFence); - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (target == nullptr) - return Error::NONE; - - if (!display_ptr->SetClientTarget(target, std::move(fence))) - return Error::BAD_PARAMETER; - - return Error::NONE; -} - -Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */, - int32_t releaseFence) { - base::unique_fd fence(releaseFence); - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - // TODO(dnicoara): Is it necessary to do anything here? - return Error::NONE; -} - -Error VrHwc::validateDisplay( - Display display, std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* /* outDisplayRequestMask */, - std::vector<Layer>* /* outRequestedLayers */, - std::vector<uint32_t>* /* outRequestMasks */) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - display_ptr->GetChangedCompositionTypes(outChangedLayers, - outCompositionTypes); - return Error::NONE; -} - -Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; } - -Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence, - std::vector<Layer>* outLayers, - std::vector<int32_t>* outReleaseFences) { - *outPresentFence = -1; - outLayers->clear(); - outReleaseFences->clear(); - - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - - if (!display_ptr) - return Error::BAD_DISPLAY; - - ComposerView::Frame frame; - std::vector<Layer> last_frame_layers; - Error status = display_ptr->GetFrame(&frame.layers); - frame.display_id = display; - frame.display_width = display_ptr->width(); - frame.display_height = display_ptr->height(); - frame.active_config = display_ptr->active_config(); - frame.power_mode = display_ptr->power_mode(); - frame.vsync_enabled = display_ptr->vsync_enabled() ? - IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE; - frame.color_transform_hint = display_ptr->color_transform_hint(); - frame.color_mode = display_ptr->color_mode(); - memcpy(frame.color_transform, display_ptr->color_transform(), - sizeof(frame.color_transform)); - if (status != Error::NONE) - return status; - - last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers(); - - base::unique_fd fence; - if (observer_) - fence = observer_->OnNewFrame(frame); - - if (fence.get() < 0) - return Error::NONE; - - *outPresentFence = dup(fence.get()); - outLayers->swap(last_frame_layers); - for (size_t i = 0; i < outLayers->size(); ++i) - outReleaseFences->push_back(dup(fence.get())); - - return Error::NONE; -} - -Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x, - int32_t y) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.cursor_x = x; - hwc_layer->info.cursor_y = y; - return Error::NONE; -} - -Error VrHwc::setLayerBuffer(Display display, Layer layer, - buffer_handle_t buffer, int32_t acquireFence) { - base::unique_fd fence(acquireFence); - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.buffer = CreateGraphicBuffer( - buffer, hwc_layer->buffer_metadata); - hwc_layer->info.fence = new Fence(fence.release()); - - return Error::NONE; -} - -Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer, - const std::vector<hwc_rect_t>& damage) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.damaged_regions = damage; - return Error::NONE; -} - -Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.blend_mode = - static_cast<ComposerView::ComposerLayer::BlendMode>(mode); - - return Error::NONE; -} - -Error VrHwc::setLayerColor(Display display, Layer layer, - IComposerClient::Color color) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.color = color; - return Error::NONE; -} - -Error VrHwc::setLayerCompositionType(Display display, Layer layer, - int32_t type) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type); - - return Error::NONE; -} - -Error VrHwc::setLayerDataspace(Display display, Layer layer, - int32_t dataspace) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.dataspace = dataspace; - return Error::NONE; -} - -Error VrHwc::setLayerDisplayFrame(Display display, Layer layer, - const hwc_rect_t& frame) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.display_frame = - {frame.left, frame.top, frame.right, frame.bottom}; - - return Error::NONE; -} - -Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.alpha = alpha; - - return Error::NONE; -} - -Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */, - buffer_handle_t /* stream */) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - return Error::NONE; -} - -Error VrHwc::setLayerSourceCrop(Display display, Layer layer, - const hwc_frect_t& crop) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom}; - - return Error::NONE; -} - -Error VrHwc::setLayerTransform(Display display, Layer layer, - int32_t transform) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.transform = transform; - return Error::NONE; -} - -Error VrHwc::setLayerVisibleRegion(Display display, Layer layer, - const std::vector<hwc_rect_t>& visible) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.visible_regions = visible; - return Error::NONE; -} - -Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.z_order = z; - - return Error::NONE; -} - -Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type, - uint32_t appId) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.type = type; - hwc_layer->info.app_id = appId; - - return Error::NONE; -} - -Error VrHwc::setClientTargetMetadata( - Display display, const IVrComposerClient::BufferMetadata& metadata) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - display_ptr->SetClientTargetMetadata(metadata); - - return Error::NONE; -} - -Error VrHwc::setLayerBufferMetadata( - Display display, Layer layer, - const IVrComposerClient::BufferMetadata& metadata) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->buffer_metadata = metadata; - - return Error::NONE; -} - -Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) { - hidl_cb(hidl_vec<Capability>()); - return Void(); -} - -Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { - std::string result; - - { - std::lock_guard<std::mutex> guard(mutex_); - result = "\nVrHwc states:\n"; - for (const auto& pair : displays_) { - result += StringPrintf("Display id: %lu\n", (unsigned long)pair.first); - pair.second->dumpDebugInfo(&result); - } - result += "\n"; - } - - hidl_cb(hidl_string(result)); - return Void(); -} - -Return<void> VrHwc::createClient(createClient_cb hidl_cb) { - std::lock_guard<std::mutex> guard(mutex_); - - Error status = Error::NONE; - sp<VrComposerClient> client; - if (!client_.promote().get()) { - client = new VrComposerClient(*this); - } else { - ALOGE("Already have a client"); - status = Error::NO_RESOURCES; - } - - client_ = client; - hidl_cb(status, client); - return Void(); -} - -Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) { - std::lock_guard<std::mutex> guard(mutex_); - - Error status = Error::NONE; - sp<VrComposerClient> client; - if (!client_.promote().get()) { - client = new VrComposerClient(*this); - } else { - ALOGE("Already have a client"); - status = Error::NO_RESOURCES; - } - - client_ = client; - hidl_cb(status, client); - return Void(); -} - -void VrHwc::ForceDisplaysRefresh() { - std::lock_guard<std::mutex> guard(mutex_); - if (event_callback_ != nullptr) { - for (const auto& pair : displays_) - event_callback_->onRefresh(pair.first); - } -} - -void VrHwc::RegisterObserver(Observer* observer) { - std::lock_guard<std::mutex> guard(mutex_); - if (observer_) - ALOGE("Overwriting observer"); - else - observer_ = observer; -} - -void VrHwc::UnregisterObserver(Observer* observer) { - std::lock_guard<std::mutex> guard(mutex_); - if (observer != observer_) - ALOGE("Trying to unregister unknown observer"); - else - observer_ = nullptr; -} - -HwcDisplay* VrHwc::FindDisplay(Display display) { - auto iter = displays_.find(display); - return iter == displays_.end() ? nullptr : iter->second.get(); -} - -void VrHwc::UpdateVsyncCallbackEnabledLocked() { - auto primary_display = FindDisplay(kDefaultDisplayId); - LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr, - "Should have created the primary display by now"); - bool send_vsync = - event_callback_ != nullptr && primary_display->vsync_enabled(); - vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr); -} - -Return<void> VrHwc::debug(const hidl_handle& fd, - const hidl_vec<hidl_string>& args) { - std::string result; - - { - std::lock_guard<std::mutex> guard(mutex_); - for (const auto& pair : displays_) { - result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first)); - pair.second->dumpDebugInfo(&result); - } - result += "\n"; - } - - FILE* out = fdopen(dup(fd->data[0]), "w"); - fprintf(out, "%s", result.c_str()); - fclose(out); - - return Void(); -} - -void HwcLayer::dumpDebugInfo(std::string* result) const { - if (!result) { - return; - } - *result += StringPrintf("Layer: composition_type: %d, type: %d, app_id: %d, z_order: %d,\ - cursor_x: %d, cursor_y: %d, color(rgba): (%d,%d,%d,%d), dataspace: %d, transform: %d,\ - display_frame(LTRB): (%d,%d,%d,%d), crop(LTRB): (%.1f,%.1f,%.1f,%.1f), blend_mode: %d\n", - composition_type, info.type, info.app_id, info.z_order, info.cursor_x, info.cursor_y, - info.color.r, info.color.g, info.color.b, info.color.a, info.dataspace, info.transform, - info.display_frame.left, info.display_frame.top, info.display_frame.right, - info.display_frame.bottom, info.crop.left, info.crop.top, info.crop.right, - info.crop.bottom, info.blend_mode); - *result += StringPrintf("Layer buffer metadata: width: %d, height: %d, stride: %d, layerCount: %d\ - , pixelFormat: %d\n", buffer_metadata.width, buffer_metadata.height, buffer_metadata.stride, - buffer_metadata.layerCount, buffer_metadata.format); -} - -status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) { - ATRACE_NAME("vr_hwc onVsync"); - std::lock_guard<std::mutex> guard(mutex_); - if (callback_ != nullptr) - callback_->onVsync(kDefaultDisplayId, vsync_timestamp); - return OK; -} - -void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) { - std::lock_guard<std::mutex> guard(mutex_); - callback_ = callback; -} - -// composer::V2_2::ComposerHal -Error VrHwc::setReadbackBuffer(Display display, - const native_handle_t* bufferHandle, - android::base::unique_fd fenceFd) { - return Error::NONE; -} - -Error VrHwc::getReadbackBufferFence(Display display, - android::base::unique_fd* outFenceFd) { - return Error::NONE; -} - -Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height, - types::V1_1::PixelFormat* format, - Display* outDisplay) { - *format = types::V1_1::PixelFormat::RGBA_8888; - *outDisplay = display_count_; - displays_[display_count_].reset(new HwcDisplay(width, height)); - display_count_++; - return Error::NONE; -} - -Error VrHwc::setPowerMode_2_2(Display display, - IComposerClient::PowerMode mode) { - bool dozeSupported = false; - - Error dozeSupportError = getDozeSupport(display, &dozeSupported); - - if (dozeSupportError != Error::NONE) - return dozeSupportError; - - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (mode < IComposerClient::PowerMode::OFF || - mode > IComposerClient::PowerMode::DOZE_SUSPEND) { - return Error::BAD_PARAMETER; - } - - if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE || - mode == IComposerClient::PowerMode::DOZE_SUSPEND)) { - return Error::UNSUPPORTED; - } - - display_ptr->set_power_mode(mode); - return Error::NONE; -} - -Error VrHwc::setLayerFloatColor(Display display, Layer layer, - IComposerClient::FloatColor color) { - return Error::NONE; -} - -Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode, - std::vector<RenderIntent>* outIntents) { - return Error::NONE; -} - -std::array<float, 16> VrHwc::getDataspaceSaturationMatrix( - types::V1_1::Dataspace dataspace) { - return {}; -} - -// composer::V2_3::ComposerHal -Error VrHwc::getHdrCapabilities_2_3(Display /*display*/, - hidl_vec<Hdr>* /*outTypes*/, - float* outMaxLuminance, - float* outMaxAverageLuminance, - float* outMinLuminance) { - *outMaxLuminance = 0; - *outMaxAverageLuminance = 0; - *outMinLuminance = 0; - return Error::NONE; -} - -Error VrHwc::setLayerPerFrameMetadata_2_3( - Display display, Layer layer, - const std::vector<IComposerClient::PerFrameMetadata>& metadata) { - return Error::NONE; -} - -Error VrHwc::getPerFrameMetadataKeys_2_3( - Display display, - std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) { - return Error::NONE; -} - -Error VrHwc::setColorMode_2_3(Display display, ColorMode mode, - RenderIntent intent) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3) - return Error::BAD_PARAMETER; - - display_ptr->set_color_mode(mode); - return Error::NONE; -} - -Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode, - std::vector<RenderIntent>* outIntents) { - return Error::NONE; -} - -Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) { - return Error::NONE; -} - -Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width, - uint32_t height, PixelFormat format, - Dataspace dataspace) { - return Error::NONE; -} - -Error VrHwc::getReadbackBufferAttributes_2_3(Display display, - PixelFormat* outFormat, - Dataspace* outDataspace) { - return Error::NONE; -} - -Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort, - std::vector<uint8_t>* outData) { - int error = 0; - auto display_client = display::DisplayClient::Create(&error); - if (!display_client) { - ALOGE("Could not connect to display service : %s(%d)", strerror(error), - error); - return Error::BAD_CONFIG; - } - auto edid_data = display_client->GetConfigurationData( - display::ConfigFileType::kDeviceEdid); - auto display_identification_port = - display_client->GetDisplayIdentificationPort(); - *outPort = display_identification_port.get(); - - std::copy(edid_data.get().begin(), edid_data.get().end(), - std::back_inserter(*outData)); - return Error::NONE; -} - -Error VrHwc::setLayerColorTransform(Display display, Layer layer, - const float* matrix) { - return Error::NONE; -} - -Error VrHwc::getDisplayedContentSamplingAttributes( - Display display, PixelFormat& format, Dataspace& dataspace, - hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) { - return Error::NONE; -} - -Error VrHwc::setDisplayedContentSamplingEnabled( - Display display, IComposerClient::DisplayedContentSampling enable, - hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, - uint64_t maxFrames) { - return Error::NONE; -} - -Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames, - uint64_t timestamp, uint64_t& frameCount, - hidl_vec<uint64_t>& sampleComponent0, - hidl_vec<uint64_t>& sampleComponent1, - hidl_vec<uint64_t>& sampleComponent2, - hidl_vec<uint64_t>& sampleComponent3) { - return Error::NONE; -} - -Error VrHwc::getDisplayCapabilities( - Display display, - std::vector<IComposerClient::DisplayCapability>* outCapabilities) { - return Error::NONE; -} - -Error VrHwc::setLayerPerFrameMetadataBlobs( - Display display, Layer layer, - std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) { - return Error::NONE; -} - -Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) { - return Error::NONE; -} - -Error VrHwc::setDisplayBrightness(Display display, float brightness) { - return Error::NONE; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h deleted file mode 100644 index 3e3a6307fa..0000000000 --- a/services/vr/hardware_composer/impl/vr_hwc.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2016 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. - */ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H -#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H - -#include <android-base/unique_fd.h> -#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#include <android/hardware/graphics/composer/2.3/IComposer.h> -#include <composer-hal/2.3/ComposerHal.h> -#include <private/dvr/vsync_service.h> -#include <ui/Fence.h> -#include <ui/GraphicBuffer.h> -#include <utils/StrongPointer.h> - -#include <mutex> -#include <unordered_map> - -using namespace android::frameworks::vr::composer::V2_0; -using namespace android::hardware::graphics::common::V1_0; -using namespace android::hardware::graphics::composer::V2_3; - -using android::hardware::hidl_bitfield; -using android::hardware::hidl_handle; -using android::hardware::hidl_string; -using android::hardware::hidl_vec; -using android::hardware::Return; -using android::hardware::Void; -using android::hardware::graphics::composer::V2_1::Config; -using android::hardware::graphics::composer::V2_1::Display; -using android::hardware::graphics::composer::V2_1::Error; -using android::hardware::graphics::composer::V2_1::Layer; -using android::hardware::graphics::composer::V2_3::IComposerClient; - -namespace android { - -class Fence; - -namespace dvr { - -class VrComposerClient; - -using android::hardware::graphics::composer::V2_3::hal::ComposerHal; - -namespace types = android::hardware::graphics::common; - -using types::V1_1::RenderIntent; -using types::V1_2::ColorMode; -using types::V1_2::Dataspace; -using types::V1_2::Hdr; -using types::V1_2::PixelFormat; - -class ComposerView { - public: - struct ComposerLayer { - using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect; - using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect; - using BlendMode = - hardware::graphics::composer::V2_3::IComposerClient::BlendMode; - - Layer id; - sp<GraphicBuffer> buffer; - sp<Fence> fence; - Recti display_frame; - Rectf crop; - BlendMode blend_mode; - float alpha; - uint32_t type; - uint32_t app_id; - uint32_t z_order; - int32_t cursor_x; - int32_t cursor_y; - IComposerClient::Color color; - int32_t dataspace; - int32_t transform; - std::vector<hwc_rect_t> visible_regions; - std::vector<hwc_rect_t> damaged_regions; - }; - - struct Frame { - Display display_id; - // This is set to true to notify the upper layer that the display is - // being removed, or left false in the case of a normal frame. The upper - // layer tracks display IDs and will handle new ones showing up. - bool removed = false; - int32_t display_width; - int32_t display_height; - Config active_config; - ColorMode color_mode; - IComposerClient::PowerMode power_mode; - IComposerClient::Vsync vsync_enabled; - float color_transform[16]; - int32_t color_transform_hint; - std::vector<ComposerLayer> layers; - }; - - class Observer { - public: - virtual ~Observer() {} - - // Returns a list of layers that need to be shown together. Layers are - // returned in z-order, with the lowest layer first. - virtual base::unique_fd OnNewFrame(const Frame& frame) = 0; - }; - - virtual ~ComposerView() {} - - virtual void ForceDisplaysRefresh() = 0; - virtual void RegisterObserver(Observer* observer) = 0; - virtual void UnregisterObserver(Observer* observer) = 0; -}; - -struct HwcLayer { - using Composition = - hardware::graphics::composer::V2_3::IComposerClient::Composition; - - explicit HwcLayer(Layer new_id) { info.id = new_id; } - - void dumpDebugInfo(std::string* result) const; - - Composition composition_type; - ComposerView::ComposerLayer info; - IVrComposerClient::BufferMetadata buffer_metadata; -}; - -class HwcDisplay { - public: - HwcDisplay(int32_t width, int32_t height); - ~HwcDisplay(); - - int32_t width() const { return width_; } - int32_t height() const { return height_; } - - HwcLayer* CreateLayer(); - bool DestroyLayer(Layer id); - HwcLayer* GetLayer(Layer id); - - bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence); - void SetClientTargetMetadata( - const IVrComposerClient::BufferMetadata& metadata); - - void GetChangedCompositionTypes( - std::vector<Layer>* layer_ids, - std::vector<IComposerClient::Composition>* composition); - - Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame); - - std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers(); - - Config active_config() const { return active_config_; } - void set_active_config(Config config) { active_config_ = config; } - - ColorMode color_mode() const { return color_mode_; } - void set_color_mode(ColorMode mode) { color_mode_ = mode; } - - IComposerClient::PowerMode power_mode() const { return power_mode_; } - void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; } - - bool vsync_enabled() const { return vsync_enabled_; } - void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;} - - const float* color_transform() const { return color_transform_; } - int32_t color_transform_hint() const { return color_transform_hint_; } - void SetColorTransform(const float* matrix, int32_t hint); - - void dumpDebugInfo(std::string* result) const; - - private: - // The client target buffer and the associated fence. - sp<GraphicBuffer> buffer_; - IVrComposerClient::BufferMetadata buffer_metadata_; - sp<Fence> fence_; - - // List of currently active layers. - std::vector<HwcLayer> layers_; - - std::vector<Layer> last_frame_layers_ids_; - - // Layer ID generator. - uint64_t layer_ids_ = 1; - - int32_t width_; - int32_t height_; - - Config active_config_; - ColorMode color_mode_; - IComposerClient::PowerMode power_mode_; - bool vsync_enabled_ = false; - float color_transform_[16]; - int32_t color_transform_hint_; - - HwcDisplay(const HwcDisplay&) = delete; - void operator=(const HwcDisplay&) = delete; -}; - -class VrHwc : public IComposer, public ComposerHal, public ComposerView { - public: - VrHwc(); - ~VrHwc() override; - - Error setLayerInfo(Display display, Layer layer, uint32_t type, - uint32_t appId); - Error setClientTargetMetadata( - Display display, const IVrComposerClient::BufferMetadata& metadata); - Error setLayerBufferMetadata( - Display display, Layer layer, - const IVrComposerClient::BufferMetadata& metadata); - - // composer::V2_1::ComposerHal - bool hasCapability(hwc2_capability_t capability) override; - - std::string dumpDebugInfo() override { return {}; } - - void registerEventCallback(ComposerHal::EventCallback* callback) override; - void unregisterEventCallback() override; - - uint32_t getMaxVirtualDisplayCount() override; - Error destroyVirtualDisplay(Display display) override; - - Error createLayer(Display display, Layer* outLayer) override; - Error destroyLayer(Display display, Layer layer) override; - - Error getActiveConfig(Display display, Config* outConfig) override; - Error getDisplayAttribute(Display display, Config config, - IComposerClient::Attribute attribute, - int32_t* outValue) override; - Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; - Error getDisplayName(Display display, hidl_string* outName) override; - Error getDisplayType(Display display, - IComposerClient::DisplayType* outType) override; - Error getDozeSupport(Display display, bool* outSupport) override; - - Error setActiveConfig(Display display, Config config) override; - Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; - - Error setColorTransform(Display display, const float* matrix, - int32_t hint) override; - Error setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t dataspace, - const std::vector<hwc_rect_t>& damage) override; - Error setOutputBuffer(Display display, buffer_handle_t buffer, - int32_t releaseFence) override; - Error validateDisplay( - Display display, std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks) override; - Error acceptDisplayChanges(Display display) override; - Error presentDisplay(Display display, int32_t* outPresentFence, - std::vector<Layer>* outLayers, - std::vector<int32_t>* outReleaseFences) override; - - Error setLayerCursorPosition(Display display, Layer layer, int32_t x, - int32_t y) override; - Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, - int32_t acquireFence) override; - Error setLayerSurfaceDamage(Display display, Layer layer, - const std::vector<hwc_rect_t>& damage) override; - Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; - Error setLayerColor(Display display, Layer layer, - IComposerClient::Color color) override; - Error setLayerCompositionType(Display display, Layer layer, - int32_t type) override; - Error setLayerDataspace(Display display, Layer layer, - int32_t dataspace) override; - Error setLayerDisplayFrame(Display display, Layer layer, - const hwc_rect_t& frame) override; - Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; - Error setLayerSidebandStream(Display display, Layer layer, - buffer_handle_t stream) override; - Error setLayerSourceCrop(Display display, Layer layer, - const hwc_frect_t& crop) override; - Error setLayerTransform(Display display, Layer layer, - int32_t transform) override; - Error setLayerVisibleRegion(Display display, Layer layer, - const std::vector<hwc_rect_t>& visible) override; - Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; - - // composer::V2_2::ComposerHal - Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle, - android::base::unique_fd fenceFd) override; - Error getReadbackBufferFence(Display display, - android::base::unique_fd* outFenceFd) override; - Error createVirtualDisplay_2_2(uint32_t width, uint32_t height, - types::V1_1::PixelFormat* format, - Display* outDisplay) override; - Error setPowerMode_2_2(Display display, - IComposerClient::PowerMode mode) override; - Error setLayerFloatColor(Display display, Layer layer, - IComposerClient::FloatColor color) override; - Error getRenderIntents(Display display, types::V1_1::ColorMode mode, - std::vector<RenderIntent>* outIntents) override; - std::array<float, 16> getDataspaceSaturationMatrix( - types::V1_1::Dataspace dataspace) override; - - // composer::V2_3::ComposerHal - Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes, - float* outMaxLuminance, - float* outMaxAverageLuminance, - float* outMinLuminance) override; - Error setLayerPerFrameMetadata_2_3( - Display display, Layer layer, - const std::vector<IComposerClient::PerFrameMetadata>& metadata) override; - Error getPerFrameMetadataKeys_2_3( - Display display, - std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override; - Error setColorMode_2_3(Display display, ColorMode mode, - RenderIntent intent) override; - Error getRenderIntents_2_3(Display display, ColorMode mode, - std::vector<RenderIntent>* outIntents) override; - Error getColorModes_2_3(Display display, - hidl_vec<ColorMode>* outModes) override; - Error getClientTargetSupport_2_3(Display display, uint32_t width, - uint32_t height, PixelFormat format, - Dataspace dataspace) override; - Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat, - Dataspace* outDataspace) override; - Error getDisplayIdentificationData(Display display, uint8_t* outPort, - std::vector<uint8_t>* outData) override; - Error setLayerColorTransform(Display display, Layer layer, - const float* matrix) override; - Error getDisplayedContentSamplingAttributes( - Display display, PixelFormat& format, Dataspace& dataspace, - hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) - override; - Error setDisplayedContentSamplingEnabled( - Display display, IComposerClient::DisplayedContentSampling enable, - hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, - uint64_t maxFrames) override; - Error getDisplayedContentSample( - Display display, uint64_t maxFrames, uint64_t timestamp, - uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0, - hidl_vec<uint64_t>& sampleComponent1, - hidl_vec<uint64_t>& sampleComponent2, - hidl_vec<uint64_t>& sampleComponent3) override; - Error getDisplayCapabilities(Display display, - std::vector<IComposerClient::DisplayCapability>* - outCapabilities) override; - Error setLayerPerFrameMetadataBlobs( - Display display, Layer layer, - std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override; - Error getDisplayBrightnessSupport(Display display, bool* outSupport) override; - Error setDisplayBrightness(Display display, float brightness) override; - - // IComposer: - Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; - Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; - Return<void> createClient(createClient_cb hidl_cb) override; - Return<void> createClient_2_3( - IComposer::createClient_2_3_cb hidl_cb) override; - - // ComposerView: - void ForceDisplaysRefresh() override; - void RegisterObserver(Observer* observer) override; - void UnregisterObserver(Observer* observer) override; - - Return<void> debug(const hidl_handle& fd, - const hidl_vec<hidl_string>& args) override; - - private: - class VsyncCallback : public BnVsyncCallback { - public: - status_t onVsync(int64_t vsync_timestamp) override; - void SetEventCallback(EventCallback* callback); - private: - std::mutex mutex_; - EventCallback* callback_; - }; - - HwcDisplay* FindDisplay(Display display); - - // Re-evaluate whether or not we should start making onVsync() callbacks to - // the client. We need enableCallback(true) to have been called, and - // setVsyncEnabled() to have been called for the primary display. The caller - // must have mutex_ locked already. - void UpdateVsyncCallbackEnabledLocked(); - - wp<VrComposerClient> client_; - - // Guard access to internal state from binder threads. - std::mutex mutex_; - - std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_; - Display display_count_ = 2; - - EventCallback* event_callback_ = nullptr; - Observer* observer_ = nullptr; - - sp<VsyncCallback> vsync_callback_; - - VrHwc(const VrHwc&) = delete; - void operator=(const VrHwc&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp deleted file mode 100644 index 2e70928662..0000000000 --- a/services/vr/hardware_composer/tests/vr_composer_test.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include <android/dvr/BnVrComposerCallback.h> -#include <binder/IServiceManager.h> -#include <gtest/gtest.h> -#include <sys/eventfd.h> -#include <vr_composer.h> - -namespace android { -namespace dvr { -namespace { - -const char kVrDisplayName[] = "VrDisplay_Test"; - -class TestComposerView : public ComposerView { - public: - TestComposerView() {} - ~TestComposerView() override = default; - - size_t display_refresh_count() const { return display_refresh_count_; } - - void ForceDisplaysRefresh() override { display_refresh_count_++; } - void RegisterObserver(Observer* observer) override {} - void UnregisterObserver(Observer* observer) override {} - - TestComposerView(const TestComposerView&) = delete; - void operator=(const TestComposerView&) = delete; - - private: - size_t display_refresh_count_ = 0; -}; - -class TestComposerCallback : public BnVrComposerCallback { - public: - TestComposerCallback() {} - ~TestComposerCallback() override = default; - - ComposerView::Frame last_frame() const { return last_frame_; } - - binder::Status onNewFrame( - const ParcelableComposerFrame& frame, - ParcelableUniqueFd* /* fence */) override { - last_frame_ = frame.frame(); - return binder::Status::ok(); - } - - private: - ComposerView::Frame last_frame_; - - TestComposerCallback(const TestComposerCallback&) = delete; - void operator=(const TestComposerCallback&) = delete; -}; - -class TestComposerCallbackWithFence : public TestComposerCallback { - public: - ~TestComposerCallbackWithFence() override = default; - - binder::Status onNewFrame( - const ParcelableComposerFrame& frame, - ParcelableUniqueFd* fence) override { - binder::Status status = TestComposerCallback::onNewFrame(frame, fence); - - base::unique_fd fd(eventfd(0, 0)); - EXPECT_LE(0, fd.get()); - fence->set_fence(fd); - - return status; - } -}; - -sp<GraphicBuffer> CreateBuffer() { - return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_HW_TEXTURE); -} - -} // namespace - -class VrComposerTest : public testing::Test { - public: - VrComposerTest() : composer_(new VrComposer(&composer_view_)) {} - ~VrComposerTest() override = default; - - sp<IVrComposer> GetComposerProxy() const { - sp<IServiceManager> sm(defaultServiceManager()); - return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName))); - } - - void SetUp() override { - sp<IServiceManager> sm(defaultServiceManager()); - EXPECT_EQ(OK, - sm->addService(String16(kVrDisplayName), composer_, false)); - } - - protected: - TestComposerView composer_view_; - sp<VrComposer> composer_; - - VrComposerTest(const VrComposerTest&) = delete; - void operator=(const VrComposerTest&) = delete; -}; - -TEST_F(VrComposerTest, TestWithoutObserver) { - sp<IVrComposer> composer = GetComposerProxy(); - ComposerView::Frame frame; - - base::unique_fd fence = composer_->OnNewFrame(frame); - ASSERT_EQ(-1, fence.get()); -} - -TEST_F(VrComposerTest, TestWithObserver) { - sp<IVrComposer> composer = GetComposerProxy(); - sp<TestComposerCallback> callback = new TestComposerCallback(); - ASSERT_EQ(0, composer_view_.display_refresh_count()); - ASSERT_TRUE(composer->registerObserver(callback).isOk()); - ASSERT_EQ(1, composer_view_.display_refresh_count()); - - ComposerView::Frame frame; - base::unique_fd fence = composer_->OnNewFrame(frame); - ASSERT_EQ(-1, fence.get()); -} - -TEST_F(VrComposerTest, TestWithOneLayer) { - sp<IVrComposer> composer = GetComposerProxy(); - sp<TestComposerCallback> callback = new TestComposerCallbackWithFence(); - ASSERT_TRUE(composer->registerObserver(callback).isOk()); - - ComposerView::Frame frame; - frame.display_id = 1; - frame.removed = false; - frame.display_width = 600; - frame.display_height = 400; - frame.layers.push_back(ComposerView::ComposerLayer{ - .id = 1, - .buffer = CreateBuffer(), - .fence = new Fence(eventfd(0, 0)), - .display_frame = {0, 0, 600, 400}, - .crop = {0.0f, 0.0f, 600.0f, 400.0f}, - .blend_mode = IComposerClient::BlendMode::NONE, - .alpha = 1.0f, - .type = 1, - .app_id = 1, - }); - base::unique_fd fence = composer_->OnNewFrame(frame); - ASSERT_LE(0, fence.get()); - - ComposerView::Frame received_frame = callback->last_frame(); - ASSERT_EQ(frame.display_id, received_frame.display_id); - ASSERT_EQ(frame.display_width, received_frame.display_width); - ASSERT_EQ(frame.display_height, received_frame.display_height); - ASSERT_EQ(frame.removed, received_frame.removed); - ASSERT_EQ(1u, received_frame.layers.size()); - ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id); - ASSERT_NE(nullptr, received_frame.layers[0].buffer.get()); - ASSERT_TRUE(received_frame.layers[0].fence->isValid()); - ASSERT_EQ(frame.layers[0].display_frame.left, - received_frame.layers[0].display_frame.left); - ASSERT_EQ(frame.layers[0].display_frame.top, - received_frame.layers[0].display_frame.top); - ASSERT_EQ(frame.layers[0].display_frame.right, - received_frame.layers[0].display_frame.right); - ASSERT_EQ(frame.layers[0].display_frame.bottom, - received_frame.layers[0].display_frame.bottom); - ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left); - ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top); - ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right); - ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom); - ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode); - ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha); - ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type); - ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id); -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp deleted file mode 100644 index d93f370945..0000000000 --- a/services/vr/hardware_composer/vr_composer.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "vr_composer.h" - -#include <binder/IPCThreadState.h> -#include <binder/PermissionCache.h> - -namespace android { -namespace dvr { -namespace { - -bool CheckPermission() { - const android::IPCThreadState* ipc = android::IPCThreadState::self(); - const pid_t pid = ipc->getCallingPid(); - const uid_t uid = ipc->getCallingUid(); - const bool permission = PermissionCache::checkPermission( - String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid); - if (!permission) - ALOGE("permission denied to pid=%d uid=%u", pid, uid); - - return permission; -} - -} // namespace - -VrComposer::VrComposer(ComposerView* composer_view) - : composer_view_(composer_view) { - composer_view_->RegisterObserver(this); -} - -VrComposer::~VrComposer() { - composer_view_->UnregisterObserver(this); -} - -binder::Status VrComposer::registerObserver( - const sp<IVrComposerCallback>& callback) { - { - std::lock_guard<std::mutex> guard(mutex_); - - if (!CheckPermission()) - return binder::Status::fromStatusT(PERMISSION_DENIED); - - if (callback_.get()) { - ALOGE("Failed to register callback, already registered"); - return binder::Status::fromStatusT(ALREADY_EXISTS); - } - - callback_ = callback; - IInterface::asBinder(callback_)->linkToDeath(this); - } - - // Don't take the lock to force display refresh otherwise it could end in a - // deadlock since HWC calls this with new frames and it has a lock of its own - // to serialize access to the display information. - composer_view_->ForceDisplaysRefresh(); - return binder::Status::ok(); -} - -binder::Status VrComposer::clearObserver() { - std::lock_guard<std::mutex> guard(mutex_); - callback_ = nullptr; - return binder::Status::ok(); -} - -base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) { - std::lock_guard<std::mutex> guard(mutex_); - - if (!callback_.get()) - return base::unique_fd(); - - ParcelableComposerFrame parcelable_frame(frame); - ParcelableUniqueFd fence; - binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence); - if (!ret.isOk()) - ALOGE("Failed to send new frame: %s", ret.toString8().string()); - - return fence.fence(); -} - -void VrComposer::binderDied(const wp<IBinder>& /* who */) { - std::lock_guard<std::mutex> guard(mutex_); - - callback_ = nullptr; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h deleted file mode 100644 index 1273352ad0..0000000000 --- a/services/vr/hardware_composer/vr_composer.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H -#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H - -#include <android/dvr/BnVrComposer.h> -#include <impl/vr_hwc.h> - -namespace android { -namespace dvr { - -class VrComposerCallback; - -// Implementation of the IVrComposer service used to notify VR Window Manager -// when SurfaceFlinger presents 2D UI changes. -// -// VR HWC updates the presented frame via the ComposerView::Observer interface. -// On notification |callback_| is called to update VR Window Manager. -// NOTE: If VR Window Manager isn't connected, the notification is a no-op. -class VrComposer - : public BnVrComposer, - public ComposerView::Observer, - public IBinder::DeathRecipient { - public: - explicit VrComposer(ComposerView* composer_view); - ~VrComposer() override; - - // BnVrComposer: - binder::Status registerObserver( - const sp<IVrComposerCallback>& callback) override; - - binder::Status clearObserver() override; - - // ComposerView::Observer: - base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override; - - private: - // IBinder::DeathRecipient: - void binderDied(const wp<IBinder>& who) override; - - std::mutex mutex_; - - sp<IVrComposerCallback> callback_; - - ComposerView* composer_view_; // Not owned. - - VrComposer(const VrComposer&) = delete; - void operator=(const VrComposer&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index ba98696aef..40cf9fbd67 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -49,7 +49,13 @@ extern "C" { * in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the * pNext chain of VkBindImageMemoryInfo and passed down to the driver. */ -#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 +/* + * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9 + * + * This version of the extension is largely designed to clean up the mix of + * GrallocUsage and GrallocUsage2 + */ +#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" #define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \ @@ -61,6 +67,8 @@ extern "C" { VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1) #define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2) +#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \ + VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3) /* clang-format off */ typedef enum VkSwapchainImageUsageFlagBitsANDROID { @@ -90,6 +98,7 @@ typedef struct { * format: gralloc format requested when the buffer was allocated * usage: gralloc usage requested when the buffer was allocated * usage2: gralloc usage requested when the buffer was allocated + * usage3: gralloc usage requested when the buffer was allocated */ typedef struct { VkStructureType sType; @@ -98,7 +107,8 @@ typedef struct { int stride; int format; int usage; /* DEPRECATED in SPEC_VERSION 6 */ - VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */ + VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */ + uint64_t usage3; /* ADDED in SPEC_VERSION 9 */ } VkNativeBufferANDROID; /* @@ -127,6 +137,21 @@ typedef struct { VkBool32 sharedImage; } VkPhysicalDevicePresentationPropertiesANDROID; +/* + * struct VkGrallocUsageInfoANDROID + * + * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID + * pNext: NULL or a pointer to a structure extending this structure + * format: value specifying the format the image will be created with + * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage + */ +typedef struct { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageUsageFlags imageUsage; +} VkGrallocUsageInfoANDROID; + /* DEPRECATED in SPEC_VERSION 6 */ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)( VkDevice device, @@ -134,7 +159,7 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)( VkImageUsageFlags imageUsage, int* grallocUsage); -/* ADDED in SPEC_VERSION 6 */ +/* DEPRECATED in SPEC_VERSION 9 */ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)( VkDevice device, VkFormat format, @@ -143,6 +168,12 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)( uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); +/* ADDED in SPEC_VERSION 9 */ +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)( + VkDevice device, + const VkGrallocUsageInfoANDROID* grallocUsageInfo, + uint64_t* grallocUsage); + typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)( VkDevice device, VkImage image, @@ -167,7 +198,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID( int* grallocUsage ); -/* ADDED in SPEC_VERSION 6 */ +/* DEPRECATED in SPEC_VERSION 9 */ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID( VkDevice device, VkFormat format, @@ -177,6 +208,13 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID( uint64_t* grallocProducerUsage ); +/* ADDED in SPEC_VERSION 9 */ +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID( + VkDevice device, + const VkGrallocUsageInfoANDROID* grallocUsageInfo, + uint64_t* grallocUsage +); + VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID( VkDevice device, VkImage image, diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 440c5b144a..a87f82fcbd 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -27,6 +27,9 @@ ndk_library { symbol_file: "libvulkan.map.txt", first_version: "24", unversioned_until: "current", + export_header_libs: [ + "ndk_vulkan_headers", + ], } cc_library_shared { @@ -37,7 +40,6 @@ cc_library_shared { "vulkan_headers", ], }, - clang: true, sanitize: { misc_undefined: ["integer"], }, diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index df70bf4419..a9706bc871 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -682,6 +682,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetPhysicalDeviceMemoryProperties2", "vkGetPhysicalDeviceMemoryProperties2KHR", "vkGetPhysicalDeviceMultisamplePropertiesEXT", + "vkGetPhysicalDeviceOpticalFlowImageFormatsNV", "vkGetPhysicalDevicePresentRectanglesKHR", "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceProperties2", diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 766451824a..a99355f047 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -1027,6 +1027,51 @@ void QueryPresentationProperties( } } +VkResult GetAndroidNativeBufferSpecVersion9Support( + VkPhysicalDevice physicalDevice, + bool& support) { + support = false; + + const InstanceData& data = GetData(physicalDevice); + + // Call to get propertyCount + uint32_t propertyCount = 0; + ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties"); + VkResult result = data.driver.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &propertyCount, nullptr); + ATRACE_END(); + + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + return result; + } + + // Call to enumerate properties + std::vector<VkExtensionProperties> properties(propertyCount); + ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties"); + result = data.driver.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &propertyCount, properties.data()); + ATRACE_END(); + + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + return result; + } + + for (uint32_t i = 0; i < propertyCount; i++) { + auto& prop = properties[i]; + + if (strcmp(prop.extensionName, + VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0) + continue; + + if (prop.specVersion >= 9) { + support = true; + return result; + } + } + + return result; +} + VkResult EnumerateDeviceExtensionProperties( VkPhysicalDevice physicalDevice, const char* pLayerName, @@ -1061,6 +1106,49 @@ VkResult EnumerateDeviceExtensionProperties( VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION}); } + // Conditionally add VK_EXT_IMAGE_COMPRESSION_CONTROL* if feature and ANB + // support is provided by the driver + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT + swapchainCompFeats = {}; + swapchainCompFeats.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT; + swapchainCompFeats.pNext = nullptr; + swapchainCompFeats.imageCompressionControlSwapchain = false; + VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {}; + imageCompFeats.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT; + imageCompFeats.pNext = &swapchainCompFeats; + imageCompFeats.imageCompressionControl = false; + + VkPhysicalDeviceFeatures2 feats2 = {}; + feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + feats2.pNext = &imageCompFeats; + + const auto& driver = GetData(physicalDevice).driver; + if (driver.GetPhysicalDeviceFeatures2 || + driver.GetPhysicalDeviceFeatures2KHR) { + GetPhysicalDeviceFeatures2(physicalDevice, &feats2); + } + + bool anb9 = false; + VkResult result = + GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9); + + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + return result; + } + + if (anb9 && imageCompFeats.imageCompressionControl) { + loader_extensions.push_back( + {VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME, + VK_EXT_IMAGE_COMPRESSION_CONTROL_SPEC_VERSION}); + } + if (anb9 && swapchainCompFeats.imageCompressionControlSwapchain) { + loader_extensions.push_back( + {VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME, + VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION}); + } + // enumerate our extensions first if (!pLayerName && pProperties) { uint32_t count = std::min( @@ -1078,7 +1166,7 @@ VkResult EnumerateDeviceExtensionProperties( } ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties"); - VkResult result = data.driver.EnumerateDeviceExtensionProperties( + result = data.driver.EnumerateDeviceExtensionProperties( physicalDevice, pLayerName, pPropertyCount, pProperties); ATRACE_END(); @@ -1254,15 +1342,18 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, return VK_ERROR_INCOMPATIBLE_DRIVER; } - // sanity check ANDROID_native_buffer implementation, whose set of + // Confirming ANDROID_native_buffer implementation, whose set of // entrypoints varies according to the spec version. if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) && !data->driver.GetSwapchainGrallocUsageANDROID && - !data->driver.GetSwapchainGrallocUsage2ANDROID) { - ALOGE("Driver's implementation of ANDROID_native_buffer is broken;" - " must expose at least one of " - "vkGetSwapchainGrallocUsageANDROID or " - "vkGetSwapchainGrallocUsage2ANDROID"); + !data->driver.GetSwapchainGrallocUsage2ANDROID && + !data->driver.GetSwapchainGrallocUsage3ANDROID) { + ALOGE( + "Driver's implementation of ANDROID_native_buffer is broken;" + " must expose at least one of " + "vkGetSwapchainGrallocUsageANDROID or " + "vkGetSwapchainGrallocUsage2ANDROID or " + "vkGetSwapchainGrallocUsage3ANDROID"); data->driver.DestroyDevice(dev, pAllocator); FreeDeviceData(data, data_allocator); @@ -1441,10 +1532,89 @@ void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, if (driver.GetPhysicalDeviceFeatures2) { driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + } else { + driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); + } + + // Conditionally add imageCompressionControlSwapchain if + // imageCompressionControl is supported Check for imageCompressionControl in + // the pChain + bool imageCompressionControl = false; + bool imageCompressionControlInChain = false; + bool imageCompressionControlSwapchainInChain = false; + VkPhysicalDeviceFeatures2* pFeats = pFeatures; + while (pFeats) { + switch (pFeats->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT: { + const VkPhysicalDeviceImageCompressionControlFeaturesEXT* + compressionFeat = reinterpret_cast< + const VkPhysicalDeviceImageCompressionControlFeaturesEXT*>( + pFeats); + imageCompressionControl = + compressionFeat->imageCompressionControl; + imageCompressionControlInChain = true; + } break; + + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: { + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT* + compressionFeat = reinterpret_cast< + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>( + pFeats); + compressionFeat->imageCompressionControlSwapchain = false; + imageCompressionControlSwapchainInChain = true; + } break; + + default: + break; + } + pFeats = reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext); + } + + if (!imageCompressionControlSwapchainInChain) { return; } - driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); + // If not in pchain, explicitly query for imageCompressionControl + if (!imageCompressionControlInChain) { + VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {}; + imageCompFeats.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT; + imageCompFeats.pNext = nullptr; + imageCompFeats.imageCompressionControl = false; + + VkPhysicalDeviceFeatures2 feats2 = {}; + feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + feats2.pNext = &imageCompFeats; + + if (driver.GetPhysicalDeviceFeatures2) { + driver.GetPhysicalDeviceFeatures2(physicalDevice, &feats2); + } else { + driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, &feats2); + } + + imageCompressionControl = imageCompFeats.imageCompressionControl; + } + + // Only enumerate imageCompressionControlSwapchin if imageCompressionControl + if (imageCompressionControl) { + pFeats = pFeatures; + while (pFeats) { + switch (pFeats->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: { + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT* + compressionFeat = reinterpret_cast< + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>( + pFeats); + compressionFeat->imageCompressionControlSwapchain = true; + } break; + + default: + break; + } + pFeats = + reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext); + } + } } void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 14c516b16b..4d2bbd69e3 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -107,6 +107,8 @@ void QueryPresentationProperties( VkPhysicalDevice physicalDevice, VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties); +bool GetAndroidNativeBufferSpecVersion9Support(VkPhysicalDevice physicalDevice); + VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName); VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index b436db1de7..de98aa7e61 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -496,6 +496,13 @@ const ProcHook g_proc_hooks[] = { nullptr, }, { + "vkGetSwapchainGrallocUsage3ANDROID", + ProcHook::DEVICE, + ProcHook::ANDROID_native_buffer, + nullptr, + nullptr, + }, + { "vkGetSwapchainGrallocUsageANDROID", ProcHook::DEVICE, ProcHook::ANDROID_native_buffer, @@ -664,6 +671,7 @@ bool InitDriverTable(VkDevice dev, INIT_PROC(false, dev, GetDeviceQueue2); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID); + INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID); // clang-format on diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 079f9cca39..2f60086a27 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -123,6 +123,7 @@ struct DeviceDriverTable { PFN_vkGetDeviceQueue2 GetDeviceQueue2; PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID; PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID; + PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID; PFN_vkAcquireImageANDROID AcquireImageANDROID; PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID; // clang-format on diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt index f49e8f3e8d..b189c6888d 100644 --- a/vulkan/libvulkan/libvulkan.map.txt +++ b/vulkan/libvulkan/libvulkan.map.txt @@ -178,6 +178,7 @@ LIBVULKAN { vkGetImageSparseMemoryRequirements; vkGetImageSparseMemoryRequirements2; # introduced=28 vkGetImageSubresourceLayout; + vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake vkGetInstanceProcAddr; vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28 vkGetPhysicalDeviceExternalBufferProperties; # introduced=28 diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index c4b1487267..e9935e5781 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -19,6 +19,8 @@ #include <android/hardware/graphics/common/1.0/types.h> #include <grallocusage/GrallocUsageConversion.h> #include <graphicsenv/GraphicsEnv.h> +#include <hardware/gralloc.h> +#include <hardware/gralloc1.h> #include <log/log.h> #include <sync/sync.h> #include <system/window.h> @@ -42,6 +44,26 @@ namespace driver { namespace { +static uint64_t convertGralloc1ToBufferUsage(uint64_t producerUsage, + uint64_t consumerUsage) { + static_assert(uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) == + uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN), + "expected ConsumerUsage and ProducerUsage CPU_READ_OFTEN " + "bits to match"); + uint64_t merged = producerUsage | consumerUsage; + if ((merged & (GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN)) == + GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) { + merged &= ~uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN); + merged |= BufferUsage::CPU_READ_OFTEN; + } + if ((merged & (GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN)) == + GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN) { + merged &= ~uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN); + merged |= BufferUsage::CPU_WRITE_OFTEN; + } + return merged; +} + const VkSurfaceTransformFlagsKHR kSupportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | @@ -486,6 +508,10 @@ android::PixelFormat GetNativePixelFormat(VkFormat format) { case VK_FORMAT_R8_UNORM: native_format = android::PIXEL_FORMAT_R_8; break; + // TODO: Do we need to query for VK_EXT_rgba10x6_formats here? + case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: + native_format = android::PIXEL_FORMAT_RGBA_10101010; + break; default: ALOGV("unsupported swapchain format %d", format); break; @@ -646,10 +672,11 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( // VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following // four values cannot be known without a surface. Default values will // be supplied anyway, but cannot be relied upon. - width = 1000; - height = 1000; - transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - max_buffer_count = 10; + width = 0xFFFFFFFF; + height = 0xFFFFFFFF; + transform_hint = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; + capabilities->minImageCount = 0xFFFFFFFF; + capabilities->maxImageCount = 0xFFFFFFFF; } else { ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); @@ -681,9 +708,9 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } + capabilities->minImageCount = std::min(max_buffer_count, 3); + capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); } - capabilities->minImageCount = std::min(max_buffer_count, 3); - capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); capabilities->currentExtent = VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; @@ -692,6 +719,17 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( capabilities->minImageExtent = VkExtent2D{1, 1}; capabilities->maxImageExtent = VkExtent2D{4096, 4096}; + if (capabilities->maxImageExtent.height < + capabilities->currentExtent.height) { + capabilities->maxImageExtent.height = + capabilities->currentExtent.height; + } + + if (capabilities->maxImageExtent.width < + capabilities->currentExtent.width) { + capabilities->maxImageExtent.width = capabilities->currentExtent.width; + } + capabilities->maxImageArrayLayers = 1; capabilities->supportedTransforms = kSupportedTransforms; @@ -720,7 +758,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); - bool wide_color_support = false; uint64_t consumer_usage = 0; bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); @@ -731,27 +768,15 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (!surfaceless_enabled) { return VK_ERROR_SURFACE_LOST_KHR; } - // Support for VK_GOOGLE_surfaceless_query. The EGL loader - // unconditionally supports wide color formats, even if they will cause - // a SurfaceFlinger fallback. Based on that, wide_color_support will be - // set to true in this case. - wide_color_support = true; + // Support for VK_GOOGLE_surfaceless_query. // TODO(b/203826952): research proper value; temporarily use the // values seen on Pixel consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; } else { Surface& surface = *SurfaceFromHandle(surface_handle); - int err = native_window_get_wide_color_support(surface.window.get(), - &wide_color_support); - if (err) { - return VK_ERROR_SURFACE_LOST_KHR; - } - ALOGV("wide_color_support is: %d", wide_color_support); - consumer_usage = surface.consumer_usage; } - wide_color_support = wide_color_support && colorspace_ext; AHardwareBuffer_Desc desc = {}; desc.width = 1; @@ -763,14 +788,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, // We must support R8G8B8A8 std::vector<VkSurfaceFormatKHR> all_formats = { {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + }; if (colorspace_ext) { all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); - } - - if (wide_color_support) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -785,13 +812,21 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R5G6B5_UNORM_PACK16, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); + } } desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); - if (wide_color_support) { + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}); @@ -806,7 +841,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); - if (wide_color_support) { + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); @@ -815,9 +853,26 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM; if (AHardwareBuffer_isSupported(&desc)) { + if (colorspace_ext) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + } + } + + // TODO query VK_EXT_rgba10x6_formats support + desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM; + if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R8_UNORM, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); + VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + } } // NOTE: Any new formats that are added must be coordinated across different @@ -895,25 +950,87 @@ VkResult GetPhysicalDeviceSurfaceFormats2KHR( return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount, nullptr); - } else { - // temp vector for forwarding; we'll marshal it into the pSurfaceFormats - // after the call. - std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount); - VkResult result = GetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount, - surface_formats.data()); - - if (result == VK_SUCCESS || result == VK_INCOMPLETE) { - // marshal results individually due to stride difference. - // completely ignore any chained extension structs. - uint32_t formats_to_marshal = *pSurfaceFormatCount; - for (uint32_t i = 0u; i < formats_to_marshal; i++) { - pSurfaceFormats[i].surfaceFormat = surface_formats[i]; - } - } + } + // temp vector for forwarding; we'll marshal it into the pSurfaceFormats + // after the call. + std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount); + VkResult result = GetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount, + surface_formats.data()); + + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { return result; } + + const auto& driver = GetData(physicalDevice).driver; + + // marshal results individually due to stride difference. + uint32_t formats_to_marshal = *pSurfaceFormatCount; + for (uint32_t i = 0u; i < formats_to_marshal; i++) { + pSurfaceFormats[i].surfaceFormat = surface_formats[i]; + + // Query the compression properties for the surface format + VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i]; + while (pSurfaceFormat->pNext) { + pSurfaceFormat = + reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext); + switch (pSurfaceFormat->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: { + VkImageCompressionPropertiesEXT* surfaceCompressionProps = + reinterpret_cast<VkImageCompressionPropertiesEXT*>( + pSurfaceFormat); + + if (surfaceCompressionProps && + driver.GetPhysicalDeviceImageFormatProperties2KHR) { + VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {}; + imageFormatInfo.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; + imageFormatInfo.format = + pSurfaceFormats[i].surfaceFormat.format; + imageFormatInfo.pNext = nullptr; + + VkImageCompressionControlEXT compressionControl = {}; + compressionControl.sType = + VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT; + compressionControl.pNext = imageFormatInfo.pNext; + + imageFormatInfo.pNext = &compressionControl; + + VkImageCompressionPropertiesEXT compressionProps = {}; + compressionProps.sType = + VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT; + compressionProps.pNext = nullptr; + + VkImageFormatProperties2KHR imageFormatProps = {}; + imageFormatProps.sType = + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR; + imageFormatProps.pNext = &compressionProps; + + VkResult compressionRes = + driver.GetPhysicalDeviceImageFormatProperties2KHR( + physicalDevice, &imageFormatInfo, + &imageFormatProps); + if (compressionRes == VK_SUCCESS) { + surfaceCompressionProps->imageCompressionFlags = + compressionProps.imageCompressionFlags; + surfaceCompressionProps + ->imageCompressionFixedRateFlags = + compressionProps.imageCompressionFixedRateFlags; + } else { + return compressionRes; + } + } + } break; + + default: + // Ignore all other extension structs + break; + } + } + } + + return result; } VKAPI_ATTR @@ -939,8 +1056,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, // VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and // VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR. We technically cannot // know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a - // surface, and that cannot be relied upon. - present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); + // surface, and that cannot be relied upon. Therefore, don't return it. present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } else { ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); @@ -1228,11 +1344,15 @@ VkResult CreateSwapchainKHR(VkDevice device, decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } - err = native_window_set_buffers_data_space(window, native_dataspace); - if (err != android::OK) { - ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)", - native_dataspace, strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; + + /* Respect consumer default dataspace upon HAL_DATASPACE_ARBITRARY. */ + if (native_dataspace != HAL_DATASPACE_ARBITRARY) { + err = native_window_set_buffers_data_space(window, native_dataspace); + if (err != android::OK) { + ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)", + native_dataspace, strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } } err = native_window_set_buffers_dimensions( @@ -1323,8 +1443,48 @@ VkResult CreateSwapchainKHR(VkDevice device, num_images = 1; } - int32_t legacy_usage = 0; - if (dispatch.GetSwapchainGrallocUsage2ANDROID) { + void* usage_info_pNext = nullptr; + VkImageCompressionControlEXT image_compression = {}; + uint64_t native_usage = 0; + if (dispatch.GetSwapchainGrallocUsage3ANDROID) { + ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID"); + VkGrallocUsageInfoANDROID gralloc_usage_info = {}; + gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID; + gralloc_usage_info.format = create_info->imageFormat; + gralloc_usage_info.imageUsage = create_info->imageUsage; + + // Look through the pNext chain for an image compression control struct + // if one is found AND the appropriate extensions are enabled, + // append it to be the gralloc usage pNext chain + const VkSwapchainCreateInfoKHR* create_infos = create_info; + while (create_infos->pNext) { + create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( + create_infos->pNext); + switch (create_infos->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { + const VkImageCompressionControlEXT* compression_infos = + reinterpret_cast<const VkImageCompressionControlEXT*>( + create_infos); + image_compression = *compression_infos; + image_compression.pNext = nullptr; + usage_info_pNext = &image_compression; + } break; + + default: + // Ignore all other info structs + break; + } + } + gralloc_usage_info.pNext = usage_info_pNext; + + result = dispatch.GetSwapchainGrallocUsage3ANDROID( + device, &gralloc_usage_info, &native_usage); + ATRACE_END(); + if (result != VK_SUCCESS) { + ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result); + return VK_ERROR_SURFACE_LOST_KHR; + } + } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) { uint64_t consumer_usage, producer_usage; ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID"); result = dispatch.GetSwapchainGrallocUsage2ANDROID( @@ -1335,10 +1495,11 @@ VkResult CreateSwapchainKHR(VkDevice device, ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } - legacy_usage = - android_convertGralloc1To0Usage(producer_usage, consumer_usage); + native_usage = + convertGralloc1ToBufferUsage(producer_usage, consumer_usage); } else if (dispatch.GetSwapchainGrallocUsageANDROID) { ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID"); + int32_t legacy_usage = 0; result = dispatch.GetSwapchainGrallocUsageANDROID( device, create_info->imageFormat, create_info->imageUsage, &legacy_usage); @@ -1347,8 +1508,9 @@ VkResult CreateSwapchainKHR(VkDevice device, ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } + native_usage = static_cast<uint64_t>(legacy_usage); } - uint64_t native_usage = static_cast<uint64_t>(legacy_usage); + native_usage |= surface.consumer_usage; bool createProtectedSwapchain = false; if (create_info->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) { @@ -1388,7 +1550,7 @@ VkResult CreateSwapchainKHR(VkDevice device, #pragma clang diagnostic ignored "-Wold-style-cast" .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID, #pragma clang diagnostic pop - .pNext = nullptr, + .pNext = usage_info_pNext, .usage = swapchain_image_usage, }; VkNativeBufferANDROID image_native_buffer = { @@ -1446,6 +1608,7 @@ VkResult CreateSwapchainKHR(VkDevice device, android_convertGralloc0To1Usage(int(img.buffer->usage), &image_native_buffer.usage2.producer, &image_native_buffer.usage2.consumer); + image_native_buffer.usage3 = img.buffer->usage; ATRACE_BEGIN("CreateImage"); result = diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index 0daad9c634..a6d540bb93 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -27,7 +27,6 @@ cc_library_shared { proprietary: true, relative_install_path: "hw", - clang: true, cflags: [ "-fvisibility=hidden", "-fstrict-aliasing", diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index 3c91150d45..f998b1ad18 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -948,6 +948,17 @@ VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice, return VK_SUCCESS; } +VkResult GetSwapchainGrallocUsage3ANDROID( + VkDevice, + const VkGrallocUsageInfoANDROID* grallocUsageInfo, + uint64_t* grallocUsage) { + // The null driver never reads or writes the gralloc buffer + ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__, + grallocUsageInfo->format); + *grallocUsage = 0; + return VK_SUCCESS; +} + VkResult AcquireImageANDROID(VkDevice, VkImage, int fence, diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index f6dcf0900c..0cb7bd3185 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -261,6 +261,7 @@ const NameProc kInstanceProcs[] = { {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))}, {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))}, {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))}, + {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))}, {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))}, {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))}, {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))}, diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index 3e003e3189..5c7fea0fa8 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -209,6 +209,7 @@ VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueu VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage); VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); +VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage); VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py index 4176509447..c25c6cbda0 100644 --- a/vulkan/scripts/generator_common.py +++ b/vulkan/scripts/generator_common.py @@ -69,6 +69,7 @@ _EXPORTED_EXTENSIONS = [ _OPTIONAL_COMMANDS = [ 'vkGetSwapchainGrallocUsageANDROID', 'vkGetSwapchainGrallocUsage2ANDROID', + 'vkGetSwapchainGrallocUsage3ANDROID', ] # Dict for mapping dispatch table to a type. diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp index fa0258bc06..b544245a7a 100644 --- a/vulkan/vkjson/Android.bp +++ b/vulkan/vkjson/Android.bp @@ -25,10 +25,8 @@ cc_library_shared { ".", ], shared_libs: [ - "libvulkan", - ], - whole_static_libs: [ "libjsoncpp", + "libvulkan", ], export_shared_lib_headers: [ "libvulkan", @@ -37,7 +35,6 @@ cc_library_shared { cc_library_static { name: "libvkjson_ndk", - clang: true, srcs: [ "vkjson.cc", "vkjson_instance.cc", diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index da6b00a0c6..0284192cc4 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -731,7 +731,7 @@ inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Features* features visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) && visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) && visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) && - visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer); + visitor->Visit("subgroupBroadcastDynamicId", &features->subgroupBroadcastDynamicId); } template <typename Visitor> |