Merge "DO NOT MERGE: Force input window updates when layer visibility changes" into tm-qpr-dev am: 34cf3256da
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/22434165
Change-Id: I524eb27f1d2d4bc5aff12bb0837bd3c697c57439
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.clang-format b/.clang-format
index 6725a1f..f63f670 100644
--- a/.clang-format
+++ b/.clang-format
@@ -12,3 +12,6 @@
PenaltyBreakBeforeFirstCallParameter: 100000
SpacesBeforeTrailingComments: 1
IncludeBlocks: Preserve
+
+DerivePointerAlignment: false
+PointerAlignment: Left
diff --git a/Android.bp b/Android.bp
index dec6716..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
cc_library_headers {
name: "libandroid_sensor_headers",
- vendor: true,
+ vendor_available: true,
+ host_supported: true,
export_include_dirs: ["include_sensor"],
}
@@ -64,14 +65,20 @@
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 @@
],
}
-cc_library_headers{
+cc_library_headers {
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 6d837c2..2ce3fb0 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,10 +1,13 @@
[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/dumpstate/
cmds/idlcli/
cmds/installd/
cmds/servicemanager/
@@ -12,11 +15,13 @@
include/powermanager/
libs/binder/fuzzer/
libs/binder/
+ libs/binderdebug/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
libs/input/
libs/nativedisplay/
+ libs/nativewindow/
libs/renderengine/
libs/ui/
libs/vr/
@@ -37,3 +42,4 @@
dumpsys_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/dumpsys/"
# bugreports matches both cmds/bugreport and cmds/bugreportz
bugreports_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/bugreport"
+binder_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^libs/binder/"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 260ee8d..e66bca0 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 7e89220..bb3faaf 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 0000000..faa43b2
--- /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 6fb9a4d..8105626 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 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_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 @@
{ 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 @@
} },
};
-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 @@
// 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_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 @@
}
}
}
+ 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 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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
if (ok && traceStart && !onlyUserspace) {
ok &= setUpKernelTracing();
- ok &= setUpVendorTracing();
+ ok &= setUpVendorTracingWithHal();
ok &= startTrace();
}
@@ -1396,7 +1475,7 @@
if (traceStop) {
cleanUpUserspaceTracing();
if (!onlyUserspace) {
- cleanUpVendorTracing();
+ cleanUpVendorTracingWithHal();
cleanUpKernelTracing();
}
}
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index d0b5d59..07e586e 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -181,6 +181,8 @@
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
@@ -302,6 +304,12 @@
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
# will likely fail with EBUSY as it would be in use by traced_probes.
@@ -390,6 +398,130 @@
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
+
+# host_hcall event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_hcall/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_hcall/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_hcall/id
+
+# host_smc event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_smc/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_smc/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_smc/id
+
+# host_mem_abort event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/id
+
+
on property:persist.debug.atrace.boottrace=1
start boottrace
@@ -397,3 +529,10 @@
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 9186514..fa7be18 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -18,3 +18,9 @@
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 eb0d898..3606827 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/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp
index a65f6de..faf461a 100644
--- a/cmds/cmd/fuzzer/Android.bp
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -42,5 +42,13 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 155276,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libcmd",
+ vector: "local_no_privileges_required",
+ service_privilege: "constrained",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a2491e5..a62bd01 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -101,6 +101,7 @@
"libhidlbase",
"liblog",
"libutils",
+ "libvintf",
"libbinderdebug",
"packagemanager_aidl-cpp",
],
@@ -125,6 +126,7 @@
],
required: [
"atrace",
+ "bugreport_procdump",
"dmabuf_dump",
"ip",
"iptables",
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 5f56531..ab81ecf 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -3,3 +3,4 @@
gavincorkery@google.com
nandana@google.com
jsharkey@android.com
+smoreland@google.com
\ No newline at end of file
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 839a2c3..649a13e 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 ec1e89b..0e42992 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 @@
#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 @@
#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_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 @@
// 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 @@
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 @@
}
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 @@
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,13 +806,16 @@
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");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+ printf("Bootconfig: ");
+ DumpFileToFd(STDOUT_FILENO, "", "/proc/bootconfig");
printf("Uptime: ");
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
@@ -829,7 +851,8 @@
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+ size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression;
+ int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), flags,
get_mtime(fd, ds.now_));
if (err != 0) {
MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
@@ -921,7 +944,8 @@
bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
- int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+ size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression;
+ int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), flags, ds.now_);
if (err != 0) {
MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
ZipWriter::ErrorCodeString(err));
@@ -1020,7 +1044,7 @@
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 +1057,26 @@
}
}
+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 +1102,7 @@
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 +1123,16 @@
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;
@@ -1200,8 +1254,10 @@
dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
dumpsys.writeDumpFooter(STDOUT_FILENO, service, std::chrono::milliseconds(1));
} else {
- status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
- if (status == OK) {
+ status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP | Dumpsys::TYPE_PID |
+ Dumpsys::TYPE_CLIENTS | Dumpsys::TYPE_THREAD,
+ service, args);
+ if (status == OK) {
dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
std::chrono::duration<double> elapsed_seconds;
if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
@@ -1218,6 +1274,9 @@
dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
bool dump_complete = (status == OK);
dumpsys.stopDumpThread(dump_complete);
+ } else {
+ MYLOGE("Failed to start dump thread for service: %s, status: %d",
+ String8(service).c_str(), status);
}
}
@@ -1394,6 +1453,25 @@
}
}
+// 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 +1564,6 @@
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 +1627,8 @@
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 +1637,8 @@
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 +1654,8 @@
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 +1672,6 @@
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 +1697,9 @@
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 +1730,8 @@
DoKmsg();
+ DumpShutdownCheckpoints();
+
DumpIpAddrAndRules();
dump_route_tables();
@@ -1755,6 +1835,13 @@
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 +1886,14 @@
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);
@@ -1812,9 +1902,17 @@
}
ds.AddDir(PREREBOOT_DATA_DIR, false);
add_mountinfo();
+ for (const char* path : {"/proc/cpuinfo", "/proc/meminfo"}) {
+ ds.AddZipEntry(ZIP_ROOT_DIR + path, path);
+ }
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 +1934,9 @@
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));
@@ -1964,6 +2065,10 @@
SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"isub"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"telecom"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
if (include_sensitive_info) {
// Contains raw IP addresses, omit from reports on user builds.
RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
@@ -2696,6 +2801,7 @@
options->do_screenshot = false;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
+ options->do_vibrate = false;
options->do_progress_updates = true;
options->do_screenshot = is_screenshot_requested;
break;
@@ -2847,6 +2953,7 @@
}
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 +3183,9 @@
TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
// Zip the (now complete) .tmp file within the internal directory.
+ ATRACE_BEGIN("FinalizeFile");
FinalizeFile();
+ ATRACE_END();
// Share the final file with the caller if the user has consented or Shell is the caller.
Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
@@ -3128,6 +3237,7 @@
tombstone_data_.clear();
anr_data_.clear();
+ shutdown_checkpoints_.clear();
return (consent_callback_ != nullptr &&
consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
@@ -3164,15 +3274,7 @@
}
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 +3283,14 @@
"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 +3506,9 @@
duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
+ if (title_.find("SHOW MAP") == std::string::npos) {
+ ATRACE_ASYNC_BEGIN(title_.c_str(), 0);
+ }
}
}
@@ -3410,6 +3523,9 @@
dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
elapsed, title_.c_str());
}
+ if (title_.find("SHOW MAP") == std::string::npos) {
+ ATRACE_ASYNC_END(title_.c_str(), 0);
+ }
}
}
@@ -3861,15 +3977,6 @@
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 ee6b1ae..7ffe80e 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -499,6 +499,9 @@
// 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 @@
/* 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 70b4e5c..0012177 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -285,8 +285,8 @@
// Other options retain default values
- EXPECT_TRUE(options_.do_vibrate);
EXPECT_FALSE(options_.progress_updates_to_socket);
+ EXPECT_FALSE(options_.do_vibrate);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.stream_to_socket);
@@ -1023,7 +1023,8 @@
};
// 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 f0c19b9..b8e5ce1 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -60,6 +60,7 @@
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 c9f680b..ac101ec 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -25,6 +25,7 @@
"CrateManager.cpp",
"InstalldNativeService.cpp",
"QuotaUtils.cpp",
+ "SysTrace.cpp",
"dexopt.cpp",
"execv_helper.cpp",
"globals.cpp",
@@ -72,8 +73,6 @@
},
},
- clang: true,
-
tidy: true,
tidy_checks: [
"-*",
@@ -81,8 +80,9 @@
"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_test_host {
name: "run_dex2oat_test",
test_suites: ["general-tests"],
- clang: true,
srcs: [
"run_dex2oat_test.cpp",
"run_dex2oat.cpp",
@@ -175,7 +174,7 @@
// 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 @@
"-Wall",
"-Werror",
],
- clang: true,
srcs: [
"otapreopt_chroot.cpp",
@@ -302,6 +300,6 @@
// 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 6d3f7c3..bb6639e 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 @@
namespace {
-constexpr const char* kDump = "android.permission.DUMP";
-
static binder::Status ok() {
return binder::Status::ok();
}
@@ -153,19 +150,6 @@
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 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 @@
} \
}
+#define CHECK_ARGUMENT_FILE_NAME(path) \
+ { \
+ binder::Status status = checkArgumentFileName((path)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+ }
+
#ifdef GRANULAR_LOCKS
/**
@@ -382,13 +387,7 @@
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 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 @@
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 @@
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 @@
}
}
fts_close(fts);
- ATRACE_END();
+ atrace_pm_end();
}
return ok();
}
@@ -1871,8 +1872,9 @@
binder::Status res = ok();
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_path(uuid_, userId);
- if (delete_dir_contents_and_dir(path, true) != 0) {
- res = error("Failed to delete " + path);
+ // Contents only, as vold is responsible for the user_de dir itself.
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
}
auto sdk_sandbox_de_path =
create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId);
@@ -1892,8 +1894,9 @@
}
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_path(uuid_, userId);
- if (delete_dir_contents_and_dir(path, true) != 0) {
- res = error("Failed to delete " + path);
+ // Contents only, as vold is responsible for the user_ce dir itself.
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
}
auto sdk_sandbox_ce_path =
create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId);
@@ -1901,8 +1904,9 @@
res = error("Failed to delete " + sdk_sandbox_ce_path);
}
path = findDataMediaPath(uuid, userId);
- if (delete_dir_contents_and_dir(path, true) != 0) {
- res = error("Failed to delete " + path);
+ // Contents only, as vold is responsible for the media dir itself.
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
}
}
return res;
@@ -1927,7 +1931,6 @@
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 @@
// 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 @@
}
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 @@
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 @@
}
active->cacheUsed -= item->size;
needed -= item->size;
- cleared += item->size;
}
if (!defy_target) {
@@ -2100,7 +2100,7 @@
}
}
}
- ATRACE_END();
+ atrace_pm_end();
} else {
return error("Legacy cache logic no longer supported");
@@ -2445,84 +2445,84 @@
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 @@
}
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 @@
}
}
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 @@
}
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 @@
}
}
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 @@
}
}
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 @@
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 @@
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 @@
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 0000000..fa65c77
--- /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 0000000..18506a9
--- /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 ebb7891..1693ed5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -442,6 +442,16 @@
static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
+ if (read_write && GetBoolProperty("dalvik.vm.useartservice", false)) {
+ // ART Service doesn't use flock and instead assumes profile files are
+ // immutable, so ensure we don't open a file for writing when it's
+ // active.
+ // TODO(b/251921228): Normally installd isn't called at all in that
+ // case, but OTA is still an exception that uses the legacy code.
+ LOG(ERROR) << "Opening ref profile " << profile
+ << " for writing is unsafe when ART Service is enabled.";
+ return invalid_unique_fd();
+ }
return open_profile(
uid,
profile,
@@ -450,14 +460,13 @@
}
static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
- const std::string& location, bool read_write, bool is_secondary_dex) {
+ const std::string& location,
+ bool is_secondary_dex) {
std::string profile_path = create_reference_profile_path(package_name, location,
is_secondary_dex);
- unique_fd ufd = open_profile(
- uid,
- profile_path,
- read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
- S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
+ unique_fd ufd = open_profile(uid, profile_path, O_RDONLY,
+ S_IRUSR | S_IWUSR |
+ S_IRGRP); // so that ART can also read it when apps run.
return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
clear_profile(path);
@@ -1104,8 +1113,7 @@
location = profile_name;
}
}
- return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
- is_secondary_dex);
+ return open_reference_profile_as_unique_file(uid, pkgname, location, is_secondary_dex);
}
// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
@@ -1909,10 +1917,11 @@
// Open the reference profile if needed.
UniqueFile reference_profile = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
-
- if (reference_profile.fd() == -1) {
- // We don't create an app image without reference profile since there is no speedup from
- // loading it in that case and instead will be a small overhead.
+ struct stat sbuf;
+ if (reference_profile.fd() == -1 ||
+ (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+ // We don't create an app image with empty or non existing reference profile since there
+ // is no speedup from loading it in that case and instead will be a small overhead.
generate_app_image = false;
}
@@ -1933,7 +1942,8 @@
RUNTIME_NATIVE_BOOT_NAMESPACE,
ENABLE_JITZYGOTE_IMAGE,
/*default_value=*/ "");
- bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable();
+ bool compile_without_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable() ||
+ force_compile_without_image();
// Decide whether to use dex2oat64.
bool use_dex2oat64 = false;
@@ -1955,8 +1965,8 @@
in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
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);
+ enable_hidden_api_checks, generate_compact_dex, compile_without_image,
+ background_job_compile, compilation_reason);
bool cancelled = false;
pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index b5bc28c..4f691c9 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -65,6 +65,10 @@
return create_cache_path_default(path, src, instruction_set);
}
+bool force_compile_without_image() {
+ return false;
+}
+
static bool initialize_globals() {
return init_globals_from_data_and_root();
}
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 240aa49..5b08c77 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/installd_deps.h b/cmds/installd/installd_deps.h
index 5093178..0d0a7fa 100644
--- a/cmds/installd/installd_deps.h
+++ b/cmds/installd/installd_deps.h
@@ -57,6 +57,9 @@
const char *src,
const char *instruction_set);
+// If true, pass "--force-jit-zygote" to dex2oat (i.e., compile without a boot image).
+extern bool force_compile_without_image();
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index e978e79..7cabdb0 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>
@@ -307,7 +308,7 @@
// This is different from the normal installd. We only do the base
// directory, the rest will be created on demand when each app is compiled.
if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
- LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+ PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
return false;
}
@@ -459,7 +460,7 @@
// this tool will wipe the OTA artifact cache and try again (for robustness after
// a failed OTA with remaining cache artifacts).
if (access(apk_path, F_OK) != 0) {
- LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+ PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
return true;
}
@@ -504,6 +505,11 @@
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) {
@@ -705,6 +711,11 @@
return true;
}
+bool force_compile_without_image() {
+ // We don't have a boot image anyway. Compile without a boot image.
+ return true;
+}
+
static int log_callback(int type, const char *fmt, ...) {
va_list ap;
int priority;
diff --git a/cmds/installd/otapreopt.rc b/cmds/installd/otapreopt.rc
index 059ae75..0bad0c5 100644
--- a/cmds/installd/otapreopt.rc
+++ b/cmds/installd/otapreopt.rc
@@ -5,4 +5,4 @@
# 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/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..1b7acab 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
namespace android {
namespace installd {
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -82,6 +86,27 @@
}
}
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+ for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+ const char* fstype = kTryMountFsTypes[i];
+ int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+ if (mount_result == 0) {
+ return true;
+ }
+ if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+ // Only try the next fstype if mounting failed due to the current one
+ // being invalid.
+ LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype << " - trying " << kTryMountFsTypes[i + 1];
+ } else {
+ PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype;
+ return false;
+ }
+ }
+ __builtin_unreachable();
+}
+
static void TryExtraMount(const char* name, const char* slot, const char* target) {
std::string partition_name = StringPrintf("%s%s", name, slot);
@@ -91,12 +116,7 @@
if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
std::string path;
if (dm.GetDmDevicePathByName(partition_name, &path)) {
- int mount_result = mount(path.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- if (mount_result == 0) {
+ if (TryMountWithFstypes(path.c_str(), target)) {
return;
}
}
@@ -105,12 +125,7 @@
// Fall back and attempt a direct mount.
std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
- int mount_result = mount(block_device.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(mount_result);
+ (void)TryMountWithFstypes(block_device.c_str(), target);
}
// Entry for otapreopt_chroot. Expected parameters are:
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
i=0
while ((i<MAXIMUM_PACKAGES)) ; do
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+
DEXOPT_PARAMS=$(cmd otadexopt next)
/system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
PROGRESS=$(cmd otadexopt progress)
print -u${STATUS_FD} "global_progress $PROGRESS"
- DONE=$(cmd otadexopt done)
- if [ "$DONE" = "OTA incomplete." ] ; then
- sleep 1
- i=$((i+1))
- continue
- fi
- break
+ sleep 1
+ i=$((i+1))
done
DONE=$(cmd otadexopt done)
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 51c4589..4221a3a 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -81,6 +81,7 @@
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 @@
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::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 @@
"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 @@
"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 559244f..c13e1f1 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -51,6 +51,7 @@
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 @@
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 2a8135a..304ba7b 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -115,6 +115,7 @@
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 @@
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 @@
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 @@
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 b3baca5..07f73b9 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -11,7 +11,6 @@
cc_test {
name: "installd_utils_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_utils_test.cpp"],
cflags: [
"-Wall",
@@ -36,7 +35,6 @@
cc_test {
name: "installd_cache_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_cache_test.cpp"],
cflags: [
"-Wall",
@@ -82,7 +80,6 @@
cc_test {
name: "installd_service_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_service_test.cpp"],
cflags: [
"-Wall",
@@ -130,7 +127,6 @@
cc_test {
name: "installd_dexopt_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_dexopt_test.cpp"],
cflags: [
"-Wall",
@@ -177,7 +173,6 @@
cc_test {
name: "installd_otapreopt_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_otapreopt_test.cpp"],
cflags: [
"-Wall",
@@ -198,7 +193,6 @@
cc_test {
name: "installd_file_test",
test_suites: ["device-tests"],
- clang: true,
srcs: ["installd_file_test.cpp"],
cflags: [
"-Wall",
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 4976646..a00c2c7 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -67,6 +67,10 @@
return false;
}
+bool force_compile_without_image() {
+ return false;
+}
+
static void mkdir(const char* path) {
const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
::mkdir(fullPath.c_str(), 0755);
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 4eb30e2..c4071c6 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 @@
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);
@@ -87,6 +73,10 @@
return create_cache_path_default(path, src, instruction_set);
}
+bool force_compile_without_image() {
+ return false;
+}
+
static void run_cmd(const std::string& cmd) {
system(cmd.c_str());
}
@@ -199,7 +189,7 @@
std::optional<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
- std::string empty_dm_file_;
+ std::string dm_file_;
std::string app_apk_dir_;
std::string app_private_dir_ce_;
std::string app_private_dir_de_;
@@ -213,6 +203,10 @@
std::string secondary_dex_de_;
virtual void SetUp() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+ }
+
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(nullptr);
// Initialize the globals holding the file system main paths (/data/, /system/ etc..).
@@ -233,6 +227,10 @@
}
virtual void TearDown() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP();
+ }
+
if (!kDebug) {
service_->controlDexOptBlocking(false);
service_->destroyAppData(
@@ -262,26 +260,6 @@
<< " : " << error_msg;
}
- // Create an empty dm file.
- empty_dm_file_ = apk_path_ + ".dm";
- {
- int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_;
- }
- FILE* file = fdopen(fd, "wb");
- if (file == nullptr) {
- return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_
- << " fd=" << fd;
- }
- ZipWriter writer(file);
- // Add vdex to zip.
- writer.StartEntry("primary.prof", ZipWriter::kCompress);
- writer.FinishEntry();
- writer.Finish();
- fclose(file);
- }
-
// Create the app user data.
binder::Status status = service_->createAppData(
volume_uuid_,
@@ -330,6 +308,46 @@
<< secondary_dex_de_ << " : " << error_msg;
}
+ // Create a non-empty dm file.
+ dm_file_ = apk_path_ + ".dm";
+ {
+ android::base::unique_fd fd(open(dm_file_.c_str(),
+ O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
+ if (fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Could not open " << dm_file_;
+ }
+ FILE* file = fdopen(fd.release(), "wb");
+ if (file == nullptr) {
+ return ::testing::AssertionFailure() << "Null file for " << dm_file_
+ << " fd=" << fd.get();
+ }
+
+ // Create a profile file.
+ std::string profile_file = app_private_dir_ce_ + "/primary.prof";
+ run_cmd("profman --generate-test-profile=" + profile_file);
+
+ // Add profile to zip.
+ ZipWriter writer(file);
+ writer.StartEntry("primary.prof", ZipWriter::kCompress);
+ android::base::unique_fd profile_fd(open(profile_file.c_str(), O_RDONLY));
+ if (profile_fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Failed to open profile '"
+ << profile_file << "'";
+ }
+ std::string profile_content;
+ if (!android::base::ReadFdToString(profile_fd, &profile_content)) {
+ return ::testing::AssertionFailure() << "Failed to read profile "
+ << profile_file << "'";
+ }
+ writer.WriteBytes(profile_content.c_str(), profile_content.length());
+ writer.FinishEntry();
+ writer.Finish();
+ fclose(file);
+
+ // Delete the temp file.
+ unlink(profile_file.c_str());
+ }
+
// Fix app data uid.
status = service_->fixupAppData(volume_uuid_, kTestUserId);
if (!status.isOk()) {
@@ -622,7 +640,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_,
@@ -671,13 +689,13 @@
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC |
DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC);
CompilePrimaryDexOk("speed-profile",
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE);
}
};
@@ -801,7 +819,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
@@ -813,7 +831,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
@@ -825,7 +843,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptBlockPrimary) {
@@ -888,7 +906,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -907,7 +925,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -920,6 +938,11 @@
TEST_F(DexoptTest, DexoptDex2oat64Enabled) {
LOG(INFO) << "DexoptDex2oat64Enabled";
+ std::string zygote_prop = android::base::GetProperty("ro.zygote", "");
+ ASSERT_GT(zygote_prop.size(), 0);
+ if (zygote_prop != "zygote32_64" && zygote_prop != "zygote64_32") {
+ GTEST_SKIP() << "DexoptDex2oat64Enabled skipped for single-bitness Zygote.";
+ }
const std::string property = "dalvik.vm.dex2oat64.enabled";
const std::string previous_value = android::base::GetProperty(property, "");
auto restore_property = android::base::make_scope_guard([=]() {
@@ -935,7 +958,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
// Enable the property and use dex2oat64.
ASSERT_TRUE(android::base::SetProperty(property, "true")) << property;
CompilePrimaryDexOk("speed-profile",
@@ -945,12 +968,16 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
class PrimaryDexReCompilationTest : public DexoptTest {
public:
virtual void SetUp() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+ }
+
DexoptTest::SetUp();
CompilePrimaryDexOk("verify",
DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
@@ -965,6 +992,10 @@
}
virtual void TearDown() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP();
+ }
+
first_compilation_odex_fd_.reset(-1);
first_compilation_vdex_fd_.reset(-1);
DexoptTest::TearDown();
@@ -987,6 +1018,10 @@
class ReconcileTest : public DexoptTest {
virtual void SetUp() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+ }
+
DexoptTest::SetUp();
CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
/*binder_ok*/ true, /*compile_ok*/ true);
@@ -1052,6 +1087,10 @@
static constexpr const char* kPrimaryProfile = "primary.prof";
virtual void SetUp() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+ }
+
DexoptTest::SetUp();
cur_profile_ = create_current_profile_path(
kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/ false);
@@ -1152,7 +1191,7 @@
service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL,
kTestAppId, profile_name, apk_path_,
has_dex_metadata ? std::make_optional<std::string>(
- empty_dm_file_)
+ dm_file_)
: std::nullopt,
&result));
ASSERT_EQ(expected_result, result);
@@ -1366,6 +1405,58 @@
/*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:
@@ -1373,6 +1464,9 @@
std::vector<int64_t> extra_ce_data_inodes_;
virtual void SetUp() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+ }
ProfileTest::SetUp();
intial_android_profiles_dir = android_profiles_dir;
@@ -1386,6 +1480,10 @@
}
virtual void TearDown() {
+ if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+ GTEST_SKIP();
+ }
+
android_profiles_dir = intial_android_profiles_dir;
deleteAppProfilesForBootMerge();
ProfileTest::TearDown();
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index f86f1d5..858a92c 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -113,6 +113,10 @@
return create_cache_path_default(path, src, instruction_set);
}
+bool force_compile_without_image() {
+ return false;
+}
+
static std::string get_full_path(const std::string& path) {
return StringPrintf("%s/%s", kTestPath.c_str(), path.c_str());
}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4d9b710..ffc082d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -523,7 +523,6 @@
*/
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 @@
}
}
if (c == '.') {
- hasSep = true;
front = true;
continue;
}
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index ff73c94..e54f9d3 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -353,8 +353,16 @@
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 cbfbdc9..af85666 100644
--- a/cmds/lshal/libprocpartition/Android.bp
+++ b/cmds/lshal/libprocpartition/Android.bp
@@ -35,5 +35,6 @@
],
export_include_dirs: [
"include",
- ]
+ ],
+ min_sdk_version: "30",
}
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index d5ca725..5e8ef5d 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -75,7 +75,7 @@
ProcessState::initWithDriver("/dev/vndbinder");
#endif
#ifndef __ANDROID__
- setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
#endif
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp
index b7e520f..711038c 100644
--- a/cmds/servicemanager/Access.cpp
+++ b/cmds/servicemanager/Access.cpp
@@ -30,6 +30,7 @@
constexpr bool kIsVendor = false;
#endif
+#ifdef __ANDROID__
static std::string getPidcon(pid_t pid) {
android_errorWriteLog(0x534e4554, "121035042");
@@ -45,7 +46,6 @@
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 @@
ad->tname->c_str());
return 0;
}
+#endif
Access::Access() {
+#ifdef __ANDROID__
union selinux_callback cb;
cb.func_audit = auditCallback;
@@ -91,6 +93,7 @@
CHECK(selinux_status_open(true /*fallback*/) >= 0);
CHECK(getcon(&mThisProcessContext) == 0);
+#endif
}
Access::~Access() {
@@ -98,6 +101,7 @@
}
Access::CallingContext Access::getCallingContext() {
+#ifdef __ANDROID__
IPCThreadState* ipc = IPCThreadState::self();
const char* callingSid = ipc->getCallingSid();
@@ -108,6 +112,9 @@
.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::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 @@
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 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 32922ca..d73a30b 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,13 +44,6 @@
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,
}
@@ -79,6 +72,7 @@
cc_test {
name: "servicemanager_test",
+ host_supported: true,
test_suites: ["device-tests"],
defaults: ["servicemanager_defaults"],
srcs: [
@@ -86,3 +80,22 @@
],
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 3cfe529..980682d 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -49,14 +49,14 @@
#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 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 @@
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 @@
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 @@
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 @@
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::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,18 @@
}
#endif // !VENDORSERVICEMANAGER
+ServiceManager::Service::~Service() {
+ if (hasClients) {
+ // only expected to happen on process death, we don't store the service
+ // name this late (it's in the map that holds this service), but if it
+ // is happening, we might want to change 'unlinkToDeath' to explicitly
+ // clear this bit so that we can abort in other cases, where it would
+ // mean inconsistent logic in servicemanager (unexpected and tested, but
+ // the original lazy service impl here had that bug).
+ 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 +305,13 @@
}
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;
}
@@ -296,48 +337,81 @@
auto ctx = mAccess->getCallingContext();
if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services");
+ return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services.");
}
if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial");
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
if (binder == nullptr) {
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder");
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder.");
}
if (!isValidServiceName(name)) {
- LOG(ERROR) << "Invalid service name: " << name;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name");
+ ALOGE("Invalid service name: %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
}
#ifndef VENDORSERVICEMANAGER
if (!meetsDeclarationRequirements(binder, name)) {
// already logged
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error");
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error.");
}
#endif // !VENDORSERVICEMANAGER
+ if ((dumpPriority & DUMP_FLAG_PRIORITY_ALL) == 0) {
+ ALOGW("Dump flag priority is not set when adding %s", name.c_str());
+ }
+
// 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;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
+ ALOGE("Could not linkToDeath when adding %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
+ }
+
+ 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);
}
@@ -348,7 +422,7 @@
Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) {
if (!mAccess->canList(mAccess->getCallingContext())) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
size_t toReserve = 0;
@@ -381,19 +455,19 @@
}
if (!isValidServiceName(name)) {
- LOG(ERROR) << "Invalid service name: " << name;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ ALOGE("Invalid service name: %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
}
if (callback == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Null callback.");
}
if (OK !=
IInterface::asBinder(callback)->linkToDeath(
sp<ServiceManager>::fromExisting(this))) {
- LOG(ERROR) << "Could not linkToDeath when adding " << name;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ ALOGE("Could not linkToDeath when adding %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't link to death.");
}
mNameToRegistrationCallback[name].push_back(callback);
@@ -413,7 +487,7 @@
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
bool found = false;
@@ -424,8 +498,8 @@
}
if (!found) {
- LOG(ERROR) << "Trying to unregister callback, but none exists " << name;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ ALOGE("Trying to unregister callback, but none exists %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Nothing to unregister.");
}
return Status::ok();
@@ -435,7 +509,7 @@
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
*outReturn = false;
@@ -463,7 +537,7 @@
}
if (outReturn->size() == 0 && allInstances.size() != 0) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
return Status::ok();
@@ -474,7 +548,7 @@
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
*outReturn = std::nullopt;
@@ -485,12 +559,36 @@
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 denied.");
+ }
+
+ return Status::ok();
+}
+
Status ServiceManager::getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) {
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
*outReturn = std::nullopt;
@@ -541,15 +639,17 @@
}
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();
}
@@ -557,35 +657,37 @@
Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) {
if (cb == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Callback null.");
}
auto ctx = mAccess->getCallingContext();
if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
auto serviceIt = mNameToService.find(name);
if (serviceIt == mNameToService.end()) {
- LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ ALOGE("Could not add callback for nonexistent service: %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Service doesn't exist.");
}
- if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
- LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ 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,
+ "Only service can register client callback for itself.");
}
if (serviceIt->second.binder != service) {
- LOG(WARNING) << "Tried to register client callback for " << name
- << " but a different service is registered under this name.";
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ 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, "Service mismatch.");
}
if (OK !=
IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
- LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ ALOGE("Could not linkToDeath when adding client callback for %s", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
}
mNameToClientCallback[name].push_back(cb);
@@ -621,67 +723,74 @@
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);
@@ -692,54 +801,61 @@
Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
if (binder == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Null service.");
}
auto ctx = mAccess->getCallingContext();
if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
auto serviceIt = mNameToService.find(name);
if (serviceIt == mNameToService.end()) {
- LOG(WARNING) << "Tried to unregister " << name
- << ", but that service wasn't registered to begin with.";
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ ALOGW("Tried to unregister %s, but that service wasn't registered to begin with.",
+ name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Service not registered.");
}
- if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
- LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ 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,
+ "Service can only unregister itself.");
}
sp<IBinder> storedBinder = serviceIt->second.binder;
if (binder != storedBinder) {
- LOG(WARNING) << "Tried to unregister " << name
- << ", but a different service is registered under this name.";
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ ALOGW("Tried to unregister %s, but a different service is registered under this name.",
+ name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+ "Different service registered under this name.");
}
+ // 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.";
- return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+ "Can't unregister, pending client.");
}
- 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);
+
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+ "Can't unregister, known client.");
}
+ ALOGI("Unregistering %s", name.c_str());
mNameToService.erase(name);
return Status::ok();
@@ -747,14 +863,14 @@
Status ServiceManager::getServiceDebugInfo(std::vector<ServiceDebugInfo>* outReturn) {
if (!mAccess->canList(mAccess->getCallingContext())) {
- return Status::fromExceptionCode(Status::EX_SECURITY);
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
outReturn->reserve(mNameToService.size());
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 +878,10 @@
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 5e40319..3aa6731 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -49,6 +49,8 @@
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 @@
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 @@
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 @@
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 0000000..b76a6bd
--- /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 2fb9c2b..c1a04dd 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 @@
};
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 @@
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 @@
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 e01f132..0000000
--- 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 e5d689f..4f92b3a 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -3,7 +3,9 @@
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 067faf9..b927c01 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 5d5a75e..0fd8d8e 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 @@
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 @@
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 c9305a1..80af1d1 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -2,6 +2,7 @@
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 0000000..c405e1e
--- /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 e65b224..4746bc3 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -318,6 +318,9 @@
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/OWNERS b/include/android/OWNERS
new file mode 100644
index 0000000..38f9c55
--- /dev/null
+++ b/include/android/OWNERS
@@ -0,0 +1 @@
+per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/android/sensor.h b/include/android/sensor.h
index eef69f4..ba81bc8 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -596,12 +596,15 @@
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 @@
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 571a361..d725be1 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 @@
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/Input.h b/include/input/Input.h
index 7ea2970..1eda981 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -818,6 +818,8 @@
const PointerCoords&);
static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
const PointerCoords&);
+ // The rounding precision for transformed motion events.
+ static constexpr float ROUNDING_PRECISION = 0.001f;
protected:
int32_t mAction;
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 5084950..1da78aa 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 @@
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 @@
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 0000000..c88bfe9
--- /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 15bd5c3..ebc74fb 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 7584ca3..f9f042e 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 76e3e66..5e539f2 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -49,6 +49,9 @@
"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 b36728e..d52861a 100644
--- a/libs/arect/include/android/rect.h
+++ b/libs/arect/include/android/rect.h
@@ -57,7 +57,7 @@
} ARect;
#ifdef __cplusplus
-};
+}
#endif
#endif // ANDROID_RECT_H
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index ea3c341..2bf15d4 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -28,11 +28,9 @@
"-Werror",
],
srcs: [
- "HmacKeyManager.cpp"
+ "HmacKeyManager.cpp",
],
- clang: true,
-
shared_libs: [
"liblog",
"libcrypto",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d8d2cf2..1f1fafc 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -60,27 +60,56 @@
//
// Currently, these are only on system android (not vendor, not host)
// TODO(b/183654927) - move these into separate libraries
-libbinder_device_interface_sources = [
- "IPermissionController.cpp",
- "PermissionCache.cpp",
- "PermissionController.cpp",
-]
-cc_library {
- name: "libbinder",
+filegroup {
+ name: "libbinder_device_interface_sources",
+ srcs: [
+ "IPermissionController.cpp",
+ "PermissionCache.cpp",
+ "PermissionController.cpp",
+ ],
+}
- version_script: "libbinder.map",
-
- // for vndbinder
- vendor_available: true,
- vndk: {
- enabled: true,
- },
- recovery_available: true,
- double_loadable: true,
+cc_defaults {
+ name: "libbinder_common_defaults",
host_supported: true,
- // TODO(b/153609531): remove when no longer needed.
- native_bridge_supported: true,
+
+ srcs: [
+ "Binder.cpp",
+ "BpBinder.cpp",
+ "Debug.cpp",
+ "FdTrigger.cpp",
+ "IInterface.cpp",
+ "IResultReceiver.cpp",
+ "Parcel.cpp",
+ "ParcelFileDescriptor.cpp",
+ "RecordedTransaction.cpp",
+ "RpcSession.cpp",
+ "RpcServer.cpp",
+ "RpcState.cpp",
+ "Stability.cpp",
+ "Status.cpp",
+ "TextOutput.cpp",
+ "Trace.cpp",
+ "Utils.cpp",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
+ ],
+
+ header_libs: [
+ "libbinder_headers",
+ ],
+}
+
+cc_defaults {
+ name: "libbinder_android_defaults",
// TODO(b/31559095): get headers from bionic on host
include_dirs: [
@@ -88,72 +117,17 @@
"bionic/libc/kernel/uapi/",
],
- // libbinder does not offer a stable wire protocol.
- // if a second copy of it is installed, then it may break after security
- // or dessert updates. Instead, apex users should use libbinder_ndk.
- apex_available: [
- "//apex_available:platform",
- ],
-
srcs: [
- "Binder.cpp",
- "BpBinder.cpp",
- "BufferedTextOutput.cpp",
- "Debug.cpp",
- "FdTrigger.cpp",
- "IInterface.cpp",
- "IMemory.cpp",
- "IPCThreadState.cpp",
- "IResultReceiver.cpp",
- "IServiceManager.cpp",
- "IShellCallback.cpp",
- "LazyServiceRegistrar.cpp",
- "MemoryBase.cpp",
- "MemoryDealer.cpp",
- "MemoryHeapBase.cpp",
- "Parcel.cpp",
- "ParcelableHolder.cpp",
- "ParcelFileDescriptor.cpp",
- "PersistableBundle.cpp",
- "ProcessState.cpp",
- "RpcSession.cpp",
- "RpcServer.cpp",
- "RpcState.cpp",
+ "OS.cpp",
"RpcTransportRaw.cpp",
- "Static.cpp",
- "Stability.cpp",
- "Status.cpp",
- "TextOutput.cpp",
- "Utils.cpp",
- ":libbinder_aidl",
],
target: {
- android: {
- srcs: libbinder_device_interface_sources,
-
- // NOT static to keep the wire protocol unfrozen
- static: {
- enabled: false,
- },
- },
- vendor: {
- exclude_srcs: libbinder_device_interface_sources,
- },
- darwin: {
- enabled: false,
- },
host: {
srcs: [
- "ServiceManagerHost.cpp",
"UtilsHost.cpp",
],
},
- recovery: {
- exclude_header_libs: [
- "libandroid_runtime_vm_headers",
- ],
- },
},
aidl: {
@@ -161,7 +135,6 @@
},
cflags: [
- "-Wall",
"-Wextra",
"-Wextra-semi",
"-Werror",
@@ -176,30 +149,25 @@
},
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,13 +185,170 @@
"abseil-*",
"android-*",
"bugprone-*",
+ "-bugprone-branch-clone", // b/155034972
"cert-*",
"clang-analyzer-*",
"google-*",
"misc-*",
"performance*",
+ "-performance-move-const-arg", // b/273486801
"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 +357,47 @@
},
}
+cc_library_static {
+ name: "libbinder_rpc_no_kernel",
+ vendor_available: true,
+ 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: [
@@ -246,6 +412,7 @@
cc_defaults {
name: "libbinder_tls_defaults",
defaults: ["libbinder_tls_shared_deps"],
+ vendor_available: true,
host_supported: true,
header_libs: [
@@ -272,6 +439,34 @@
defaults: ["libbinder_tls_defaults"],
}
+cc_library {
+ 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 +523,10 @@
enabled: false,
},
},
+ visibility: [
+ ":__subpackages__",
+ "//system/tools/aidl:__subpackages__",
+ ],
}
// TODO(b/184872979): remove once the Rust API is created.
@@ -338,6 +537,7 @@
"libbase",
"libbinder",
"libbinder_ndk",
+ "libcutils_sockets",
"liblog",
"libutils",
],
@@ -351,7 +551,11 @@
// 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 +574,7 @@
cc_library {
name: "libbatterystats_aidl",
+ host_supported: true,
srcs: [
"IBatteryStats.cpp",
],
@@ -382,6 +587,7 @@
cc_library {
name: "libprocessinfoservice_aidl",
+ host_supported: true,
srcs: [
"IProcessInfoService.cpp",
"ProcessInfoService.cpp",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 39befbe..3e49656 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(BBinder) == 20);
#endif
+// global b/c b/230079120 - consistent symbol table
#ifdef BINDER_RPC_DEV_SERVERS
-constexpr const bool kEnableRpcDevServers = true;
+bool kEnableRpcDevServers = true;
#else
-constexpr const bool kEnableRpcDevServers = false;
+bool kEnableRpcDevServers = false;
+#endif
+
+#ifdef BINDER_ENABLE_RECORDING
+bool kEnableRecording = true;
+#else
+bool kEnableRecording = false;
#endif
// Log any reply transactions for which the data exceeds this size
@@ -156,10 +170,14 @@
status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd,
const sp<IBinder>& keepAliveBinder) {
- if constexpr (!kEnableRpcDevServers) {
+ if (!kEnableRpcDevServers) {
ALOGW("setRpcClientDebug disallowed because RPC is not enabled");
return INVALID_OPERATION;
}
+ if (!kEnableKernelIpc) {
+ ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled");
+ return INVALID_OPERATION;
+ }
BBinder* local = this->localBinder();
if (local != nullptr) {
@@ -193,6 +211,17 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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(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 @@
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 @@
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 @@
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 @@
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(
- data.readStrongBinder());
+ sp<IBinder> shellCallbackBinder = data.readStrongBinder();
sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
data.readStrongBinder());
// XXX can't add virtuals until binaries are updated.
- //return shellCommand(in, out, err, args, resultReceiver);
+ // sp<IShellCallback> shellCallback = IShellCallback::asInterface(
+ // shellCallbackBinder);
+ // return shellCommand(in, out, err, args, resultReceiver);
(void)in;
(void)out;
(void)err;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 921e57c..53852d8 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 @@
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 @@
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 @@
// ---------------------------------------------------------------------------
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 @@
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 @@
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 @@
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 @@
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 @@
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 = 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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-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 @@
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 @@
// RPC binder doesn't currently support inc from weak binders
if (CC_UNLIKELY(isRpcBinder())) return false;
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return false;
+ }
+
ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle());
IPCThreadState* ipc = IPCThreadState::self();
return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false;
diff --git a/libs/binder/BuildFlags.h b/libs/binder/BuildFlags.h
new file mode 100644
index 0000000..3e9d1c2
--- /dev/null
+++ b/libs/binder/BuildFlags.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace android {
+
+#ifdef BINDER_RPC_SINGLE_THREADED
+constexpr bool kEnableRpcThreads = false;
+#else
+constexpr bool kEnableRpcThreads = true;
+#endif
+
+#ifdef BINDER_WITH_KERNEL_IPC
+constexpr bool kEnableKernelIpc = true;
+#else // BINDER_WITH_KERNEL_IPC
+constexpr bool kEnableKernelIpc = false;
+#endif // BINDER_WITH_KERNEL_IPC
+
+} // namespace android
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index e4ac4b4..c6e4fb3 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -15,6 +15,7 @@
*/
#include "Debug.h"
+#include "BuildFlags.h"
#include <binder/ProcessState.h>
@@ -301,6 +302,11 @@
}
ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) {
+ if constexpr (!kEnableKernelIpc) {
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return 0;
+ }
+
sp<ProcessState> proc = ProcessState::selfOrNull();
if (proc.get() == nullptr) {
return 0;
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 5e22593..8ee6cb0 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 @@
// pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are
// a subset of event | POLLHUP | POLLERR | POLLNVAL.
+#endif
// POLLNVAL: invalid FD number, e.g. not opened.
if (pfd[0].revents & POLLNVAL) {
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a545d6c..5fbf290 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 @@
* 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 3c97dca..da58251 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 @@
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 @@
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 @@
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 @@
break;
}
- out << endl;
+ out << "\n";
return cmd;
}
@@ -400,14 +408,92 @@
// 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 @@
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 @@
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::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 @@
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 @@
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 @@
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 @@
#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 @@
mCallRestriction(mProcess->mCallRestriction) {
pthread_setspecific(gTLS, this);
clearCaller();
+ mHasExplicitIdentity = false;
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
@@ -908,8 +1015,10 @@
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 @@
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 @@
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 @@
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
+ logExtendedError();
}
return err;
@@ -1023,17 +1134,19 @@
}
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 fd2d868..2408307 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 @@
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 @@
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 @@
}
}
-#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 @@
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 @@
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 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 @@
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 8132d46..8fe1d2b 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -74,7 +74,7 @@
fd = memfd_create_region(name ? name : "MemoryHeapBase", size);
if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
- ((mFlags & MEMFD_ALLOW_SEALING) ? 0 : F_SEAL_SEAL);
+ ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name,
SEAL_FLAGS, strerror(errno));
@@ -85,12 +85,9 @@
}
return;
#else
- mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING);
+ mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG);
#endif
}
- if (mFlags & MEMFD_ALLOW_SEALING) {
- LOG_ALWAYS_FATAL("Invalid Flags. MEMFD_ALLOW_SEALING only valid with FORCE_MEMFD.");
- }
fd = ashmem_create_region(name ? name : "MemoryHeapBase", size);
ALOGE_IF(fd < 0, "MemoryHeapBase: error creating ashmem region: %s", strerror(errno));
if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
@@ -103,7 +100,7 @@
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
- if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) {
+ if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) {
LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor");
}
int open_flags = O_RDWR;
@@ -125,7 +122,7 @@
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
- if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) {
+ if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) {
LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor");
}
const size_t pagesize = getpagesize();
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
new file mode 100644
index 0000000..ce60e33
--- /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 0000000..fecae31
--- /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 f954e74..bb17683 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 58b0b35..0aca163 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 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 @@
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 @@
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 @@
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 @@
return finishFlattenBinder(binder);
}
+#ifdef BINDER_WITH_KERNEL_IPC
flat_binder_object obj;
int schedBits = 0;
@@ -255,13 +286,15 @@
if (status != OK) return status;
return finishFlattenBinder(binder);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
- if (isForRpc()) {
- LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
-
+ if (const auto* rpcFields = maybeRpcFields()) {
int32_t isPresent;
status_t status = readInt32(&isPresent);
if (status != OK) return status;
@@ -271,10 +304,14 @@
if (isPresent & 1) {
uint64_t addr;
if (status_t status = readUint64(&addr); status != OK) return status;
- if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder);
+ if (status_t status =
+ rpcFields->mSession->state()->onBinderEntering(rpcFields->mSession, addr,
+ &binder);
status != OK)
return status;
- if (status_t status = mSession->state()->flushExcessBinderRefs(mSession, addr, binder);
+ if (status_t status =
+ rpcFields->mSession->state()->flushExcessBinderRefs(rpcFields->mSession,
+ addr, binder);
status != OK)
return status;
}
@@ -282,6 +319,7 @@
return finishUnflattenBinder(binder, out);
}
+#ifdef BINDER_WITH_KERNEL_IPC
const flat_binder_object* flat = readObject(false);
if (flat) {
@@ -299,6 +337,10 @@
}
}
return BAD_TYPE;
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
// ---------------------------------------------------------------------------
@@ -333,6 +375,10 @@
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 @@
}
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 @@
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 @@
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 @@
err = NO_ERROR;
- if (numObjects > 0) {
- const sp<ProcessState> proc(ProcessState::self());
- // grow objects
- if (mObjectsCapacity < mObjectsSize + numObjects) {
- if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow
- if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow
- size_t newSize = ((mObjectsSize + numObjects)*3)/2;
- if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
- binder_size_t *objects =
- (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
- if (objects == (binder_size_t*)nullptr) {
- return NO_MEMORY;
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ auto* otherKernelFields = parcel->maybeKernelFields();
+ LOG_ALWAYS_FATAL_IF(otherKernelFields == nullptr);
+
+ const binder_size_t* objects = otherKernelFields->mObjects;
+ size_t size = otherKernelFields->mObjectsSize;
+ // Count objects in range
+ int firstIndex = -1, lastIndex = -2;
+ for (int i = 0; i < (int)size; i++) {
+ size_t off = objects[i];
+ if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) {
+ if (firstIndex == -1) {
+ firstIndex = i;
+ }
+ lastIndex = i;
}
- mObjects = objects;
- mObjectsCapacity = newSize;
+ }
+ int numObjects = lastIndex - firstIndex + 1;
+ if (numObjects > 0) {
+ const sp<ProcessState> proc(ProcessState::self());
+ // grow objects
+ if (kernelFields->mObjectsCapacity < kernelFields->mObjectsSize + numObjects) {
+ if ((size_t)numObjects > SIZE_MAX - kernelFields->mObjectsSize)
+ return NO_MEMORY; // overflow
+ if (kernelFields->mObjectsSize + numObjects > SIZE_MAX / 3)
+ return NO_MEMORY; // overflow
+ size_t newSize = ((kernelFields->mObjectsSize + numObjects) * 3) / 2;
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
+ binder_size_t* objects = (binder_size_t*)realloc(kernelFields->mObjects,
+ newSize * sizeof(binder_size_t));
+ if (objects == (binder_size_t*)nullptr) {
+ return NO_MEMORY;
+ }
+ kernelFields->mObjects = objects;
+ kernelFields->mObjectsCapacity = newSize;
+ }
+
+ // append and acquire objects
+ int idx = kernelFields->mObjectsSize;
+ for (int i = firstIndex; i <= lastIndex; i++) {
+ size_t off = objects[i] - offset + startPos;
+ kernelFields->mObjects[idx++] = off;
+ kernelFields->mObjectsSize++;
+
+ flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off);
+ acquire_object(proc, *flat, this);
+
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ // If this is a file descriptor, we need to dup it so the
+ // new Parcel now owns its own fd, and can declare that we
+ // officially know we have fds.
+ flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
+ flat->cookie = 1;
+ kernelFields->mHasFds = kernelFields->mFdsKnown = true;
+ if (!mAllowFds) {
+ err = FDS_NOT_ALLOWED;
+ }
+ }
+ }
+ }
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else {
+ auto* rpcFields = maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+ auto* otherRpcFields = parcel->maybeRpcFields();
+ if (otherRpcFields == nullptr) {
+ return BAD_TYPE;
+ }
+ if (rpcFields->mSession != otherRpcFields->mSession) {
+ return BAD_TYPE;
}
- // append and acquire objects
- int idx = mObjectsSize;
- for (int i = firstIndex; i <= lastIndex; i++) {
- size_t off = objects[i] - offset + startPos;
- mObjects[idx++] = off;
- mObjectsSize++;
+ const size_t savedDataPos = mDataPos;
+ base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; };
- flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData + off);
- acquire_object(proc, *flat, this);
+ rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size());
+ if (otherRpcFields->mFds != nullptr) {
+ if (rpcFields->mFds == nullptr) {
+ rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
+ }
+ rpcFields->mFds->reserve(otherRpcFields->mFds->size());
+ }
+ for (size_t i = 0; i < otherRpcFields->mObjectPositions.size(); i++) {
+ const binder_size_t objPos = otherRpcFields->mObjectPositions[i];
+ if (offset <= objPos && objPos < offset + len) {
+ size_t newDataPos = objPos - offset + startPos;
+ rpcFields->mObjectPositions.push_back(newDataPos);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- // If this is a file descriptor, we need to dup it so the
- // new Parcel now owns its own fd, and can declare that we
- // officially know we have fds.
- flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
- flat->cookie = 1;
- mHasFds = mFdsKnown = true;
+ mDataPos = newDataPos;
+ int32_t objectType;
+ if (status_t status = readInt32(&objectType); status != OK) {
+ return status;
+ }
+ if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ continue;
+ }
+
if (!mAllowFds) {
- err = FDS_NOT_ALLOWED;
+ return FDS_NOT_ALLOWED;
+ }
+
+ // Read FD, duplicate, and add to list.
+ int32_t fdIndex;
+ if (status_t status = readInt32(&fdIndex); status != OK) {
+ return status;
+ }
+ 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 @@
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 @@
}
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 @@
return BAD_VALUE;
}
*result = false;
- for (size_t i = 0; i < mObjectsSize; i++) {
- size_t pos = mObjects[i];
- if (pos < offset) continue;
- if (pos + sizeof(flat_binder_object) > offset + len) {
- if (mObjectsSorted) break;
- else continue;
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ size_t pos = kernelFields->mObjects[i];
+ if (pos < offset) continue;
+ if (pos + sizeof(flat_binder_object) > offset + len) {
+ if (kernelFields->mObjectsSorted) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + pos);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ *result = true;
+ break;
+ }
}
- const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos);
- if (flat->hdr.type == BINDER_TYPE_FD) {
- *result = true;
- break;
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (const auto* rpcFields = maybeRpcFields()) {
+ for (uint32_t pos : rpcFields->mObjectPositions) {
+ if (offset <= pos && pos < limit) {
+ const auto* type = reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ if (*type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
+ *result = true;
+ break;
+ }
+ }
}
}
return NO_ERROR;
@@ -654,23 +820,28 @@
LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
"format must be set before data is written OR on IPC data");
- LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
- mSession = session;
+ mVariantFields.emplace<RpcFields>(session);
}
bool Parcel::isForRpc() const {
- return mSession != nullptr;
+ return std::holds_alternative<RpcFields>(mVariantFields);
}
void Parcel::updateWorkSourceRequestHeaderPosition() const {
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return;
+ }
+
// Only update the request headers once. We only want to point
// to the first headers read/written.
- if (!mRequestHeaderPresent) {
- mWorkSourceRequestHeaderPosition = dataPosition();
- mRequestHeaderPresent = true;
+ if (!kernelFields->mRequestHeaderPresent) {
+ kernelFields->mWorkSourceRequestHeaderPosition = dataPosition();
+ kernelFields->mRequestHeaderPresent = true;
}
}
+#ifdef BINDER_WITH_KERNEL_IPC
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#elif defined(__ANDROID_RECOVERY__)
@@ -678,6 +849,7 @@
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
+#endif // BINDER_WITH_KERNEL_IPC
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
@@ -686,13 +858,18 @@
}
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
- if (CC_LIKELY(!isForRpc())) {
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid()
: IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
}
// currently the interface identification token is just its name as a string
@@ -701,12 +878,16 @@
bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
{
- if (!mRequestHeaderPresent) {
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return false;
+ }
+ if (!kernelFields->mRequestHeaderPresent) {
return false;
}
const size_t initialPosition = dataPosition();
- setDataPosition(mWorkSourceRequestHeaderPosition);
+ setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition);
status_t err = writeInt32(uid);
setDataPosition(initialPosition);
return err == NO_ERROR;
@@ -714,12 +895,16 @@
uid_t Parcel::readCallingWorkSourceUid() const
{
- if (!mRequestHeaderPresent) {
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields == nullptr) {
+ return false;
+ }
+ if (!kernelFields->mRequestHeaderPresent) {
return IPCThreadState::kUnsetWorkSource;
}
const size_t initialPosition = dataPosition();
- setDataPosition(mWorkSourceRequestHeaderPosition);
+ setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition);
uid_t uid = readInt32();
setDataPosition(initialPosition);
return uid;
@@ -740,7 +925,8 @@
size_t len,
IPCThreadState* threadState) const
{
- if (CC_LIKELY(!isForRpc())) {
+ if (auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
// StrictModePolicy.
int32_t strictPolicy = readInt32();
if (threadState == nullptr) {
@@ -766,6 +952,11 @@
header);
return false;
}
+#else // BINDER_WITH_KERNEL_IPC
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)threadState;
+ return false;
+#endif // BINDER_WITH_KERNEL_IPC
}
// Interface descriptor.
@@ -782,7 +973,15 @@
}
}
+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 @@
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 @@
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::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 @@
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::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 @@
// 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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
// 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 @@
// 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 @@
}
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 @@
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 @@
// 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 @@
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::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 @@
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 @@
}
free(mData);
}
- if (mObjects) free(mObjects);
+ auto* kernelFields = maybeKernelFields();
+ if (kernelFields && kernelFields->mObjects) free(kernelFields->mObjects);
}
}
@@ -2433,13 +2833,18 @@
ALOGV("restartWrite Setting data size of %p to %zu", this, mDataSize);
ALOGV("restartWrite Setting data pos of %p to %zu", this, mDataPos);
- free(mObjects);
- mObjects = nullptr;
- mObjectsSize = mObjectsCapacity = 0;
- mNextObjectHint = 0;
- mObjectsSorted = false;
- mHasFds = false;
- mFdsKnown = true;
+ if (auto* kernelFields = maybeKernelFields()) {
+ free(kernelFields->mObjects);
+ kernelFields->mObjects = nullptr;
+ kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = 0;
+ kernelFields->mNextObjectHint = 0;
+ kernelFields->mObjectsSorted = false;
+ kernelFields->mHasFds = false;
+ kernelFields->mFdsKnown = true;
+ } else if (auto* rpcFields = maybeRpcFields()) {
+ rpcFields->mObjectPositions.clear();
+ rpcFields->mFds.reset();
+ }
mAllowFds = true;
return NO_ERROR;
@@ -2453,17 +2858,27 @@
return BAD_VALUE;
}
+ auto* kernelFields = maybeKernelFields();
+ auto* rpcFields = maybeRpcFields();
+
// If shrinking, first adjust for any objects that appear
// after the new data size.
- size_t objectsSize = mObjectsSize;
+ size_t objectsSize =
+ kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size();
if (desired < mDataSize) {
if (desired == 0) {
objectsSize = 0;
} else {
- while (objectsSize > 0) {
- if (mObjects[objectsSize-1] < desired)
- break;
- objectsSize--;
+ if (kernelFields) {
+ while (objectsSize > 0) {
+ if (kernelFields->mObjects[objectsSize - 1] < desired) break;
+ objectsSize--;
+ }
+ } else {
+ while (objectsSize > 0) {
+ if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
+ objectsSize--;
+ }
}
}
}
@@ -2484,7 +2899,7 @@
}
binder_size_t* objects = nullptr;
- if (objectsSize) {
+ if (kernelFields && objectsSize) {
objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
if (!objects) {
free(data);
@@ -2495,20 +2910,32 @@
// 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 @@
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 @@
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 @@
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 @@
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::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 @@
}
return openAshmemSize;
}
+#endif // BINDER_WITH_KERNEL_IPC
// --- Parcel::Blob ---
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4a01d81..5f1f506 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 @@
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 @@
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 @@
// the thread handler is installed
if (gProcess) {
gProcess->mForked = true;
+
+ // "O_CLOFORK"
+ close(gProcess->mDriverFD);
+ gProcess->mDriverFD = -1;
}
gProcessMutex.unlock();
}
@@ -182,7 +202,6 @@
ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
"*startThreadPool when zero threads are requested.");
}
-
mThreadPoolStarted = true;
spawnPooledThread(true);
}
@@ -290,12 +309,17 @@
return &mHandleToObject.editItemAt(handle);
}
+// see b/166779391: cannot change the VNDK interface, so access like this
+extern sp<BBinder> the_context_object;
+
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
+ if (handle == 0 && the_context_object != nullptr) return the_context_object;
+
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
@@ -386,6 +410,9 @@
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = sp<PoolThread>::make(isMain);
t->run(name.string());
+ pthread_mutex_lock(&mThreadCountLock);
+ mKernelStartedThreads++;
+ pthread_mutex_unlock(&mThreadCountLock);
}
}
@@ -402,12 +429,27 @@
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 @@
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 @@
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 0000000..ef58ed3
--- /dev/null
+++ b/libs/binder/RecordedTransaction.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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/scopeguard.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.";
+ 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);
+ struct stat fileStat;
+ if (fstat(fd.get(), &fileStat) != 0) {
+ LOG(ERROR) << "Unable to get file information";
+ return std::nullopt;
+ }
+
+ off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
+ do {
+ if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
+ LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+ return std::nullopt;
+ }
+ transaction_checksum_t checksum = 0;
+ if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
+ LOG(ERROR) << "Failed to read chunk descriptor.";
+ return std::nullopt;
+ }
+
+ fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
+ off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
+ off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
+
+ if (chunk.dataSize > kMaxChunkDataSize) {
+ LOG(ERROR) << "Chunk data exceeds maximum size.";
+ return std::nullopt;
+ }
+
+ size_t chunkPayloadSize =
+ chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
+
+ if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
+ LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+ return std::nullopt;
+ }
+
+ if (PADDING8(chunkPayloadSize) != 0) {
+ LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
+ return std::nullopt;
+ }
+
+ size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
+ void* mappedMemory =
+ mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
+ auto mmap_guard = android::base::make_scope_guard(
+ [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
+
+ transaction_checksum_t* payloadMap =
+ reinterpret_cast<transaction_checksum_t*>(mappedMemory);
+ 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;
+ }
+
+ fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
+
+ 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.";
+ break;
+ }
+ } 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 ace5cd5..0d06e9e 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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
status_t status;
while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
- sockaddr_storage addr;
- socklen_t addrLen = sizeof(addr);
+ std::array<uint8_t, kRpcAddressSize> addr;
+ static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small");
+ socklen_t addrLen = addr.size();
- unique_fd clientFd(
- TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr),
- &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK)));
+ RpcTransportFd clientSocket;
+ if ((status = mAcceptFn(*this, &clientSocket)) != OK) {
+ if (status == DEAD_OBJECT)
+ break;
+ else
+ continue;
+ }
- LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address");
+ LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
- if (clientFd < 0) {
- ALOGE("Could not accept4 socket: %s", strerror(errno));
+ if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
+ &addrLen)) {
+ ALOGE("Could not getpeername socket: %s", strerror(errno));
continue;
}
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+ if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) {
+ ALOGE("Dropped client connection fd %d", clientSocket.fd.get());
+ continue;
+ }
{
- 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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
};
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 @@
}
}
- 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 @@
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 @@
// 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 @@
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 @@
}
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 d40778a..fbad0f7 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -20,8 +20,8 @@
#include <dlfcn.h>
#include <inttypes.h>
+#include <netinet/tcp.h>
#include <poll.h>
-#include <pthread.h>
#include <unistd.h>
#include <string_view>
@@ -34,21 +34,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 +63,14 @@
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 +80,30 @@
}
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());
- mMaxOutgoingThreads = threads;
+void RpcSession::setMaxOutgoingConnections(size_t connections) {
+ RpcMutexLockGuard _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mStartedSetup,
+ "Must set max outgoing threads before setting up connections");
+ mMaxOutgoingConnections = connections;
}
size_t RpcSession::getMaxOutgoingThreads() {
- std::lock_guard<std::mutex> _l(mMutex);
- return mMaxOutgoingThreads;
+ RpcMutexLockGuard _l(mMutex);
+ return mMaxOutgoingConnections;
}
-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 +112,9 @@
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 +125,58 @@
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 +192,9 @@
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 +203,11 @@
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 +223,8 @@
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 +249,7 @@
}
bool RpcSession::shutdownAndWait(bool wait) {
- std::unique_lock<std::mutex> _l(mMutex);
+ RpcMutexUniqueLock _l(mMutex);
LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Shutdown trigger not installed");
mShutdownTrigger->trigger();
@@ -221,6 +262,12 @@
}
_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 +302,7 @@
status_t RpcSession::readId() {
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
}
@@ -278,25 +325,27 @@
}
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 +372,7 @@
}
namespace {
-#ifdef __ANDROID_RECOVERY__
+#if !defined(__ANDROID__) || defined(__ANDROID_RECOVERY__)
class JavaThreadAttacher {};
#else
// RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If
@@ -360,10 +409,11 @@
"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 +453,8 @@
sp<RpcSession::EventListener> listener;
{
- std::lock_guard<std::mutex> _l(session->mMutex);
- auto it = session->mConnections.mThreads.find(std::this_thread::get_id());
+ RpcMutexLockGuard _l(session->mMutex);
+ auto it = session->mConnections.mThreads.find(rpc_this_thread::get_id());
LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end());
it->second.detach();
session->mConnections.mThreads.erase(it);
@@ -437,10 +487,17 @@
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 +521,9 @@
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 +541,7 @@
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
@@ -499,11 +559,11 @@
return status;
}
- size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
- ALOGI_IF(outgoingThreads != numThreadsAvailable,
+ size_t outgoingConnections = std::min(numThreadsAvailable, mMaxOutgoingConnections);
+ ALOGI_IF(outgoingConnections != numThreadsAvailable,
"Server hints client to start %zu outgoing threads, but client will only start %zu "
"because it is preconfigured to start at most %zu outgoing threads.",
- numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+ numThreadsAvailable, outgoingConnections, mMaxOutgoingConnections);
// TODO(b/189955605): we should add additional sessions dynamically
// instead of all at once - the other side should be responsible for setting
@@ -512,10 +572,10 @@
// any requests at all.
// we've already setup one client
- LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
- "incoming threads",
- outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
- for (size_t i = 0; i + 1 < outgoingThreads; i++) {
+ LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing connections (server max: "
+ "%zu) and %zu incoming threads",
+ outgoingConnections, numThreadsAvailable, mMaxIncomingThreads);
+ for (size_t i = 0; i + 1 < outgoingConnections; i++) {
if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
}
@@ -549,12 +609,26 @@
return -savedErrno;
}
- if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) {
+ int noDelay = 1;
+ int result =
+ setsockopt(serverFd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay));
+ if (result < 0) {
+ int savedErrno = errno;
+ ALOGE("Could not set TCP_NODELAY on %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return -savedErrno;
+ }
+ }
+
+ 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 +636,8 @@
}
// 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 +659,17 @@
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 +688,7 @@
RpcConnectionHeader header{
.version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
.options = 0,
+ .fileDescriptorTransportMode = static_cast<uint8_t>(mFileDescriptorTransportMode),
.sessionIdSize = static_cast<uint16_t>(sessionId.size()),
};
@@ -621,8 +697,8 @@
}
iovec headerIov{&header, sizeof(header)};
- auto sendHeaderStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, {});
+ auto sendHeaderStatus = server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1,
+ std::nullopt, nullptr);
if (sendHeaderStatus != OK) {
ALOGE("Could not write connection header to socket: %s",
statusToString(sendHeaderStatus).c_str());
@@ -633,7 +709,8 @@
iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())),
sessionId.size()};
auto sendSessionIdStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, {});
+ server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1,
+ std::nullopt, nullptr);
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
base::HexString(sessionId.data(), sessionId.size()).c_str(),
@@ -652,14 +729,14 @@
}
status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) {
- std::mutex mutex;
- std::condition_variable joinCv;
- std::unique_lock<std::mutex> lock(mutex);
- std::thread thread;
+ RpcMutex mutex;
+ RpcConditionVariable joinCv;
+ RpcMutexUniqueLock lock(mutex);
+ RpcMaybeThread thread;
sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this);
bool ownershipTransferred = false;
- thread = std::thread([&]() {
- std::unique_lock<std::mutex> threadLock(mutex);
+ thread = RpcMaybeThread([&]() {
+ RpcMutexUniqueLock threadLock(mutex);
std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport);
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
sp<RpcSession> session = thiz;
@@ -675,6 +752,7 @@
RpcSession::join(std::move(session), std::move(setupResult));
});
+ rpcJoinIfSingleThreaded(thread);
joinCv.wait(lock, [&] { return ownershipTransferred; });
LOG_ALWAYS_FATAL_IF(!ownershipTransferred);
return OK;
@@ -694,9 +772,9 @@
status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) {
sp<RpcConnection> connection = sp<RpcConnection>::make();
{
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
connection->rpcTransport = std::move(rpcTransport);
- connection->exclusiveTid = gettid();
+ connection->exclusiveTid = rpcGetThreadId();
mConnections.mOutgoing.push_back(connection);
}
@@ -706,10 +784,7 @@
mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this));
}
- {
- std::lock_guard<std::mutex> _l(mMutex);
- connection->exclusiveTid = std::nullopt;
- }
+ clearConnectionTid(connection);
return status;
}
@@ -722,6 +797,7 @@
LOG_ALWAYS_FATAL_IF(mEventListener != nullptr);
LOG_ALWAYS_FATAL_IF(eventListener == nullptr);
LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr);
+ LOG_ALWAYS_FATAL_IF(mCtx != nullptr);
mShutdownTrigger = FdTrigger::make();
if (mShutdownTrigger == nullptr) return false;
@@ -735,7 +811,7 @@
sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport) {
- std::lock_guard<std::mutex> _l(mMutex);
+ RpcMutexLockGuard _l(mMutex);
if (mConnections.mIncoming.size() >= mMaxIncomingThreads) {
ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)",
@@ -753,7 +829,7 @@
sp<RpcConnection> session = sp<RpcConnection>::make();
session->rpcTransport = std::move(rpcTransport);
- session->exclusiveTid = gettid();
+ session->exclusiveTid = rpcGetThreadId();
mConnections.mIncoming.push_back(session);
mConnections.mMaxIncoming = mConnections.mIncoming.size();
@@ -762,7 +838,7 @@
}
bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) {
- std::unique_lock<std::mutex> _l(mMutex);
+ RpcMutexUniqueLock _l(mMutex);
if (auto it =
std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection);
it != mConnections.mIncoming.end()) {
@@ -779,6 +855,15 @@
return false;
}
+void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) {
+ RpcMutexUniqueLock _l(mMutex);
+ connection->exclusiveTid = std::nullopt;
+ if (mConnections.mWaitingThreads > 0) {
+ _l.unlock();
+ mAvailableConnectionCv.notify_one();
+ }
+}
+
std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) {
return mCtx->getCertificate(format);
}
@@ -789,8 +874,8 @@
connection->mConnection = nullptr;
connection->mReentrant = false;
- pid_t tid = gettid();
- std::unique_lock<std::mutex> _l(session->mMutex);
+ uint64_t tid = rpcGetThreadId();
+ RpcMutexUniqueLock _l(session->mMutex);
session->mConnections.mWaitingThreads++;
while (true) {
@@ -860,7 +945,8 @@
(session->server()
? "This is a server session, so see RpcSession::setMaxIncomingThreads "
"for the corresponding client"
- : "This is a client session, so see RpcSession::setMaxOutgoingThreads "
+ : "This is a client session, so see "
+ "RpcSession::setMaxOutgoingConnections "
"for this client or RpcServer::setMaxThreads for the corresponding "
"server"));
return WOULD_BLOCK;
@@ -876,7 +962,7 @@
return OK;
}
-void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+void RpcSession::ExclusiveConnection::findConnection(uint64_t tid, sp<RpcConnection>* exclusive,
sp<RpcConnection>* available,
std::vector<sp<RpcConnection>>& sockets,
size_t socketsIndexHint) {
@@ -908,13 +994,28 @@
// 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 6d89064..ed3ce24 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 @@
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 @@
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 @@
// 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,107 @@
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);
+ return clear(RpcMutexUniqueLock(mNodeMutex));
+}
+void RpcState::clear(RpcMutexUniqueLock nodeLock) {
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
+ nodeLock.unlock();
+ 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)";
- }
- } else {
- desc = "(local binder)";
- }
- } else {
- desc = "(null)";
- }
-
- ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s",
- node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc);
+ 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 = "(binder proxy)";
+ }
+ } else {
+ desc = "(local binder)";
+ }
+ } else {
+ desc = "(not promotable)";
+ }
+
+ 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 +350,11 @@
mData.reset(new (std::nothrow) uint8_t[size]);
}
-status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
- const std::function<status_t()>& altPoll) {
+status_t RpcState::rpcSend(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
for (int i = 0; i < niovs; i++) {
LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
what, i + 1, niovs, connection->rpcTransport.get(),
@@ -320,7 +363,8 @@
if (status_t status =
connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
- iovs, niovs, altPoll);
+ iovs, niovs, altPoll,
+ ancillaryFds);
status != OK) {
LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -331,11 +375,14 @@
return OK;
}
-status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) {
+status_t RpcState::rpcRec(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
- iovs, niovs, {});
+ iovs, niovs, std::nullopt,
+ ancillaryFds);
status != OK) {
LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -355,7 +402,7 @@
const sp<RpcSession>& session, uint32_t* version) {
RpcNewSessionResponse response;
iovec iov{&response, sizeof(response)};
- if (status_t status = rpcRec(connection, session, "new session response", &iov, 1);
+ if (status_t status = rpcRec(connection, session, "new session response", &iov, 1, nullptr);
status != OK) {
return status;
}
@@ -369,14 +416,15 @@
.msg = RPC_CONNECTION_INIT_OKAY,
};
iovec iov{&init, sizeof(init)};
- return rpcSend(connection, session, "connection init", &iov, 1);
+ return rpcSend(connection, session, "connection init", &iov, 1, std::nullopt);
}
status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session) {
RpcOutgoingConnectionInit init;
iovec iov{&init, sizeof(init)};
- if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK)
+ if (status_t status = rpcRec(connection, session, "connection init", &iov, 1, nullptr);
+ status != OK)
return status;
static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY));
@@ -448,20 +496,12 @@
status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection,
const sp<IBinder>& binder, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply, uint32_t flags) {
- if (!data.isForRpc()) {
- ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code "
- "%" PRIu32,
- binder.get(), code);
- return BAD_TYPE;
+ std::string errorMsg;
+ if (status_t status = validateParcel(session, data, &errorMsg); status != OK) {
+ ALOGE("Refusing to send RPC on binder %p code %" PRIu32 ": Parcel %p failed validation: %s",
+ binder.get(), code, &data, errorMsg.c_str());
+ return status;
}
-
- if (data.objectsCount() != 0) {
- ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p "
- "code %" PRIu32,
- &data, binder.get(), code);
- return BAD_TYPE;
- }
-
uint64_t address;
if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status;
@@ -477,7 +517,7 @@
uint64_t asyncNumber = 0;
if (address != 0) {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(address);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -493,14 +533,21 @@
}
}
- LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) -
- sizeof(RpcWireTransaction) <
- data.dataSize(),
- "Too much data %zu", data.dataSize());
+ auto* rpcFields = data.maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+ Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(),
+ rpcFields->mObjectPositions.size()};
+
+ uint32_t bodySize;
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(),
+ &bodySize) ||
+ __builtin_add_overflow(objectTableSpan.byteSize(), bodySize,
+ &bodySize),
+ "Too much data %zu", data.dataSize());
RpcWireHeader command{
.command = RPC_COMMAND_TRANSACT,
- .bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()),
+ .bodySize = bodySize,
};
RpcWireTransaction transaction{
@@ -508,42 +555,46 @@
.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;
- constexpr size_t kWaitLogUs = 10000;
- size_t waitUs = 0;
-
// 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
+ constexpr size_t kWaitMaxUs = 1000000;
+ constexpr size_t kWaitLogUs = 10000;
+ size_t waitUs = 0;
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.
+ // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
+ // errors here, then we may need to undo the binder-sent counts for the transaction as
+ // well as for the binder objects in the Parcel
return status;
}
@@ -560,54 +611,91 @@
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;
+
+ // Reset to avoid spurious use-after-move warning from clang-tidy.
+ ancillaryFds = decltype(ancillaryFds)();
}
- CommandData data(command.bodySize);
- if (!data.valid()) return NO_MEMORY;
+ const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value());
- iovec iov{data.data(), command.bodySize};
- if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK)
- return status;
-
- if (command.bodySize < sizeof(RpcWireReply)) {
+ if (command.bodySize < rpcReplyWireSize) {
ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!",
sizeof(RpcWireReply), command.bodySize);
(void)session->shutdownAndWait(false);
return BAD_VALUE;
}
- RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
- if (rpcReply->status != OK) return rpcReply->status;
+
+ RpcWireReply rpcReply;
+ memset(&rpcReply, 0, sizeof(RpcWireReply)); // zero because of potential short read
+
+ CommandData data(command.bodySize - rpcReplyWireSize);
+ if (!data.valid()) return NO_MEMORY;
+
+ iovec iovs[]{
+ {&rpcReply, rpcReplyWireSize},
+ {data.data(), data.size()},
+ };
+ if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr);
+ status != OK)
+ return status;
+
+ if (rpcReply.status != OK) return rpcReply.status;
+
+ Span<const uint8_t> parcelSpan = {data.data(), data.size()};
+ Span<const uint32_t> objectTableSpan;
+ if (session->getProtocolVersion().value() >=
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
+ 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();
- reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
- nullptr, 0, cleanup_reply_data);
-
- reply->markForRpc(session);
-
- return OK;
+ return reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size,
+ objectTableSpan.data, objectTableSpan.size,
+ std::move(ancillaryFds), cleanup_reply_data);
}
status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection,
@@ -618,7 +706,7 @@
};
{
- std::lock_guard<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(addr);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -634,8 +722,9 @@
body.amount = it->second.timesRecd - target;
it->second.timesRecd = target;
- LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it),
+ LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(session, std::move(_l), it),
"Bad state. RpcState shouldn't own received binder");
+ // LOCK ALREADY RELEASED
}
RpcWireHeader cmd = {
@@ -643,31 +732,32 @@
.bodySize = sizeof(RpcDecStrong),
};
iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}};
- return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs));
+ return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt);
}
status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
RpcWireHeader command;
iovec iov{&command, sizeof(command)};
- if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1);
+ if (status_t status =
+ rpcRec(connection, session, "command header (for server)", &iov, 1,
+ enableAncillaryFds(session->getFileDescriptorTransportMode()) ? &ancillaryFds
+ : nullptr);
status != OK)
return status;
- return processCommand(connection, session, command, type);
+ return processCommand(connection, session, command, type, std::move(ancillaryFds));
}
status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
- uint8_t buf;
while (true) {
- size_t num_bytes;
- status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes);
+ status_t status = connection->rpcTransport->pollRead();
if (status == WOULD_BLOCK) break;
if (status != OK) return status;
- if (!num_bytes) break;
status = getAndExecuteCommand(connection, session, type);
if (status != OK) return status;
@@ -675,28 +765,33 @@
return OK;
}
-status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcWireHeader& command,
- CommandType type) {
+status_t RpcState::processCommand(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command, CommandType type,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
+#ifdef BINDER_WITH_KERNEL_IPC
IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
IPCThreadState::SpGuard spGuard{
.address = __builtin_frame_address(0),
- .context = "processing binder RPC command",
+ .context = "processing binder RPC command (where RpcServer::setPerSessionRootObject is "
+ "used to distinguish callers)",
};
const IPCThreadState::SpGuard* origGuard;
if (kernelBinderState != nullptr) {
origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
}
- ScopeGuard guardUnguard = [&]() {
+
+ base::ScopeGuard guardUnguard = [&]() {
if (kernelBinderState != nullptr) {
kernelBinderState->restoreGetCallingSpGuard(origGuard);
}
};
+#endif // BINDER_WITH_KERNEL_IPC
switch (command.command) {
case RPC_COMMAND_TRANSACT:
if (type != CommandType::ANY) return BAD_TYPE;
- return processTransact(connection, session, command);
+ return processTransact(connection, session, command, std::move(ancillaryFds));
case RPC_COMMAND_DEC_STRONG:
return processDecStrong(connection, session, command);
}
@@ -710,8 +805,10 @@
(void)session->shutdownAndWait(false);
return DEAD_OBJECT;
}
-status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcWireHeader& command) {
+status_t RpcState::processTransact(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
CommandData transactionData(command.bodySize);
@@ -719,24 +816,26 @@
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 +879,7 @@
(void)session->shutdownAndWait(false);
replyStatus = BAD_VALUE;
} else if (oneway) {
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it->second.binder.promote() != target) {
ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr);
@@ -791,6 +890,7 @@
it->second.asyncTodo.push(BinderNode::AsyncTodo{
.ref = target,
.data = std::move(transactionData),
+ .ancillaryFds = std::move(ancillaryFds),
.asyncNumber = transaction->asyncNumber,
});
@@ -824,54 +924,85 @@
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;
+ connection->allowNested = origAllowNested;
+ } else {
+ LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
+
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+ replyStatus = reply.writeInt32(session->getMaxIncomingThreads());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+ // for client connections, this should always report the value
+ // originally returned from the server, so this is asserting
+ // that it exists
+ replyStatus = reply.writeByteVector(session->mId);
+ break;
+ }
+ default: {
+ sp<RpcServer> server = session->server();
+ if (server) {
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+ sp<IBinder> root = session->mSessionSpecificRootObject
+ ?: server->getRootObject();
+ replyStatus = reply.writeStrongBinder(root);
+ break;
+ }
+ default: {
+ replyStatus = UNKNOWN_TRANSACTION;
+ }
}
- default: {
- replyStatus = UNKNOWN_TRANSACTION;
- }
+ } else {
+ ALOGE("Special command sent, but no server object attached.");
}
- } else {
- ALOGE("Special command sent, but no server object attached.");
}
}
}
@@ -897,7 +1028,7 @@
// downside: asynchronous transactions may drown out synchronous
// transactions.
{
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
// last refcount dropped after this transaction happened
if (it == mNodeForAddress.end()) return OK;
@@ -908,8 +1039,8 @@
return DEAD_OBJECT;
}
- if (it->second.asyncTodo.size() == 0) return OK;
- if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+ if (it->second.asyncTodo.size() != 0 &&
+ it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
it->second.asyncNumber, addr);
@@ -920,6 +1051,7 @@
// 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 +1075,69 @@
replyStatus = flushExcessBinderRefs(session, addr, target);
}
- LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) -
- sizeof(RpcWireReply) <
- reply.dataSize(),
- "Too much data for reply %zu", reply.dataSize());
+ std::string errorMsg;
+ if (status_t status = validateParcel(session, reply, &errorMsg); status != OK) {
+ ALOGE("Reply Parcel failed validation: %s", errorMsg.c_str());
+ // Forward the error to the client of the transaction.
+ reply.freeData();
+ reply.markForRpc(session);
+ replyStatus = status;
+ }
+ auto* rpcFields = reply.maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rpcFields == nullptr);
+
+ const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value());
+
+ Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(),
+ rpcFields->mObjectPositions.size()};
+
+ uint32_t bodySize;
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) ||
+ __builtin_add_overflow(objectTableSpan.byteSize(), bodySize,
+ &bodySize),
+ "Too much data for reply %zu", reply.dataSize());
RpcWireHeader cmdReply{
.command = RPC_COMMAND_REPLY,
- .bodySize = static_cast<uint32_t>(sizeof(RpcWireReply) + reply.dataSize()),
+ .bodySize = bodySize,
};
RpcWireReply rpcReply{
.status = replyStatus,
+ // NOTE: Not necessarily written to socket depending on session
+ // version.
+ // NOTE: bodySize didn't overflow => this cast is safe
+ .parcelDataSize = static_cast<uint32_t>(reply.dataSize()),
+ .reserved = {0, 0, 0},
};
-
iovec iovs[]{
{&cmdReply, sizeof(RpcWireHeader)},
- {&rpcReply, sizeof(RpcWireReply)},
+ {&rpcReply, rpcReplyWireSize},
{const_cast<uint8_t*>(reply.data()), reply.dataSize()},
+ objectTableSpan.toIovec(),
};
- return rpcSend(connection, session, "reply", iovs, arraysize(iovs));
+ return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt,
+ rpcFields->mFds.get());
}
status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
- CommandData commandData(command.bodySize);
- if (!commandData.valid()) {
- return NO_MEMORY;
- }
- iovec iov{commandData.data(), commandData.size()};
- if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK)
- return status;
-
if (command.bodySize != sizeof(RpcDecStrong)) {
ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcDecStrong. Terminating!",
sizeof(RpcDecStrong), command.bodySize);
(void)session->shutdownAndWait(false);
return BAD_VALUE;
}
- RpcDecStrong* body = reinterpret_cast<RpcDecStrong*>(commandData.data());
- uint64_t addr = RpcWireAddress::toRaw(body->address);
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ RpcDecStrong body;
+ iovec iov{&body, sizeof(RpcDecStrong)};
+ if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1, nullptr);
+ status != OK)
+ return status;
+
+ uint64_t addr = RpcWireAddress::toRaw(body.address);
+ RpcMutexUniqueLock _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr);
@@ -1002,27 +1154,88 @@
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;
- sp<IBinder> tempHold = tryEraseNode(it);
- _l.unlock();
+ it->second.timesSent -= body.amount;
+ sp<IBinder> tempHold = tryEraseNode(session, std::move(_l), it);
+ // LOCK ALREADY RELEASED
tempHold = nullptr; // destructor may make binder calls on this session
return OK;
}
-sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
+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(const sp<RpcSession>& session, RpcMutexUniqueLock nodeLock,
+ std::map<uint64_t, BinderNode>::iterator& it) {
+ bool shouldShutdown = false;
+
sp<IBinder> ref;
if (it->second.timesSent == 0) {
@@ -1032,9 +1245,27 @@
LOG_ALWAYS_FATAL_IF(!it->second.asyncTodo.empty(),
"Can't delete binder w/ pending async transactions");
mNodeForAddress.erase(it);
+
+ if (mNodeForAddress.size() == 0) {
+ shouldShutdown = true;
+ }
}
}
+ // If we shutdown, prevent RpcState from being re-used. This prevents another
+ // thread from getting the root object again.
+ if (shouldShutdown) {
+ clear(std::move(nodeLock));
+ } else {
+ nodeLock.unlock(); // explicit
+ }
+ // LOCK IS RELEASED
+
+ if (shouldShutdown) {
+ ALOGI("RpcState has no binders left, so triggering shutdown...");
+ (void)session->shutdownAndWait(false);
+ }
+
return ref;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index f4a0894..0e23ea7 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 @@
*/
[[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();
@@ -162,6 +168,7 @@
void clear();
private:
+ void clear(RpcMutexUniqueLock nodeLock);
void dumpLocked();
// Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
@@ -178,28 +185,39 @@
size_t mSize;
};
- [[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs,
- int niovs, const std::function<status_t()>& altPoll = nullptr);
- [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, iovec* iovs,
- int niovs);
+ [[nodiscard]] status_t rpcSend(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds =
+ nullptr);
+ [[nodiscard]] status_t rpcRec(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr);
[[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply);
- [[nodiscard]] status_t processCommand(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- const RpcWireHeader& command, CommandType type);
- [[nodiscard]] status_t processTransact(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- const RpcWireHeader& command);
- [[nodiscard]] status_t processTransactInternal(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session,
- CommandData transactionData);
+ [[nodiscard]] status_t processCommand(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command, CommandType type,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ [[nodiscard]] status_t processTransact(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ const RpcWireHeader& command,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ [[nodiscard]] status_t processTransactInternal(
+ const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
+ CommandData transactionData,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
[[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session,
const RpcWireHeader& command);
+ // Whether `parcel` is compatible with `session`.
+ [[nodiscard]] static status_t validateParcel(const sp<RpcSession>& session,
+ const Parcel& parcel, std::string* errorMsg);
+
struct BinderNode {
// Two cases:
// A - local binder we are serving
@@ -233,6 +251,7 @@
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,18 +265,29 @@
//
// (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
- // happens, and there is a strong reference to the binder kept by
- // binderNode, this returns that strong reference, so that it can be
- // dropped after any locks are removed.
- sp<IBinder> tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it);
+ // Checks if there is any reference left to a node and erases it. If this
+ // is the last node, shuts down the session.
+ //
+ // Node lock is passed here for convenience, so that we can release it
+ // and terminate the session, but we could leave it up to the caller
+ // by returning a continuation if we needed to erase multiple specific
+ // nodes. It may be tempting to allow the client to keep on holding the
+ // lock and instead just return whether or not we should shutdown, but
+ // this introduces the posssibility that another thread calls
+ // getRootBinder and thinks it is valid, rather than immediately getting
+ // an error.
+ sp<IBinder> tryEraseNode(const sp<RpcSession>& session, RpcMutexUniqueLock nodeLock,
+ std::map<uint64_t, BinderNode>::iterator& it);
+
// true - success
// false - session shutdown, halt
[[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
- std::mutex mNodeMutex;
+ RpcMutex mNodeMutex;
bool mTerminated = false;
uint32_t mNextId = 0;
// binders known by both sides of a session
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 7cfc780..cd067bf 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 @@
// 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 0000000..d5a6da2
--- /dev/null
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 adjustStatus(-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));
+ };
+
+ status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn,
+ "tipc_send", POLLOUT, altPoll);
+ return adjustStatus(status);
+ }
+
+ 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;
+ };
+
+ status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, readFn, "read",
+ POLLIN, altPoll);
+ return adjustStatus(status);
+ }
+
+ bool isWaiting() override { return mSocket.isInPollingState(); }
+
+private:
+ status_t adjustStatus(status_t status) {
+ if (status == -ENOTCONN) {
+ // TIPC returns ENOTCONN on disconnect, but that's basically
+ // the same as DEAD_OBJECT and the latter is the common libbinder
+ // error code for dead connections
+ return DEAD_OBJECT;
+ }
+
+ return status;
+ }
+
+ 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 adjustStatus(-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 bc68c37..3e98ecc 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -181,9 +181,10 @@
// |sslError| should be from Ssl::getError().
// If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
// return error. Also return error if |fdTrigger| is triggered before or during poll().
- status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
- const char* fnString, int additionalEvent,
- const std::function<status_t()>& altPoll) {
+ status_t pollForSslError(
+ 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 0000000..32f0db8
--- /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 0000000..3b53b05
--- /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 171550e..ff1b01a 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -45,7 +45,8 @@
struct RpcConnectionHeader {
uint32_t version; // maximum supported by caller
uint8_t options;
- uint8_t reservered[9];
+ uint8_t fileDescriptorTransportMode;
+ uint8_t reservered[8];
// Follows is sessionIdSize bytes.
// if size is 0, this is requesting a new session.
uint16_t sessionIdSize;
@@ -108,6 +109,10 @@
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
+//
+// When file descriptors are included in out-of-band data (e.g. in unix domain
+// sockets), they are always paired with the RpcWireHeader bytes of the
+// transaction or reply the file descriptors belong to.
struct RpcWireHeader {
uint32_t command; // RPC_COMMAND_*
@@ -131,7 +136,10 @@
uint64_t asyncNumber;
- uint32_t reserved[4];
+ // The size of the Parcel data directly following RpcWireTransaction.
+ uint32_t parcelDataSize;
+
+ uint32_t reserved[3];
uint8_t data[];
};
@@ -139,9 +147,23 @@
struct RpcWireReply {
int32_t status; // transact return
- uint8_t data[];
+
+ // -- Fields below only transmitted starting at protocol version 1 --
+
+ // The size of the Parcel data directly following RpcWireReply.
+ uint32_t parcelDataSize;
+
+ uint32_t reserved[3];
+
+ // Byte size of RpcWireReply in the wire protocol.
+ static size_t wireSize(uint32_t protocolVersion) {
+ if (protocolVersion == 0) {
+ return sizeof(int32_t);
+ }
+ return sizeof(RpcWireReply);
+ }
};
-static_assert(sizeof(RpcWireReply) == 4);
+static_assert(sizeof(RpcWireReply) == 20);
#pragma clang diagnostic pop
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 194254a..2b67f03 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -159,8 +159,8 @@
LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
auto rpcSession = RpcSession::make();
- if (options.maxOutgoingThreads.has_value()) {
- rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+ if (options.maxOutgoingConnections.has_value()) {
+ rpcSession->setMaxOutgoingConnections(*options.maxOutgoingConnections);
}
if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 83b97d0..dba6587 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -139,6 +139,9 @@
mMessage = String8(message.value_or(String16()));
// Skip over the remote stack trace data
+ const size_t remote_start = parcel.dataPosition();
+ // Get available size before reading more
+ const size_t remote_avail = parcel.dataAvail();
int32_t remote_stack_trace_header_size;
status = parcel.readInt32(&remote_stack_trace_header_size);
if (status != OK) {
@@ -146,13 +149,16 @@
return status;
}
if (remote_stack_trace_header_size < 0 ||
- static_cast<size_t>(remote_stack_trace_header_size) > parcel.dataAvail()) {
+ static_cast<size_t>(remote_stack_trace_header_size) > remote_avail) {
android_errorWriteLog(0x534e4554, "132650049");
setFromStatusT(UNKNOWN_ERROR);
return UNKNOWN_ERROR;
}
- parcel.setDataPosition(parcel.dataPosition() + remote_stack_trace_header_size);
+
+ if (remote_stack_trace_header_size != 0) {
+ parcel.setDataPosition(remote_start + remote_stack_trace_header_size);
+ }
if (mException == EX_SERVICE_SPECIFIC) {
status = parcel.readInt32(&mErrorCode);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index ebb0d27..41707d4 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"
}
]
},
@@ -82,6 +79,56 @@
},
{
"name": "rustBinderSerializationTest"
+ },
+ {
+ "name": "libbinder_ndk_bindgen_test"
+ },
+ {
+ "name": "libbinder_rpc_unstable_bindgen_test"
+ }
+ ],
+ "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"
+ }
+ ],
+ "imports": [
+ {
+ "path": "packages/modules/Virtualization"
}
]
}
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index a0ade50..5dd1f90 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -39,11 +39,10 @@
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 @@
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 0000000..1ebfa1a
--- /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 d2a5be1..0314b0f 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -18,24 +18,10 @@
#include <string.h>
-using android::base::ErrnoError;
-using android::base::Result;
-
namespace android {
void zeroMemory(uint8_t* data, size_t size) {
memset(data, 0, size);
}
-Result<void> setNonBlocking(android::base::borrowed_fd fd) {
- int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
- if (flags == -1) {
- return ErrnoError() << "Could not get flags for fd";
- }
- if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) {
- return ErrnoError() << "Could not set non-blocking flag for fd";
- }
- return {};
-}
-
} // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index ff2fad8..e04199c 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 @@
// 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 5880c0a..0fb1615 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -114,6 +114,12 @@
@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 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
#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 46223bb..d960a0b 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -59,6 +59,8 @@
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 @@
[[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 @@
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 19ad5e6..5496d61 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 @@
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 @@
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 @@
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 @@
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 0000000..8b3fc1c
--- /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 43fc5ff..e75d548 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -56,6 +56,8 @@
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 @@
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 7067830..9f7e2c8 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -72,9 +72,9 @@
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual const String16& getInterfaceDescriptor() const;
+ typedef INTERFACE BaseInterface;
protected:
- typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
@@ -85,9 +85,9 @@
{
public:
explicit BpInterface(const sp<IBinder>& remote);
+ typedef INTERFACE BaseInterface;
protected:
- typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index bf02099..d261c21 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -28,6 +28,10 @@
// ---------------------------------------------------------------------------
namespace android {
+/**
+ * Kernel binder thread state. All operations here refer to kernel binder. This
+ * object is allocated per-thread.
+ */
class IPCThreadState
{
public:
@@ -135,12 +139,15 @@
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 @@
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 @@
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 413c97f..55167a7 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -67,7 +67,8 @@
* 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 @@
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 @@
{
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;
@@ -214,12 +224,12 @@
// }
// Resources are cleaned up when the object is destroyed.
//
-// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
-// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
-// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
-// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+// For each returned binder object, at most |maxOutgoingConnections| outgoing connections are
+// instantiated, depending on how many the service on the device is configured with.
+// Hence, only |maxOutgoingConnections| calls can be made simultaneously.
+// See also RpcSession::setMaxOutgoingConnections.
struct RpcDelegateServiceManagerOptions {
- std::optional<size_t> maxOutgoingThreads;
+ std::optional<size_t> maxOutgoingConnections;
};
sp<IServiceManager> createRpcDelegateServiceManager(
const RpcDelegateServiceManagerOptions& options);
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 15dd28f..c7177bd 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -26,9 +26,10 @@
// ---------------------------------------------------------------------------
-class MemoryHeapBase : public virtual BnMemoryHeap
+class MemoryHeapBase : public BnMemoryHeap
{
public:
+ static constexpr auto MEMFD_ALLOW_SEALING_FLAG = 0x00000800;
enum {
READ_ONLY = IMemoryHeap::READ_ONLY,
// memory won't be mapped locally, but will be mapped in the remote
@@ -48,7 +49,7 @@
// Clients of shared files can seal at anytime via syscall, leading to
// TOC/TOU issues if additional seals prevent access from the creating
// process. Alternatively, seccomp fcntl().
- MEMFD_ALLOW_SEALING = 0x00000800
+ MEMFD_ALLOW_SEALING = FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG
};
/*
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index e2b2c51..162cd40 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
uint8_t* mData;
size_t mDataSize;
size_t mDataCapacity;
- mutable size_t mDataPos;
- binder_size_t* mObjects;
- size_t mObjectsSize;
- size_t mObjectsCapacity;
- mutable size_t mNextObjectHint;
- mutable bool mObjectsSorted;
+ mutable size_t mDataPos;
- mutable bool mRequestHeaderPresent;
+ // Fields only needed when parcelling for "kernel Binder".
+ struct KernelFields {
+ binder_size_t* mObjects = nullptr;
+ size_t mObjectsSize = 0;
+ size_t mObjectsCapacity = 0;
+ mutable size_t mNextObjectHint = 0;
- mutable size_t mWorkSourceRequestHeaderPosition;
+ mutable size_t mWorkSourceRequestHeaderPosition = 0;
+ mutable bool mRequestHeaderPresent = false;
- mutable bool mFdsKnown;
- mutable bool mHasFds;
+ mutable bool mObjectsSorted = false;
+ mutable bool mFdsKnown = true;
+ mutable bool mHasFds = false;
+ };
+ // Fields only needed when parcelling for RPC Binder.
+ struct RpcFields {
+ RpcFields(const sp<RpcSession>& session);
+
+ // Should always be non-null.
+ const sp<RpcSession> mSession;
+
+ enum ObjectType : int32_t {
+ TYPE_BINDER_NULL = 0,
+ TYPE_BINDER = 1,
+ // FD to be passed via native transport (Trusty IPC or UNIX domain socket).
+ TYPE_NATIVE_FILE_DESCRIPTOR = 2,
+ };
+
+ // Sorted.
+ std::vector<uint32_t> mObjectPositions;
+
+ // File descriptors referenced by the parcel data. Should be indexed
+ // using the offsets in the parcel data. Don't assume the list is in the
+ // same order as `mObjectPositions`.
+ //
+ // Boxed to save space. Lazy allocated.
+ std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds;
+ };
+ std::variant<KernelFields, RpcFields> mVariantFields;
+
+ // Pointer to KernelFields in mVariantFields if present.
+ KernelFields* maybeKernelFields() { return std::get_if<KernelFields>(&mVariantFields); }
+ const KernelFields* maybeKernelFields() const {
+ return std::get_if<KernelFields>(&mVariantFields);
+ }
+ // Pointer to RpcFields in mVariantFields if present.
+ RpcFields* maybeRpcFields() { return std::get_if<RpcFields>(&mVariantFields); }
+ const RpcFields* maybeRpcFields() const { return std::get_if<RpcFields>(&mVariantFields); }
+
bool mAllowFds;
// if this parcelable is involved in a secure transaction, force the
// 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 @@
// ---------------------------------------------------------------------------
-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 9896fd7..08d8e43 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -42,6 +42,7 @@
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 88790a8..40fd30a 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -111,6 +111,11 @@
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 675585e..ce578e3 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -29,11 +29,17 @@
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 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
+ // For main functions - dangerous for libraries to use
void startThreadPool();
bool becomeContextManager();
@@ -53,10 +60,17 @@
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 @@
void setCallRestriction(CallRestriction restriction);
/**
- * Get the max number of threads that the kernel can start.
- *
- * Note: this is the lower bound. Additional threads may be started.
+ * Get the max number of threads that have joined the thread pool.
+ * This includes kernel started threads, user joined threads and polling
+ * threads if used.
*/
- size_t getThreadPoolMaxThreadCount() const;
+ size_t getThreadPoolMaxTotalThreadCount() const;
+
+ /**
+ * Check to see if the thread pool has started.
+ */
+ 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 @@
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 0000000..eb765fe
--- /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 6b31812..1001b64 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 @@
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 @@
[[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 setupVsockServer(unsigned int port);
+ [[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 bindCid, unsigned int port);
/**
* Creates an RPC server at the current port using IPv4.
@@ -96,7 +119,10 @@
[[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
/**
- * This must be called before adding a client session.
+ * This must be called before adding a client session. This corresponds
+ * to the number of incoming connections to RpcSession objects in the
+ * server, which will correspond to the number of outgoing connections
+ * in client RpcSession objects.
*
* If this is not specified, this will be a single-threaded server.
*
@@ -114,6 +140,15 @@
void setProtocolVersion(uint32_t version);
/**
+ * Set the supported transports for sending and receiving file descriptors.
+ *
+ * Clients will propose a mode when connecting. If the mode is not in the
+ * provided list, the connection will be rejected.
+ */
+ void setSupportedFileDescriptorTransportModes(
+ const std::vector<RpcSession::FileDescriptorTransportMode>& modes);
+
+ /**
* The root object can be retrieved by any client, without any
* authentication. TODO(b/183988761)
*
@@ -125,12 +160,30 @@
*/
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 +221,56 @@
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 a579442..a323feb 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,17 +39,24 @@
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
* and a server. Multiple connections are needed for multiple parallel "binder"
* calls which may also have nested calls.
+ *
+ * Once a binder exists in the session, if all references to all binders are dropped,
+ * the session shuts down.
*/
class RpcSession final : public virtual RefBase {
public:
- static constexpr size_t kDefaultMaxOutgoingThreads = 10;
-
// Create an RpcSession with default configuration (raw sockets).
static sp<RpcSession> make();
@@ -59,26 +68,30 @@
/**
* Set the maximum number of incoming threads allowed to be made (for things like callbacks).
* By default, this is 0. This must be called before setting up this connection as a client.
- * Server sessions will inherits this value from RpcServer.
+ * Server sessions will inherits this value from RpcServer. Each thread will serve a
+ * connection to the remote RpcSession.
*
* If this is called, 'shutdown' on this session must also be called.
* Otherwise, a threadpool will leak.
*
- * TODO(b/189955605): start these dynamically
+ * TODO(b/189955605): start these lazily - currently all are started
*/
void setMaxIncomingThreads(size_t threads);
size_t getMaxIncomingThreads();
/**
- * Set the maximum number of outgoing threads allowed to be made.
- * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
- * connection as a client.
+ * Set the maximum number of outgoing connections allowed to be made.
+ * By default, this is |kDefaultMaxOutgoingConnections|. This must be called before setting up
+ * this connection as a client.
*
- * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
- * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
- * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+ * For an RpcSession client, if you are connecting to a server which starts N threads,
+ * then this must be set to >= N. If you set the maximum number of outgoing connections
+ * to 1, but the server requests 10, then it would be considered an error. If you set a
+ * maximum number of connections to 10, and the server requests 1, then only 1 will be
+ * created. This API is used to limit the amount of resources a server can request you
+ * create.
*/
- void setMaxOutgoingThreads(size_t threads);
+ void setMaxOutgoingConnections(size_t connections);
size_t getMaxOutgoingThreads();
/**
@@ -88,6 +101,20 @@
[[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 +122,11 @@
[[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 +201,11 @@
*/
[[nodiscard]] status_t sendDecStrong(const BpBinder* binder);
+ /**
+ * Whether any requests are currently being processed.
+ */
+ bool hasActiveRequests();
+
~RpcSession();
/**
@@ -183,9 +220,16 @@
private:
friend sp<RpcSession>;
friend RpcServer;
+ friend RpcServerTrusty;
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
+ static constexpr size_t kDefaultMaxOutgoingConnections = 10;
+
+ // 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 +243,11 @@
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 +256,7 @@
// whether this or another thread is currently using this fd to make
// or receive transactions.
- std::optional<pid_t> exclusiveTid;
+ std::optional<uint64_t> exclusiveTid;
bool allowNested = false;
};
@@ -225,7 +270,7 @@
//
// transfer ownership of thread (usually done while a lock is taken on the
// structure which originally owns the thread)
- void preJoinThreadOwnership(std::thread thread);
+ void preJoinThreadOwnership(RpcMaybeThread thread);
// pass FD to thread and read initial connection information
struct PreJoinSetupResult {
// Server connection object associated with this
@@ -244,7 +289,7 @@
[[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 +302,15 @@
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 +327,7 @@
const sp<RpcConnection>& get() { return mConnection; }
private:
- static void findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ static void findConnection(uint64_t tid, sp<RpcConnection>* exclusive,
sp<RpcConnection>* available,
std::vector<sp<RpcConnection>>& sockets,
size_t socketsIndexHint);
@@ -306,7 +357,7 @@
// For a more complicated case, the client might itself open up a thread to
// serve calls to the server at all times (e.g. if it hosts a callback)
- wp<RpcServer> mForServer; // maybe null, for client sessions
+ wp<RpcServer> mForServer; // maybe null, for client sessions
sp<WaitForShutdownListener> mShutdownListener; // used for client sessions
wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client
@@ -320,22 +371,27 @@
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;
+ size_t mMaxOutgoingConnections = kDefaultMaxOutgoingConnections;
std::optional<uint32_t> mProtocolVersion;
+ FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
- std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
+ RpcConditionVariable 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 0000000..8abf04e
--- /dev/null
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <pthread.h>
+
+#include <android-base/threads.h>
+
+#include <functional>
+#include <memory>
+#include <thread>
+
+namespace android {
+
+#ifdef BINDER_RPC_SINGLE_THREADED
+class RpcMutex {
+public:
+ void lock() {}
+ void unlock() {}
+};
+
+class RpcMutexUniqueLock {
+public:
+ RpcMutexUniqueLock(RpcMutex&) {}
+ void unlock() {}
+};
+
+class RpcMutexLockGuard {
+public:
+ RpcMutexLockGuard(RpcMutex&) {}
+};
+
+class RpcConditionVariable {
+public:
+ void notify_one() {}
+ void notify_all() {}
+
+ void wait(RpcMutexUniqueLock&) {}
+
+ template <typename Predicate>
+ void wait(RpcMutexUniqueLock&, Predicate stop_waiting) {
+ LOG_ALWAYS_FATAL_IF(!stop_waiting(), "RpcConditionVariable::wait condition not met");
+ }
+
+ template <typename Duration>
+ std::cv_status wait_for(RpcMutexUniqueLock&, const Duration&) {
+ return std::cv_status::no_timeout;
+ }
+
+ template <typename Duration, typename Predicate>
+ bool wait_for(RpcMutexUniqueLock&, const Duration&, Predicate stop_waiting) {
+ return stop_waiting();
+ }
+};
+
+class RpcMaybeThread {
+public:
+ RpcMaybeThread() = default;
+
+ template <typename Function, typename... Args>
+ RpcMaybeThread(Function&& f, Args&&... args) {
+ // std::function requires a copy-constructible closure,
+ // so we need to wrap both the function and its arguments
+ // in a shared pointer that std::function can copy internally
+ struct Vars {
+ std::decay_t<Function> f;
+ std::tuple<std::decay_t<Args>...> args;
+
+ explicit Vars(Function&& f, Args&&... args)
+ : f(std::move(f)), args(std::move(args)...) {}
+ };
+ auto vars = std::make_shared<Vars>(std::forward<Function>(f), std::forward<Args>(args)...);
+ mFunc = [vars]() { std::apply(std::move(vars->f), std::move(vars->args)); };
+ }
+
+ void join() {
+ if (mFunc) {
+ // Move mFunc into a temporary so we can clear mFunc before
+ // executing the callback. This avoids infinite recursion if
+ // the callee then calls join() again directly or indirectly.
+ decltype(mFunc) func = nullptr;
+ mFunc.swap(func);
+ func();
+ }
+ }
+ void detach() { join(); }
+
+ class id {
+ public:
+ bool operator==(const id&) const { return true; }
+ bool operator!=(const id&) const { return false; }
+ bool operator<(const id&) const { return false; }
+ bool operator<=(const id&) const { return true; }
+ bool operator>(const id&) const { return false; }
+ bool operator>=(const id&) const { return true; }
+ };
+
+ id get_id() const { return id(); }
+
+private:
+ std::function<void(void)> mFunc;
+};
+
+namespace rpc_this_thread {
+static inline RpcMaybeThread::id get_id() {
+ return RpcMaybeThread::id();
+}
+} // namespace rpc_this_thread
+
+static inline uint64_t rpcGetThreadId() {
+ return 0;
+}
+
+static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) {
+ t.join();
+}
+#else // BINDER_RPC_SINGLE_THREADED
+using RpcMutex = std::mutex;
+using RpcMutexUniqueLock = std::unique_lock<std::mutex>;
+using RpcMutexLockGuard = std::lock_guard<std::mutex>;
+using RpcConditionVariable = std::condition_variable;
+using RpcMaybeThread = std::thread;
+namespace rpc_this_thread = std::this_thread;
+
+static inline uint64_t rpcGetThreadId() {
+ return base::GetThreadId();
+}
+
+static inline void rpcJoinIfSingleThreaded(RpcMaybeThread&) {}
+#endif // BINDER_RPC_SINGLE_THREADED
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 751c4f9..fd52a3a 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 @@
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 @@
* 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 @@
// 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 @@
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 bf9c92b..eb98042 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -94,7 +94,7 @@
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 @@
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 0000000..9937842
--- /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 5baa4d7..a157792 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;
+
+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 and joins before returning.
-bool RunRpcServer(AIBinder* service, unsigned int port);
+// 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 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 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 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);
+// 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);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
+// 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);
-// Connect to an RPC server with preconnected file descriptors.
+// 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, to service connections.
+void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);
+
+// Sets the maximum number of outgoing connections.
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* session, size_t connections);
+
+// 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 0000000..4a4172a
--- /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 0000000..b034b9b
--- /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 bf2b25b..a167f23 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) {
- LOG(ERROR) << "Failed to set up vsock server with port " << port
- << " error: " << statusToString(status).c_str();
- return false;
+
+ 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
}
- 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();
-
- // Shutdown any open sessions since server failed.
- (void)server->shutdown();
- return true;
-}
-
-bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
- void* param) {
- auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
+ 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;
+ }
+ 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));
-
- if (readyCallback) readyCallback(param);
- server->join();
-
- // Shutdown any open sessions since server failed.
- (void)server->shutdown();
- return true;
+ return createObjectHandle<ARpcServer>(server);
}
-bool RunRpcServer(AIBinder* service, unsigned int port) {
- return RunRpcServerCallback(service, port, nullptr, nullptr);
+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);
}
-AIBinder* RpcClient(unsigned int cid, unsigned int port) {
+ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd) {
+ auto server = RpcServer::make();
+ auto fd = unique_fd(bootstrapFd);
+ if (!fd.ok()) {
+ LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+ 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 nullptr;
+ }
+ server->setRootObject(AIBinder_toPlatformBinder(service));
+ return createObjectHandle<ARpcServer>(server);
+}
+
+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);
+}
+
+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 ARpcServer_shutdown(ARpcServer* handle) {
+ return handleToStrongPointer<RpcServer>(handle)->shutdown();
+}
+
+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 @@
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 @@
}
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_setMaxOutgoingConnections(ARpcSession* handle, size_t connections) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
+ session->setMaxOutgoingConnections(connections);
+}
}
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index e856569..63679c2 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 79c8c8f..58ed418 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -57,6 +57,7 @@
srcs: [
"ibinder.cpp",
"ibinder_jni.cpp",
+ "libbinder.cpp",
"parcel.cpp",
"parcel_jni.cpp",
"process.cpp",
@@ -65,10 +66,14 @@
"service_manager.cpp",
],
- shared_libs: [
+ static_libs: [
"libandroid_runtime_lazy",
"libbase",
+ ],
+
+ shared_libs: [
"libbinder",
+ "liblog",
"libutils",
],
@@ -113,6 +118,7 @@
"abseil-*",
"android-*",
"bugprone-*",
+ "-bugprone-branch-clone", // b/155034972
"cert-*",
"clang-analyzer-*",
"-clang-analyzer-core.CallAndMessage",
@@ -180,4 +186,8 @@
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 28e3ff4..d0de7b9 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::Parcel;
using ::android::sp;
using ::android::status_t;
+using ::android::statusToString;
using ::android::String16;
using ::android::String8;
using ::android::wp;
@@ -63,6 +62,9 @@
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 @@
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 @@
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 @@
} 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 @@
}
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 @@
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 @@
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_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_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 @@
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 @@
(*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 @@
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 @@
}
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 @@
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 730e51b..67bb092 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -53,12 +53,14 @@
}
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 @@
// 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 @@
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 7ea9be7..d6937c2 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 @@
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 @@
}
};
+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 @@
/**
* 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 c8e78fc..9949de2 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 @@
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 @@
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 @@
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 0bf1e3d..95eee26 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 @@
return AParcel_writeParcelable(parcel, value);
} else {
static_assert(dependent_false_v<T>, "unrecognized type");
- return STATUS_OK;
}
}
@@ -1707,7 +1706,6 @@
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 f45aa76..caee471 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ef71a81..b0c7f6d 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 @@
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 @@
};
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 @@
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 4163897..db2d2c1 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 @@
*
* 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 @@
*
* 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 @@
*
* 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 @@
*
* 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 6880d86..8e288b3 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -56,6 +56,12 @@
* If the binder is null, null is returned. If this binder object was originally an IBinder object,
* the original java object will be returned.
*
+ * WARNING: this function returns global and local references. This can be
+ * figured out using GetObjectRefType. Though, when this function is called
+ * from within a Java context, the local ref will automatically be cleaned
+ * up. If this is called outside of a Java frame,
+ * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup.
+ *
* Available since API level 29.
*
* \param env Java environment. Must not be null.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 8457581..d833b83 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,11 +26,11 @@
#pragma once
+#include <android/binder_status.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
-
-#include <android/binder_status.h>
+#include <uchar.h>
struct AIBinder;
typedef struct AIBinder AIBinder;
@@ -59,6 +59,11 @@
/**
* Sets the position within the parcel.
*
+ * This must be called with a position that has been previously returned from
+ * AParcel_getDataPosition. If writes are made after setting the data position, they must
+ * be made in the exact same sequence used before resetting data position. Writing over
+ * objects such as binders or file descriptors is not supported.
+ *
* Available since API level 29.
*
* \param parcel The parcel of which to set the position.
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
index f0c00e8..74a7157 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 @@
*/
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 dfa8ea2..89fd7a3 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 @@
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 flags 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_addServiceWithFlags(
+ AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flags)
+ __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 @@
*
* \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 @@
__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 @@
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 f408fad..3fbe90d 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -28,19 +28,35 @@
*
* 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();
+void ABinderProcess_startThreadPool(void);
/**
* This sets the maximum number of threads that can be started in the threadpool. By default, after
* startThreadPool is called, this is 15. If it is called additional times, it will only prevent
* the kernel from starting new threads and will not delete already existing threads.
+ *
+ * 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(void);
+/**
* 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();
+void ABinderProcess_joinThreadPool(void);
/**
* This gives you an fd to wait on. Whenever data is available on the fd,
@@ -63,6 +79,6 @@
*
* \return STATUS_OK on success
*/
-__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands() __INTRODUCED_IN(31);
+__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands(void) __INTRODUCED_IN(31);
__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index d0cd11f..c1f62e5 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 @@
* 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 @@
* 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 @@
* 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 0000000..f94d81d
--- /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 3824a1b..1c5f79f 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -89,12 +89,12 @@
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 @@
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 @@
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_addServiceWithFlags; # 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 c320e8d..b5a2e2f 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -129,7 +129,13 @@
}
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 @@
}
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 @@
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 @@
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 ac582a4..0fea57b 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -24,14 +24,17 @@
using ::android::IPCThreadState;
using ::android::ProcessState;
-void ABinderProcess_startThreadPool() {
+void ABinderProcess_startThreadPool(void) {
ProcessState::self()->startThreadPool();
ProcessState::self()->giveThreadPoolName();
}
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
}
-void ABinderProcess_joinThreadPool() {
+bool ABinderProcess_isThreadPoolStarted(void) {
+ return ProcessState::self()->isThreadPoolStarted();
+}
+void ABinderProcess_joinThreadPool(void) {
IPCThreadState::self()->joinThreadPool();
}
@@ -39,6 +42,6 @@
return IPCThreadState::self()->setupPolling(fd);
}
-binder_status_t ABinderProcess_handlePolledCommands() {
+binder_status_t ABinderProcess_handlePolledCommands(void) {
return IPCThreadState::self()->handlePolledCommands();
}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 7649a26..2977786 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -28,6 +28,7 @@
using ::android::IServiceManager;
using ::android::sp;
using ::android::status_t;
+using ::android::statusToString;
using ::android::String16;
using ::android::String8;
@@ -40,6 +41,20 @@
status_t exception = sm->addService(String16(instance), binder->getBinder());
return PruneException(exception);
}
+
+binder_exception_t AServiceManager_addServiceWithFlags(AIBinder* binder, const char* instance,
+ const AServiceManager_AddServiceFlag flags) {
+ if (binder == nullptr || instance == nullptr) {
+ return EX_ILLEGAL_ARGUMENT;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ bool allowIsolated = flags & 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_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 @@
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 f3cd218..43b2cb8 100644
--- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
@@ -106,7 +106,7 @@
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 2afe5d2..76acff5 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -72,6 +72,11 @@
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 7408d0c..0a562f0 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -30,8 +30,13 @@
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 1b136dc..cefc42f 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;
@@ -49,10 +51,23 @@
constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService";
constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
+constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged";
-constexpr unsigned int kShutdownWaitTime = 10;
+constexpr unsigned int kShutdownWaitTime = 11;
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;
@@ -144,6 +159,24 @@
return 1; // should not return
}
+int generatedFlaggedService(const AServiceManager_AddServiceFlag flags, const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ auto binder = service->asBinder();
+
+ binder_exception_t exception =
+ AServiceManager_addServiceWithFlags(binder.get(), instance, flags);
+
+ if (exception != EX_NONE) {
+ LOG(FATAL) << "Could not register: " << exception << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
// manually-written parceling class considered bad practice
class MyFoo : public IFoo {
binder_status_t doubleNumber(int32_t in, int32_t* out) override {
@@ -252,12 +285,52 @@
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 +354,24 @@
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 +428,16 @@
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 +593,6 @@
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 +633,8 @@
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 +751,35 @@
}
}
+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;
@@ -747,6 +866,12 @@
EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
}
+TEST(NdkBinder, FlaggedServiceAccessible) {
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestServiceFlagged));
+ ASSERT_NE(nullptr, testService);
+}
+
TEST(NdkBinder, GetClassInterfaceDescriptor) {
ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
}
@@ -801,6 +926,13 @@
prctl(PR_SET_PDEATHSIG, SIGHUP);
return generatedService();
}
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ // We may want to change this flag to be more generic ones for the future
+ AServiceManager_AddServiceFlag test_flags =
+ AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
+ return generatedFlaggedService(test_flags, kBinderNdkUnitTestServiceFlagged);
+ }
ABinderProcess_setThreadPoolMaxThreadCount(1); // to receive death notifications/callbacks
ABinderProcess_startThreadPool();
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 355b3b4..d36ebac 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -15,12 +15,13 @@
"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 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.rkpd",
"com.android.uwb",
"com.android.virt",
],
@@ -71,6 +73,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
target: {
darwin: {
enabled: false,
@@ -79,12 +82,14 @@
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 @@
],
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 @@
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 @@
"libbinder_ndk",
],
rustlibs: [
- "liblibc",
"libbinder_ndk_sys",
"libdowncast_rs",
+ "liblibc",
],
}
@@ -185,13 +175,3 @@
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 9dcef42..2d2bf7c 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 @@
/// 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 0000000..0067a20
--- /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 0000000..a957385
--- /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 0000000..c87876a
--- /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 0000000..28c5390
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -0,0 +1,219 @@
+/*
+ * 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 connections.
+ pub fn set_max_outgoing_connections(&self, connections: usize) {
+ // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ unsafe {
+ binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
+ self.as_ptr(),
+ connections,
+ )
+ };
+ }
+
+ /// 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 5d6206d..d0e35de 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;
@@ -100,22 +100,17 @@
/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
/// makes no stability guarantees ([`Local`]). [`Local`] is
/// currently the default stability.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Stability {
/// Default stability, visible to other modules in the same compilation
/// context (e.g. modules on system.img)
+ #[default]
Local,
/// A Vendor Interface Object, which promises to be stable
Vintf,
}
-impl Default for Stability {
- fn default() -> Self {
- Stability::Local
- }
-}
-
impl From<Stability> for i32 {
fn from(stability: Stability) -> i32 {
use Stability::*;
@@ -133,7 +128,7 @@
match stability {
0 => Ok(Local),
1 => Ok(Vintf),
- _ => Err(StatusCode::BAD_VALUE)
+ _ => Err(StatusCode::BAD_VALUE),
}
}
}
@@ -158,7 +153,12 @@
/// 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 +454,19 @@
/// 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 +605,12 @@
/// 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 +1086,7 @@
}
} => {
$( #[$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 +1099,15 @@
}
}
+ 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 579f9f9..d90acaf 100644
--- a/libs/binder/rust/src/binder_async.rs
+++ b/libs/binder/rust/src/binder_async.rs
@@ -41,7 +41,10 @@
/// 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 2598ebc..f6b09ed 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -18,7 +18,7 @@
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 @@
// 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 @@
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 @@
}
}
+ /// 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 @@
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 dbfb1c2..0c8b48f 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 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 b7c7ae4..5557168 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -30,6 +30,7 @@
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 @@
// 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 @@
}
/// 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 @@
}
/// 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 @@
/// 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 @@
// 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 @@
// 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 @@
/// 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 @@
}
}
+/// 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 256fa8b..e4c568e 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -22,10 +22,10 @@
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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
/// 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 @@
// 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 @@
/// ```
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 @@
///
/// 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 @@
/// ```
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 @@
///
/// 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 @@
///
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 @@
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 @@
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 @@
///
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 4d3d59a..de6d649 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 @@
// 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 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 0c7e48d..4b658fc 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -22,8 +22,8 @@
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 @@
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 @@
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 @@
///
/// 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 @@
true
}
-
macro_rules! parcelable_primitives {
{
$(
@@ -303,11 +288,7 @@
// 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 @@
// `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 @@
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 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 @@
// 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()))
}
}
}
@@ -802,7 +771,13 @@
#[macro_export]
macro_rules! impl_serialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::binder_impl::Serialize for $parcelable {
+ $crate::impl_serialize_for_parcelable!($parcelable < >);
+ };
+ ($parcelable:ident < $( $param:ident ),* , >) => {
+ $crate::impl_serialize_for_parcelable!($parcelable < $($param),* >);
+ };
+ ($parcelable:ident < $( $param:ident ),* > ) => {
+ impl < $($param),* > $crate::binder_impl::Serialize for $parcelable < $($param),* > {
fn serialize(
&self,
parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
@@ -811,9 +786,9 @@
}
}
- impl $crate::binder_impl::SerializeArray for $parcelable {}
+ impl < $($param),* > $crate::binder_impl::SerializeArray for $parcelable < $($param),* > {}
- impl $crate::binder_impl::SerializeOption for $parcelable {
+ impl < $($param),* > $crate::binder_impl::SerializeOption for $parcelable < $($param),* > {
fn serialize_option(
this: Option<&Self>,
parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
@@ -839,7 +814,13 @@
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::binder_impl::Deserialize for $parcelable {
+ $crate::impl_deserialize_for_parcelable!($parcelable < >);
+ };
+ ($parcelable:ident < $( $param:ident ),* , >) => {
+ $crate::impl_deserialize_for_parcelable!($parcelable < $($param),* >);
+ };
+ ($parcelable:ident < $( $param:ident ),* > ) => {
+ impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > {
fn deserialize(
parcel: &$crate::binder_impl::BorrowedParcel<'_>,
) -> std::result::Result<Self, $crate::StatusCode> {
@@ -861,9 +842,9 @@
}
}
- impl $crate::binder_impl::DeserializeArray for $parcelable {}
+ impl < $($param: Default),* > $crate::binder_impl::DeserializeArray for $parcelable < $($param),* > {}
- impl $crate::binder_impl::DeserializeOption for $parcelable {
+ impl < $($param: Default),* > $crate::binder_impl::DeserializeOption for $parcelable < $($param),* > {
fn deserialize_option(
parcel: &$crate::binder_impl::BorrowedParcel<'_>,
) -> std::result::Result<Option<Self>, $crate::StatusCode> {
@@ -881,8 +862,7 @@
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 +895,8 @@
#[cfg(test)]
mod tests {
- use crate::parcel::Parcel;
use super::*;
+ use crate::parcel::Parcel;
#[test]
fn test_custom_parcelable() {
@@ -934,11 +914,11 @@
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 +1137,7 @@
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 +1153,7 @@
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 432da5d..c829d37 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -48,10 +48,7 @@
#[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 @@
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 @@
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 @@
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 @@
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 @@
// 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 4df557b..254efae 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -22,7 +22,8 @@
};
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 @@
}
}
+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 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 @@
/// 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 @@
//
// 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 @@
/// 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 0aef744..cc18741 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -124,7 +124,8 @@
/// 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 c9d6af0..ca2cedc 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -60,9 +60,7 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
);
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
})
};
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
#[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 @@
#[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 @@
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 @@
#[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 @@
#[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 @@
#[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 @@
// 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 @@
// 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 0000000..df8a2af
--- /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 0000000..29bf92c
--- /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 0000000..43a3094
--- /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 0000000..5cb406a
--- /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 0000000..8ce6558
--- /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 0000000..c530382
--- /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 0000000..1bbd674
--- /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 0000000..831bd56
--- /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 0000000..a2d48b6
--- /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 ec780f2..3f59dab 100644
--- a/libs/binder/rust/tests/serialization.cpp
+++ b/libs/binder/rust/tests/serialization.cpp
@@ -381,7 +381,7 @@
string expected = "TestingFileDescriptors";
vector<char> buf(expected.length());
base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size());
- ASSERT_EQ(expected, string(buf.data()));
+ ASSERT_EQ(expected, string(buf.data(), expected.length()));
}
TEST_F(SerializationTest, SerializeIBinder) {
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index b62da7b..6220db4 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -23,7 +23,7 @@
};
// 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 {
($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 @@
) -> 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
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 777f3c9..692cc95 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -156,6 +156,10 @@
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 9ebeca0..873e955 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -100,6 +100,7 @@
"binderBinderUnitTest.cpp",
"binderStatusUnitTest.cpp",
"binderMemoryHeapBaseUnitTest.cpp",
+ "binderRecordedTransactionTest.cpp",
],
shared_libs: [
"libbinder",
@@ -137,10 +138,12 @@
aidl_interface {
name: "binderRpcTestIface",
+ vendor_available: true,
host_supported: true,
unstable: true,
srcs: [
"BinderRpcTestClientInfo.aidl",
+ "BinderRpcTestServerConfig.aidl",
"BinderRpcTestServerInfo.aidl",
"IBinderRpcCallback.aidl",
"IBinderRpcSession.aidl",
@@ -157,6 +160,7 @@
cc_library_static {
name: "libbinder_tls_test_utils",
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
@@ -167,7 +171,6 @@
"libbinder_tls_shared_deps",
],
shared_libs: [
- "libbinder",
"libbase",
"liblog",
],
@@ -185,30 +188,80 @@
],
}
-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",
],
+
+ // This test uses a lot of resources and takes a long time. Due to
+ // design of several tests, it is also very sensitive to resource
+ // contention on the device. b/276820894
+ test_options: {
+ unit_test: false,
+ },
+
+ 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,13 +270,213 @@
"libcutils",
"liblog",
],
- static_libs: [
- "libbinder_tls_static",
- "libbinder_tls_test_utils",
- "binderRpcTestIface-cpp",
- "binderRpcTestIface-ndk",
+}
+
+cc_defaults {
+ name: "binderRpcTest_static_defaults",
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ // libcrypto_static is not visible to this module
+ "libcrypto",
],
- test_suites: ["general-tests"],
+ 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: [
+ "binderRpcTestIface-cpp",
+ ],
+}
+
+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 {
+ name: "binderRpcToTrustyTest",
+ vendor: true,
+ host_supported: false,
+ defaults: [
+ "binderRpcTest_common_defaults",
+ "binderRpcTest_static_defaults",
+ ],
+
+ srcs: [
+ "binderRpcTest.cpp",
+ "binderRpcTestCommon.cpp",
+ "binderRpcUniversalTests.cpp",
+ ],
+
+ cflags: [
+ "-DBINDER_RPC_TO_TRUSTY_TEST",
+ ],
+
+ static_libs: [
+ // We want to link libbinder statically so we can push the binary
+ // to the device for testing independently of the library
+ "libbinder_rpc_no_kernel",
+ "libbinder_trusty",
+ "libtrusty",
+ ],
+
+ test_suites: ["device-tests"],
require_root: true,
}
@@ -324,7 +577,6 @@
"libbinder",
"libutils",
],
- clang: true,
cflags: [
"-g",
"-Wno-missing-field-initializers",
@@ -445,6 +697,7 @@
shared_libs: [
"libbinder",
"liblog",
+ "libcutils",
"libutils",
"libutilscallstack",
"libbase",
@@ -529,3 +782,39 @@
],
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 0000000..b2e0ef2
--- /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 fdd02a4..1164767 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -24,6 +24,9 @@
// number of known RPC binders to process, RpcState::countBinders by session
int[] countBinders();
+ // Return a null binder with a non-nullable return type.
+ IBinder getNullBinder();
+
// Caller sends server, callee pings caller's server and returns error code.
int pingMe(IBinder binder);
@nullable IBinder repeatBinder(@nullable IBinder binder);
@@ -64,4 +67,21 @@
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();
+
+ // Same as blockingSendFdOneway, but with integers.
+ oneway void blockingSendIntOneway(int n);
+ int blockingRecvInt();
}
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index e1f5ed5..bc40864 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 @@
});
}
-using android::IBinder;
-using android::Parcel;
-using android::String16;
+using android::BBinder;
using android::defaultServiceManager;
-using android::sp;
+using android::IBinder;
using android::IServiceManager;
+using android::OK;
+using android::Parcel;
+using android::RpcServer;
+using android::RpcSession;
+using android::sp;
+using android::status_t;
+using android::statusToString;
+using android::String16;
static sp<IBinder> GetRemoteBinder() {
// This gets binder representing the service manager
@@ -160,6 +172,28 @@
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 @@
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 @@
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 ce2770f..b6aed0d 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 @@
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/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 464da60..77a5fa8 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -66,7 +66,7 @@
void initHostRpcServiceManagerOnce() {
static std::once_flag gSmOnce;
std::call_once(gSmOnce, [] {
- setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
});
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index b1e17b7..8974ad7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -30,6 +30,7 @@
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <binder/Binder.h>
@@ -82,6 +83,7 @@
static constexpr int kSchedPolicy = SCHED_RR;
static constexpr int kSchedPriority = 7;
static constexpr int kSchedPriorityMore = 8;
+static constexpr int kKernelThreads = 15;
static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -113,8 +115,16 @@
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 @@
{
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 @@
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());
@@ -493,7 +507,13 @@
}
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
- EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+
+ // b/268232063 - succeeds ~0.08% of the time
+ {
+ auto ret = IPCThreadState::self()->freeze(pid, true, 0);
+ EXPECT_TRUE(ret == -EAGAIN || ret == OK);
+ }
+
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
@@ -1146,6 +1166,56 @@
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 +1291,53 @@
EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
}
+struct TooManyFdsFlattenable : Flattenable<TooManyFdsFlattenable> {
+ TooManyFdsFlattenable(size_t fdCount) : mFdCount(fdCount) {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const {
+ // Return a valid non-zero size here so we don't get an unintended
+ // BAD_VALUE from Parcel::write
+ return 16;
+ }
+ size_t getFdCount() const { return mFdCount; }
+ status_t flatten(void *& /*buffer*/, size_t & /*size*/, int *&fds, size_t &count) const {
+ for (size_t i = 0; i < count; i++) {
+ fds[i] = STDIN_FILENO;
+ }
+ return NO_ERROR;
+ }
+ status_t unflatten(void const *& /*buffer*/, size_t & /*size*/, int const *& /*fds*/,
+ size_t & /*count*/) {
+ /* This doesn't get called */
+ return NO_ERROR;
+ }
+
+ size_t mFdCount;
+};
+
+TEST_F(BinderLibTest, TooManyFdsFlattenable) {
+ rlimit origNofile;
+ int ret = getrlimit(RLIMIT_NOFILE, &origNofile);
+ ASSERT_EQ(0, ret);
+
+ // Restore the original file limits when the test finishes
+ base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); });
+
+ rlimit testNofile = {1024, 1024};
+ ret = setrlimit(RLIMIT_NOFILE, &testNofile);
+ ASSERT_EQ(0, ret);
+
+ Parcel parcel;
+ // Try to write more file descriptors than supported by the OS
+ TooManyFdsFlattenable tooManyFds1(1024);
+ EXPECT_THAT(parcel.write(tooManyFds1), StatusEq(-EMFILE));
+
+ // Try to write more file descriptors than the internal limit
+ TooManyFdsFlattenable tooManyFds2(1025);
+ EXPECT_THAT(parcel.write(tooManyFds2), StatusEq(BAD_VALUE));
+}
+
TEST(ServiceNotifications, Unregister) {
auto sm = defaultServiceManager();
using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
@@ -1234,6 +1351,89 @@
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(500);
+ // 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 +1834,74 @@
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 +1912,7 @@
sp<IBinder> m_strongRef;
sp<IBinder> m_callback;
bool m_exitOnDestroy;
+ std::mutex m_blockMutex;
};
int run_server(int index, int readypipefd, bool usePoll)
@@ -1756,6 +2014,7 @@
}
}
} else {
+ ProcessState::self()->setThreadPoolMaxThreadCount(kKernelThreads);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
index 21cb70b..278dd2b 100644
--- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
+++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
@@ -23,6 +23,7 @@
#ifdef __BIONIC__
TEST(MemoryHeapBase, ForceMemfdRespected) {
auto mHeap = sp<MemoryHeapBase>::make(10, MemoryHeapBase::FORCE_MEMFD, "Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_FALSE(ashmem_valid(fd));
@@ -33,6 +34,7 @@
auto mHeap = sp<MemoryHeapBase>::make(8192,
MemoryHeapBase::FORCE_MEMFD,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL);
@@ -43,6 +45,7 @@
MemoryHeapBase::FORCE_MEMFD |
MemoryHeapBase::MEMFD_ALLOW_SEALING,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0);
@@ -53,6 +56,7 @@
MemoryHeapBase::FORCE_MEMFD |
MemoryHeapBase::READ_ONLY,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
@@ -64,6 +68,7 @@
MemoryHeapBase::READ_ONLY |
MemoryHeapBase::MEMFD_ALLOW_SEALING,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE);
@@ -74,6 +79,7 @@
auto mHeap = sp<MemoryHeapBase>::make(8192,
MemoryHeapBase::READ_ONLY,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
void* ptr = mHeap->getBase();
EXPECT_NE(ptr, MAP_FAILED);
@@ -87,6 +93,7 @@
MemoryHeapBase::READ_ONLY |
MemoryHeapBase::MEMFD_ALLOW_SEALING,
"Test mapping");
+ ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
void* ptr = mHeap->getBase();
EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY);
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
new file mode 100644
index 0000000..30172cc
--- /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/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 52ba9b0..5939273 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -102,9 +102,11 @@
}
static sp<RpcSession> gSession = RpcSession::make();
+static sp<IBinder> gRpcBinder;
// Certificate validation happens during handshake and does not affect the result of benchmarks.
// Skip certificate validation to simplify the setup process.
static sp<RpcSession> gSessionTls = RpcSession::make(makeFactoryTls());
+static sp<IBinder> gRpcTlsBinder;
#ifdef __BIONIC__
static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
static sp<IBinder> gKernelBinder;
@@ -118,9 +120,9 @@
return gKernelBinder;
#endif
case RPC:
- return gSession->getRootObject();
+ return gRpcBinder;
case RPC_TLS:
- return gSessionTls->getRootObject();
+ return gRpcTlsBinder;
default:
LOG(FATAL) << "Unknown transport value: " << transport;
return nullptr;
@@ -254,11 +256,13 @@
(void)unlink(addr.c_str());
forkRpcServer(addr.c_str(), RpcServer::make(RpcTransportCtxFactoryRaw::make()));
setupClient(gSession, addr.c_str());
+ gRpcBinder = gSession->getRootObject();
std::string tlsAddr = tmp + "/binderRpcTlsBenchmark";
(void)unlink(tlsAddr.c_str());
forkRpcServer(tlsAddr.c_str(), RpcServer::make(makeFactoryTls()));
setupClient(gSessionTls, tlsAddr.c_str());
+ gRpcTlsBinder = gSessionTls->getRootObject();
::benchmark::RunSpecifiedBenchmarks();
return 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index c2639e7..504b3ce 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 BINDER_RPC_TO_TRUSTY_TEST
+#include <binder/RpcTransportTipcAndroid.h>
+#include <trusty/tipc.h>
+#endif // BINDER_RPC_TO_TRUSTY_TEST
+
+#include "binderRpcTestCommon.h"
+#include "binderRpcTestFixture.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -62,278 +44,48 @@
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";
+#ifdef BINDER_TEST_NO_SHARED_LIBS
+constexpr bool kEnableSharedLibs = false;
+#else
+constexpr bool kEnableSharedLibs = true;
+#endif
-enum class RpcSecurity { RAW, TLS };
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
+#endif
-static inline std::vector<RpcSecurity> RpcSecurityValues() {
- return {RpcSecurity::RAW, RpcSecurity::TLS};
+static std::string WaitStatusToString(int wstatus) {
+ if (WIFEXITED(wstatus)) {
+ return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
+ }
+ if (WIFSIGNALED(wstatus)) {
+ return base::StringPrintf("term signal %d", WTERMSIG(wstatus));
+ }
+ return base::StringPrintf("unexpected state %d", wstatus);
}
-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 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(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();
- }
-};
-
-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());
-}
-
-TEST(BinderRpc, CannotUseNextWireVersion) {
- auto session = RpcSession::make();
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
-}
-
-TEST(BinderRpc, CanUseExperimentalWireVersion) {
- auto session = RpcSession::make();
- EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
-}
-
-using android::binder::Status;
-
-#define EXPECT_OK(status) \
- 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 @@
}
~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;
@@ -360,36 +129,42 @@
static std::string allocateSocketAddress() {
static size_t id = 0;
std::string temp = getenv("TMPDIR") ?: "/tmp";
- auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+ auto ret = temp + "/binderRpcTest_" + std::to_string(getpid()) + "_" + std::to_string(id++);
unlink(ret.c_str());
return ret;
};
static unsigned int allocateVsockPort() {
- static unsigned int vsockPort = 3456;
+ static unsigned int vsockPort = 34567;
return vsockPort++;
}
-struct ProcessSession {
+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;
+}
+
+// Destructors need to be defined, even if pure virtual
+ProcessSession::~ProcessSession() {}
+
+class LinuxProcessSession : public ProcessSession {
+public:
// reference to process hosting a socket server
Process host;
- 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;
-
- ProcessSession(ProcessSession&&) = default;
- ~ProcessSession() {
+ LinuxProcessSession(LinuxProcessSession&&) = default;
+ LinuxProcessSession(Process&& host) : host(std::move(host)) {}
+ ~LinuxProcessSession() override {
for (auto& session : sessions) {
session.root = nullptr;
}
- for (auto& info : sessions) {
+ for (size_t sessionNum = 0; sessionNum < sessions.size(); sessionNum++) {
+ auto& info = sessions.at(sessionNum);
sp<RpcSession>& session = info.session;
EXPECT_NE(nullptr, session);
@@ -398,72 +173,31 @@
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;
+ // 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)
+ << "For session " << sessionNum << ". "
+ << (debugBacktrace(host.getPid()), debugBacktrace(getpid()),
+ "Leaked sess: ")
+ << strongSession->getStrongCount() << " checked time " << i;
- // 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;
+ if (strongSession != nullptr) {
+ sleep(1);
+ }
}
}
-
- rootIface = nullptr;
- rootBinder = nullptr;
}
-};
-enum class SocketType {
- PRECONNECTED,
- UNIX,
- VSOCK,
- INET,
-};
-static inline std::string PrintToString(SocketType socketType) {
- switch (socketType) {
- case SocketType::PRECONNECTED:
- return "preconnected_uds";
- case SocketType::UNIX:
- return "unix_domain_socket";
- case SocketType::VSOCK:
- return "vm_socket";
- case SocketType::INET:
- return "inet_socket";
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- return "";
+ void setCustomExitStatusCheck(std::function<void(int wstatus)> f) override {
+ host.setCustomExitStatusCheck(std::move(f));
}
-}
+
+ void terminate() override { host.terminate(); }
+};
static base::unique_fd connectTo(const RpcSocketAddress& addr) {
base::unique_fd serverFd(
@@ -480,128 +214,122 @@
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();
+#ifndef BINDER_RPC_TO_TRUSTY_TEST
+static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
+ base::unique_fd sockClient, sockServer;
+ if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
}
- static 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);
+}
+#endif // BINDER_RPC_TO_TRUSTY_TEST
+
+std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+ return newTlsFactory(rpcSecurity);
+}
+
+// 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";
+
+ if (options.numIncomingConnectionsBySession.size() != 0) {
+ CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions);
}
- 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;
- }
+ 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());
- 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()));
- }
+ 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" : "");
- 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;
- }
+ base::unique_fd bootstrapClientFd, socketFd;
- // 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");
- }
-
- BinderRpcTestServerInfo serverInfo;
- serverInfo.port = static_cast<int64_t>(outPort);
- serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
- writeToFd(writeEnd, serverInfo);
- auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
-
- if (rpcSecurity == RpcSecurity::TLS) {
- for (const auto& clientCert : clientInfo.certs) {
- CHECK_EQ(OK,
- certVerifier
- ->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- clientCert.data));
- }
- }
-
- configure(server);
-
- server->join();
-
- // Another thread calls shutdown. Wait for it to complete.
- (void)server->shutdown();
- }),
- };
-
- 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 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);
}
+ }
- auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd());
+ 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;
+ }
+
+ 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 BINDER_RPC_TO_TRUSTY_TEST
+ factory = RpcTransportCtxFactoryTipcAndroid::make();
+#else
+ LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor");
+#endif
+ } else {
+ factory = newTlsFactory(rpcSecurity, certVerifier);
+ }
+ sessions.emplace_back(RpcSession::make(std::move(factory)));
+ }
+
+ 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 +342,80 @@
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));
- }
-
- 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);
-}
-
-TEST_P(BinderRpc, ManySessions) {
- auto proc = createRpcTestSocketServerProcess({});
-
- std::vector<sp<IBinderRpcSession>> sessions;
+ status_t status;
- 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);
+ const auto& session = sessions.at(i);
- while (!sessions.empty()) {
- sessions.pop_back();
- expectSessions(sessions.size(), proc.rootIface);
- }
- expectSessions(0, proc.rootIface);
-}
+ size_t numIncoming = options.numIncomingConnectionsBySession.size() > 0
+ ? options.numIncomingConnectionsBySession.at(i)
+ : 0;
-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();
+ CHECK(session->setProtocolVersion(clientVersion));
+ session->setMaxIncomingThreads(numIncoming);
+ session->setMaxOutgoingConnections(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 BINDER_RPC_TO_TRUSTY_TEST
+ auto port = trustyIpcPort(serverVersion);
+ for (size_t i = 0; i < 5; i++) {
+ // Try to connect several times,
+ // in case the service is slow to start
+ int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
+ if (tipcFd >= 0) {
+ return android::base::unique_fd(tipcFd);
+ }
+ usleep(50000);
+ }
+ return 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()});
+ }
+ return ret;
}
TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+ if (clientOrServerSingleThreaded()) {
+ GTEST_SKIP() << "This test requires multiple threads";
+ }
+
constexpr size_t kNumThreads = 10;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
@@ -1048,7 +433,7 @@
// 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 +447,7 @@
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) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -1082,25 +466,41 @@
}
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});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+
+ // b/272429574 - below 500ms, the test fails
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
}
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;
auto proc = createRpcTestSocketServerProcess(
{.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+
+ // b/272429574 - below 500ms, the test fails
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
}
TEST_P(BinderRpc, ThreadingStressTest) {
- constexpr size_t kNumClientThreads = 10;
- constexpr size_t kNumServerThreads = 10;
- constexpr size_t kNumCalls = 100;
+ if (clientOrServerSingleThreaded()) {
+ GTEST_SKIP() << "This test requires multiple threads";
+ }
+
+ constexpr size_t kNumClientThreads = 5;
+ constexpr size_t kNumServerThreads = 5;
+ constexpr size_t kNumCalls = 50;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
@@ -1127,6 +527,10 @@
}
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,50 +551,79 @@
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) {
- constexpr size_t kNumSleeps = 10;
+ if (clientOrServerSingleThreaded()) {
+ GTEST_SKIP() << "This test requires multiple threads";
+ }
+
+ constexpr size_t kNumQueued = 10;
constexpr size_t kNumExtraServerThreads = 4;
- constexpr size_t kSleepMs = 50;
// make sure calls to the same object happen on the same thread
auto proc = createRpcTestSocketServerProcess({.numThreads = 1 + kNumExtraServerThreads});
- EXPECT_OK(proc.rootIface->lock());
-
- size_t epochMsBefore = epochMillis();
-
- // all these *Async commands should be queued on the server sequentially,
+ // all these *Oneway commands should be queued on the server sequentially,
// even though there are multiple threads.
- for (size_t i = 0; i + 1 < kNumSleeps; i++) {
- proc.rootIface->sleepMsAsync(kSleepMs);
+ for (size_t i = 0; i + 1 < kNumQueued; i++) {
+ proc.rootIface->blockingSendIntOneway(i);
}
- EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
-
- // this can only return once the final async call has unlocked
- EXPECT_OK(proc.rootIface->lockUnlock());
-
- size_t epochMsAfter = epochMillis();
-
- EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+ for (size_t i = 0; i + 1 < kNumQueued; i++) {
+ int n;
+ proc.rootIface->blockingRecvInt(&n);
+ EXPECT_EQ(n, i);
+ }
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 +632,7 @@
// 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 +660,157 @@
// 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));
- }
-
- 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;
- }
-
- // 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, SessionWithIncomingThreadpoolDoesntLeak) {
+ if (clientOrServerSingleThreaded()) {
+ GTEST_SKIP() << "This test requires multiple threads";
}
+
+ // session 0 - will check for leaks in destrutor of proc
+ // session 1 - we want to make sure it gets deleted when we drop all references to it
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = 1, .numIncomingConnectionsBySession = {0, 1}, .numSessions = 2});
+
+ wp<RpcSession> session = proc.proc->sessions.at(1).session;
+
+ // remove all references to the second session
+ proc.proc->sessions.at(1).root = nullptr;
+ proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
+
+ // TODO(b/271830568) more efficient way to wait for other incoming threadpool
+ // to drain commands.
+ for (size_t i = 0; i < 100; i++) {
+ usleep(10 * 1000);
+ if (session.promote() == nullptr) break;
+ }
+
+ EXPECT_EQ(nullptr, session.promote());
}
-TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
- auto proc = createRpcTestSocketServerProcess({});
- auto cb = sp<MyBinderRpcCallback>::make();
+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;
+ };
- Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
- EXPECT_EQ(WOULD_BLOCK, status.transactionError());
+ // Death recipient needs to have an incoming connection to be called
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
+
+ auto dr = sp<MyDeathRec>::make();
+ ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
+
+ 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"
+ // can't do this before checking the death recipient by calling
+ // forceShutdown earlier, because shutdownAndWait will also trigger
+ // a death recipient, but if we had a way to wait for the service
+ // to gracefully shutdown, we could use that here.
+ 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, .numIncomingConnectionsBySession = {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, 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});
+
+ 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, .numIncomingConnectionsBySession = {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));
+
+ proc.forceShutdown();
}
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 +825,237 @@
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 +1065,10 @@
}
TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
+ if constexpr (!kEnableSharedLibs) {
+ GTEST_SKIP() << "Test disabled because Binder was built as a static library";
+ }
+
auto proc = createRpcTestSocketServerProcess({});
ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
@@ -1348,6 +1082,7 @@
ASSERT_TRUE(status.isOk()) << status.getDescription();
ASSERT_EQ("aoeuaoeu", out);
}
+#endif // __ANDROID_VENDOR__
ssize_t countFds() {
DIR* dir = opendir("/proc/self/fd/");
@@ -1360,6 +1095,13 @@
}
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 +1111,107 @@
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);
-}
-
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+ ::testing::Combine(::testing::Values(SocketType::TIPC),
+ ::testing::Values(RpcSecurity::RAW),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(true), ::testing::Values(true)),
+ BinderRpc::PrintParamInfo);
+#else // BINDER_RPC_TO_TRUSTY_TEST
static bool testSupportVsockLoopback() {
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
- sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make());
- if (status_t status = server->setupVsockServer(vsockPort); status != OK) {
- if (status == -EAFNOSUPPORT) {
- return false;
- }
- LOG_ALWAYS_FATAL("Could not setup vsock server: %s", statusToString(status).c_str());
- }
- server->start();
- sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
- status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
- while (!server->shutdown()) usleep(10000);
- ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str());
- return status == OK;
+ android::base::unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
+
+ sockaddr_vm serverAddr{
+ .svm_family = AF_VSOCK,
+ .svm_port = vsockPort,
+ .svm_cid = VMADDR_CID_ANY,
+ };
+ int ret = TEMP_FAILURE_RETRY(
+ bind(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr)));
+ LOG_ALWAYS_FATAL_IF(0 != ret, "Could not bind socket to port %u: %s", vsockPort,
+ strerror(errno));
+
+ ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/));
+ LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", vsockPort,
+ strerror(errno));
+
+ // Try to connect to the server using the VMADDR_CID_LOCAL cid
+ // to see if the kernel supports it. It's safe to use a blocking
+ // connect because vsock sockets have a 2 second connection timeout,
+ // and they return ETIMEDOUT after that.
+ android::base::unique_fd connectFd(
+ TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort,
+ strerror(errno));
+
+ bool success = false;
+ sockaddr_vm connectAddr{
+ .svm_family = AF_VSOCK,
+ .svm_port = vsockPort,
+ .svm_cid = VMADDR_CID_LOCAL,
+ };
+ ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr),
+ sizeof(connectAddr)));
+ if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) {
+ android::base::unique_fd acceptFd;
+ while (true) {
+ pollfd pfd[]{
+ {.fd = serverFd.get(), .events = POLLIN, .revents = 0},
+ {.fd = connectFd.get(), .events = POLLOUT, .revents = 0},
+ };
+ ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno));
+
+ if (pfd[0].revents & POLLIN) {
+ sockaddr_vm acceptAddr;
+ socklen_t acceptAddrLen = sizeof(acceptAddr);
+ ret = TEMP_FAILURE_RETRY(accept4(serverFd.get(),
+ reinterpret_cast<sockaddr*>(&acceptAddr),
+ &acceptAddrLen, SOCK_CLOEXEC));
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Could not accept4 socket: %s", strerror(errno));
+ LOG_ALWAYS_FATAL_IF(acceptAddrLen != static_cast<socklen_t>(sizeof(acceptAddr)),
+ "Truncated address");
+
+ // Store the fd in acceptFd so we keep the connection alive
+ // while polling connectFd
+ acceptFd.reset(ret);
+ }
+
+ if (pfd[1].revents & POLLOUT) {
+ // Connect either succeeded or timed out
+ int connectErrno;
+ socklen_t connectErrnoLen = sizeof(connectErrno);
+ int ret = getsockopt(connectFd.get(), SOL_SOCKET, SO_ERROR, &connectErrno,
+ &connectErrnoLen);
+ LOG_ALWAYS_FATAL_IF(ret == -1,
+ "Could not getsockopt() after connect() "
+ "on non-blocking socket: %s.",
+ strerror(errno));
+
+ // We're done, this is all we wanted
+ success = connectErrno == 0;
+ break;
+ }
+ }
+ } else {
+ success = ret == 0;
+ }
+
+ ALOGE("Detected vsock loopback supported: %s", success ? "yes" : "no");
+
+ return success;
}
static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
- 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);
@@ -1414,7 +1226,11 @@
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);
class BinderRpcServerRootObject
@@ -1427,7 +1243,7 @@
};
auto [isStrong1, isStrong2, rpcSecurity] = GetParam();
- auto server = RpcServer::make(newFactory(rpcSecurity));
+ auto server = RpcServer::make(newTlsFactory(rpcSecurity));
auto binder1 = sp<BBinder>::make();
IBinder* binderRaw1 = binder1.get();
setRootObject(isStrong1)(server.get(), binder1);
@@ -1468,37 +1284,17 @@
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 +1336,84 @@
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(newTlsFactory(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(newTlsFactory(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(newTlsFactory(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 rpcServer = RpcServer::make(newFactory(rpcSecurity));
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
+ auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity));
+ rpcServer->setProtocolVersion(serverVersion);
switch (socketType) {
case SocketType::PRECONNECTED: {
return AssertionFailure() << "Not supported by this test";
@@ -1574,9 +1429,35 @@
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,11 +1484,14 @@
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";
- mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
+ if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
+ mCtx = newTlsFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
mSetup = true;
return AssertionSuccess();
@@ -1621,14 +1505,33 @@
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 +1539,8 @@
}
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 +1558,9 @@
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 +1581,8 @@
FdTrigger* fdTrigger) {
std::string message(kMessage);
iovec messageIov{message.data(), message.size()};
- auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
+ std::nullopt, nullptr);
if (status != OK) return AssertionFailure() << statusToString(status);
return AssertionSuccess();
}
@@ -1687,9 +1593,10 @@
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();
+ mCtx = newTlsFactory(rpcSecurity, mCertVerifier)->newClientCtx();
if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
return AssertionSuccess();
}
@@ -1700,7 +1607,7 @@
// 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 +1615,9 @@
LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed");
std::string readMessage(expectedMessage.size(), '\0');
iovec readMessageIov{readMessage.data(), readMessage.size()};
- status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(),
- &readMessageIov, 1, {});
+ status_t readStatus =
+ mClientTransport->interruptableReadFully(mFdTrigger.get(), &readMessageIov, 1,
+ std::nullopt, nullptr);
if (readStatus != OK) {
return AssertionFailure() << statusToString(readStatus);
}
@@ -1728,9 +1636,11 @@
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 +1667,28 @@
using Server = RpcTransportTestUtils::Server;
using Client = RpcTransportTestUtils::Client;
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [socketType, rpcSecurity, certificateFormat] = info.param;
- auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param;
+ auto ret = PrintToString(socketType) + "_" + newTlsFactory(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 +1696,15 @@
}
template <typename A, typename B>
status_t trust(const A& a, const B& b) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b);
}
+ void SetUp() override {
+ if constexpr (!kEnableRpcThreads) {
+ GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+ }
+ }
};
TEST_P(RpcTransportTest, GoodCertificate) {
@@ -1817,7 +1738,8 @@
}
TEST_P(RpcTransportTest, UntrustedServer) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
auto untrustedServer = std::make_unique<Server>();
ASSERT_TRUE(untrustedServer->setUp(GetParam()));
@@ -1835,7 +1757,9 @@
client.run(handshakeOk);
}
TEST_P(RpcTransportTest, MaliciousServer) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
+
auto validServer = std::make_unique<Server>();
ASSERT_TRUE(validServer->setUp(GetParam()));
@@ -1858,7 +1782,9 @@
}
TEST_P(RpcTransportTest, UntrustedClient) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
+
auto server = std::make_unique<Server>();
ASSERT_TRUE(server->setUp(GetParam()));
@@ -1877,7 +1803,9 @@
}
TEST_P(RpcTransportTest, MaliciousClient) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam();
+ (void)serverVersion;
+
auto server = std::make_unique<Server>();
ASSERT_TRUE(server->setUp(GetParam()));
@@ -1904,7 +1832,8 @@
auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
std::string message(RpcTransportTestUtils::kMessage);
iovec messageIov{message.data(), message.size()};
- auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
+ std::nullopt, nullptr);
if (status != OK) return AssertionFailure() << statusToString(status);
{
@@ -1915,7 +1844,8 @@
}
iovec msg2Iov{msg2.data(), msg2.size()};
- status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, {});
+ status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt,
+ nullptr);
if (status != DEAD_OBJECT)
return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
"should return DEAD_OBJECT, but it is "
@@ -1957,28 +1887,83 @@
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 +1978,8 @@
auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat);
auto desCert = deserializeCertificate(certData, certificateFormat);
auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert));
- auto utilsParam =
- std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat));
+ auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS,
+ std::make_optional(certificateFormat), serverVersion);
auto server = std::make_unique<RpcTransportTestUtils::Server>();
ASSERT_TRUE(server->setUp(utilsParam, std::move(auth)));
@@ -2013,8 +1998,10 @@
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);
+#endif // BINDER_RPC_TO_TRUSTY_TEST
} // namespace android
diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp
new file mode 100644
index 0000000..fe9a5a1
--- /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 0000000..c1364dd
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -0,0 +1,460 @@
+/*
+ * 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;
+ // right now, this can be empty, or length numSessions, where each value
+ // represents the info for the corresponding session, but we should
+ // probably switch this to be a list of sessions options so that other
+ // options can all be specified per session
+ std::vector<size_t> numIncomingConnectionsBySession = {};
+ 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> newTlsFactory(
+ 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);
+ }
+
+ Status blockingSendIntOneway(int /*n*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+
+ Status blockingRecvInt(int* /*n*/) 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 0000000..6cde9f7
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -0,0 +1,175 @@
+/*
+ * 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;
+
+ // TODO(b/271830568): fix this in binderRpcTest, we always use the first session to cause the
+ // remote process to shutdown. Normally, when we shutdown, the default in the destructor is to
+ // check that there are no leaks and shutdown. However, when there are incoming threadpools,
+ // there will be a few extra binder threads there, so we can't shutdown the server. We should
+ // consider an alternative way of doing the test so that we don't need this, some ideas, such as
+ // program in understanding of incoming threadpool into the destructor so that (e.g.
+ // intelligently wait for sessions to shutdown now that they will do this)
+ void forceShutdown() {
+ if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+ EXPECT_TRUE(proc->sessions.at(0).session->shutdownAndWait(true));
+ expectAlreadyShutdown = true;
+ }
+
+ 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) {
+ 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";
+ } else {
+ ret += "_multi_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
+ }
+ return ret;
+ }
+
+protected:
+ static std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity);
+
+ 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 0000000..a9736d5
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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();
+ }
+
+ HandoffChannel<int> mIntChannel;
+
+ Status blockingSendIntOneway(int n) override {
+ mIntChannel.write(n);
+ return Status::ok();
+ }
+
+ Status blockingRecvInt(int* n) override {
+ *n = mIntChannel.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(newTlsFactory(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 0000000..8557389
--- /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 0000000..28be10d
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW:
+ return RpcTransportCtxFactoryTipcTrusty::make();
+ default:
+ LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
+ }
+}
+
+// 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(std::any_of(options.numIncomingConnectionsBySession.begin(),
+ options.numIncomingConnectionsBySession.end(),
+ [](size_t n) { return n != 0; }),
+ "Non-zero incoming connections on Trusty");
+
+ RpcSecurity rpcSecurity = std::get<1>(GetParam());
+ uint32_t clientVersion = std::get<2>(GetParam());
+ uint32_t serverVersion = std::get<3>(GetParam());
+
+ auto ret = std::make_unique<TrustyProcessSession>();
+
+ status_t status;
+ for (size_t i = 0; i < options.numSessions; i++) {
+ auto session = android::RpcSession::make(newFactory(rpcSecurity));
+
+ EXPECT_TRUE(session->setProtocolVersion(clientVersion));
+ session->setMaxOutgoingConnections(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 0000000..1f46010
--- /dev/null
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -0,0 +1,518 @@
+/*
+ * 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,
+ .numIncomingConnectionsBySession = {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;
+
+ proc.forceShutdown();
+ }
+ }
+ }
+}
+
+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 4fcf42d..3dab2c7 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -233,11 +233,15 @@
"0100000025000000|03000000|00000000|ffffffff|03000000|00000000|00000000|"
"07000000020000003a0044000000000000000000|f8ffffff020000003a002f00000000000000000008000000";
+TEST(RpcWire, V0) {
+ checkRepr(kCurrentRepr, 0);
+}
+
TEST(RpcWire, CurrentVersion) {
checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION);
}
-static_assert(RPC_WIRE_PROTOCOL_VERSION == 0,
+static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL,
"If the binder wire protocol is updated, this test should test additional versions. "
"The binder wire protocol should only be updated on upstream AOSP.");
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 3b1faa8..cfaf2a9 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -249,12 +249,13 @@
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 4330e3e..25e286c 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -37,17 +37,24 @@
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 @@
// ~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 @@
// ~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 e5d32da..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -7,6 +7,26 @@
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 @@
"libcutils",
"libhidlbase",
"liblog",
+ "binderReadParcelIface-cpp",
+ "binderReadParcelIface-ndk",
],
target: {
@@ -59,12 +81,14 @@
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 47ec776..46d387c 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 @@
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 @@
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 @@
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 @@
},
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,26 @@
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;
+ std::string toString = genericDataParcelable.toString();
+ FUZZ_LOG() << " toString() result: " << toString;
+ },
};
// 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 5aeb5cc..3a1471e 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 @@
}
#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 @@
// 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,46 @@
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;
+ std::string toString = genericDataParcelable.toString();
+ FUZZ_LOG() << "toString() result: " << toString;
+ },
+ [](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 cf24ab9..d19f25b 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 ee9840f..438e8ae 100644
--- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
@@ -35,19 +35,19 @@
#define PARCEL_READ_OPT_STATUS(T, FUN) \
PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN)
-#define PARCEL_READ_NO_STATUS(T, FUN) \
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
- FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
- T t = p.FUN();\
- FUZZ_LOG() << #T " value: " << t;\
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \
+ T t = p.FUN(); \
+ FUZZ_LOG() << #T " value: " << t; \
}
-#define PARCEL_READ_WITH_STATUS(T, FUN) \
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
- FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
- T t;\
- status_t status = p.FUN(&t);\
- FUZZ_LOG() << #T " status: " << status << " value: " << t;\
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
+ T t; \
+ status_t status = p.FUN(&t); \
+ FUZZ_LOG() << #T " status: " << status << " value: " << t; \
}
// clang-format off
@@ -56,27 +56,30 @@
PARCEL_READ_NO_STATUS(size_t, dataAvail),
PARCEL_READ_NO_STATUS(size_t, dataPosition),
PARCEL_READ_NO_STATUS(size_t, dataCapacity),
- [] (const ::android::hardware::Parcel& p, uint8_t pos) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ // aborts on larger values
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX);
FUZZ_LOG() << "about to setDataPosition: " << pos;
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
- [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
FUZZ_LOG() << "about to enforceInterface";
- std::string interfaceName(length, 'a');
- bool okay = p.enforceInterface(interfaceName.c_str());
+ bool okay = p.enforceInterface(provider.ConsumeRandomLengthString().c_str());
FUZZ_LOG() << "enforceInterface status: " << okay;
},
PARCEL_READ_NO_STATUS(size_t, objectsCount),
- [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ // Read at least a bit. Unbounded allocation would OOM.
+ size_t length = provider.ConsumeIntegralInRange<size_t>(0, 1024);
FUZZ_LOG() << "about to read";
std::vector<uint8_t> data (length);
status_t status = p.read(data.data(), length);
FUZZ_LOG() << "read status: " << status << " data: " << HexString(data.data(), data.size());
},
- [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t length = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to read";
- std::vector<uint8_t> data (length);
const void* inplace = p.readInplace(length);
FUZZ_LOG() << "read status: " << (inplace ? HexString(inplace, length) : "null");
},
@@ -91,14 +94,14 @@
PARCEL_READ_OPT_STATUS(float, readFloat),
PARCEL_READ_OPT_STATUS(double, readDouble),
PARCEL_READ_OPT_STATUS(bool, readBool),
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readCString";
const char* str = p.readCString();
FUZZ_LOG() << "readCString " << (str ? str : "<null>");
},
PARCEL_READ_OPT_STATUS(::android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16),
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readString16Inplace";
size_t outSize = 0;
const char16_t* str = p.readString16Inplace(&outSize);
@@ -106,7 +109,8 @@
},
PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readBuffer";
size_t handle = 0;
const void* data = nullptr;
@@ -116,7 +120,8 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readNullableBuffer";
size_t handle = 0;
const void* data = nullptr;
@@ -126,7 +131,8 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readEmbeddedBuffer";
size_t handle = 0;
size_t parent_buffer_handle = 0;
@@ -138,7 +144,8 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) {
+ size_t size = provider.ConsumeIntegral<size_t>();
FUZZ_LOG() << "about to readNullableEmbeddedBuffer";
size_t handle = 0;
size_t parent_buffer_handle = 0;
@@ -150,7 +157,7 @@
// should be null since we don't create any IPC objects
CHECK(data == nullptr) << data;
},
- [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to readNativeHandleNoDup";
const native_handle_t* handle = nullptr;
status_t status = p.readNativeHandleNoDup(&handle);
diff --git a/libs/binder/tests/parcel_fuzzer/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 0000000..8fc9263
--- /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 843b6e3..6ea9708 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 459fb12..27587a9 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 @@
/**
* 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 d5aa353..8bef33f 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 @@
.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 @@
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 @@
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 462ef9a..a1fb701 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 @@
}
} // 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 f435dae..bef4ab6 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 @@
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 @@
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 @@
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 @@
doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
std::move(provider));
},
+ [](FuzzedDataProvider&& provider) {
+ doAppendFuzz<::android::Parcel>("binder", std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider));
+ },
};
provider.PickValueInArray(fuzzBackend)(std::move(provider));
diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
index b68a8a9..765a93e 100644
--- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
+++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
@@ -15,5 +15,9 @@
*/
#pragma once
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <functional>
+
template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>;
diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
new file mode 100644
index 0000000..1216250
--- /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 0000000..dd08f72
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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 {
+ enum JustSomeEnum {
+ SOME_ENUMERATOR,
+ ANOTHER_ENUMERATOR,
+ MAYBE_ONE_MORE_ENUMERATOR,
+ }
+
+ const int COOL_CONSTANT = 0x1234;
+
+ int data;
+ float majorVersion;
+ float minorVersion;
+ IBinder binder;
+ ParcelFileDescriptor fileDescriptor;
+ int[] array;
+ String greatString;
+ @utf8InCpp
+ String greaterString;
+ @nullable String nullableString;
+ JustSomeEnum gretEnum = JustSomeEnum.ANOTHER_ENUMERATOR;
+}
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 0000000..0187168
--- /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 0000000..8a1fecb
--- /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 ab0b7e3..e4dbb2d 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 0204f5e..f0beed2 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 @@
},
// 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 0000000..b48dc27
--- /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 0000000..2fb7820
--- /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 a8713a2..b8ae84d 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -133,8 +133,13 @@
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 @@
}
}
- usleep(10000);
-
if (hangupBeforeShutdown) {
connections.clear();
while (!server->listSessions().empty() || server->numUninitializedSessions()) {
@@ -167,6 +170,10 @@
}
}
+ 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 56d958c..0035e4e 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -398,14 +398,13 @@
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/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index 8ea948c..a881582 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -104,3 +104,42 @@
defaults: ["binder_fuzz_defaults"],
srcs: ["MemoryDealerFuzz.cpp"],
}
+
+cc_fuzz {
+ name: "binder_recordedTransactionFileFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["RecordedTransactionFileFuzz.cpp"],
+ corpus: [
+ "recorded_transaction_corpus/*",
+ ],
+}
+
+cc_fuzz {
+ name: "binder_recordedTransactionFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["RecordedTransactionFuzz.cpp"],
+ target: {
+ android: {
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libbase",
+ "libbinder",
+ ],
+ static_libs: ["libbinder_random_parcel"],
+ },
+ host: {
+ static_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libbase",
+ "libbinder",
+ "libbinder_random_parcel",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index e77c55c..910c9dc 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -49,6 +49,7 @@
});
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/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
new file mode 100644
index 0000000..e494366
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <filesystem>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::FILE* intermediateFile = std::tmpfile();
+ fwrite(data, sizeof(uint8_t), size, intermediateFile);
+ rewind(intermediateFile);
+ int fileNumber = fileno(intermediateFile);
+
+ android::base::unique_fd fd(dup(fileNumber));
+
+ auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
+
+ std::fclose(intermediateFile);
+
+ if (transaction.has_value()) {
+ intermediateFile = std::tmpfile();
+
+ android::base::unique_fd fdForWriting(fileno(intermediateFile));
+ auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+ std::fclose(intermediateFile);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
new file mode 100644
index 0000000..943fb9f
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <fuzzbinder/random_parcel.h>
+#include <filesystem>
+#include <string>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::fillRandomParcel;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+ android::String16 interfaceName =
+ android::String16(provider.ConsumeRandomLengthString().c_str());
+
+ uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+ time_t sec = provider.ConsumeIntegral<time_t>();
+ long nsec = provider.ConsumeIntegral<long>();
+ timespec timestamp = {.tv_sec = sec, .tv_nsec = nsec};
+ android::status_t transactionStatus = provider.ConsumeIntegral<android::status_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
+ android::RandomParcelOptions options;
+
+ android::Parcel p0, p1;
+ fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
+ fillRandomParcel(&p1, std::move(provider), &options);
+
+ auto transaction =
+ android::binder::debug::RecordedTransaction::fromDetails(interfaceName, code, flags,
+ timestamp, p0, p1,
+ transactionStatus);
+
+ if (transaction.has_value()) {
+ std::FILE* intermediateFile = std::tmpfile();
+ android::base::unique_fd fdForWriting(fileno(intermediateFile));
+ auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+ std::fclose(intermediateFile);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
new file mode 100644
index 0000000..79442078
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
Binary files differ
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
new file mode 100644
index 0000000..658addb
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
Binary files differ
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
new file mode 100644
index 0000000..8ec9823
--- /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 0000000..8a60af8
--- /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 0000000..3a99606
--- /dev/null
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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) {
+ auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
+ if (channelContext == nullptr) {
+ // Connections marked "incoming" (outgoing from the server's side)
+ // do not have a valid channel context because joinFn does not get
+ // called for them. We ignore them here.
+ return;
+ }
+
+ auto& session = channelContext->session;
+ (void)session->shutdownAndWait(false);
+}
+
+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 0000000..d249b2e
--- /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 0000000..b1caf61
--- /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 0000000..fcb43f8
--- /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 0000000..1afd324
--- /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 0000000..d8b080f
--- /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 0000000..975f689
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -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.
+#
+
+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 \
+
+# TEST_P tests from binderRpcUniversalTests.cpp don't get linked in
+# unless we pass in --whole-archive to the linker (b/275620340).
+MODULE_USE_WHOLE_ARCHIVE := true
+
+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 0000000..1c4f7ee
--- /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 0000000..5d1a51d
--- /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 0000000..d0a1fbc
--- /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 0000000..6678eb8
--- /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 0000000..8eae8c2
--- /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 0000000..de84617
--- /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 0000000..ead9f9c
--- /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 0000000..2747314
--- /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 0000000..ab3fbba
--- /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 0000000..07dcc1c
--- /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 0000000..046b403
--- /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 0000000..db044c2
--- /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 0000000..d51e752
--- /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 0000000..c7e117e
--- /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 0000000..f636826
--- /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 0000000..ab7a50d
--- /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 0000000..b4243af
--- /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 0000000..6a48d2b
--- /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 0000000..03fd006
--- /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 0000000..42db29a
--- /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 0000000..02df8af
--- /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 0000000..1300121
--- /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/Android.bp b/libs/binderdebug/Android.bp
index 3eeaf3e..1454727 100644
--- a/libs/binderdebug/Android.bp
+++ b/libs/binderdebug/Android.bp
@@ -21,6 +21,17 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+cc_benchmark {
+ name: "binder_thread_stats",
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ "libbase",
+ ],
+ static_libs: ["libbinderdebug"],
+ srcs: ["stats.cpp"],
+}
+
cc_library {
name: "libbinderdebug",
vendor_available: true,
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
index d086b49..a8f2cbf 100644
--- a/libs/binderdebug/BinderDebug.cpp
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -48,14 +48,12 @@
return -errno;
}
}
- static const std::regex kContextLine("^context (\\w+)$");
bool isDesiredContext = false;
std::string line;
- std::smatch match;
while (getline(ifs, line)) {
- if (std::regex_search(line, match, kContextLine)) {
- isDesiredContext = match.str(1) == contextName;
+ if (base::StartsWith(line, "context")) {
+ isDesiredContext = base::Split(line, " ").back() == contextName;
continue;
}
if (!isDesiredContext) {
@@ -66,57 +64,72 @@
return OK;
}
+// Examples of what we are looking at:
+// node 66730: u00007590061890e0 c0000759036130950 pri 0:120 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 2300 1790
+// thread 2999: l 00 need_return 1 tr 0
status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo) {
std::smatch match;
static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
std::string contextStr = contextToString(context);
status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
- if (std::regex_search(line, match, kReferencePrefix)) {
- const std::string& ptrString = "0x" + match.str(2); // use number after c
- uint64_t ptr;
- if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
- // Should not reach here, but just be tolerant.
- return;
- }
- const std::string proc = " proc ";
- auto pos = line.rfind(proc);
- if (pos != std::string::npos) {
- for (const std::string& pidStr : base::Split(line.substr(pos + proc.size()), " ")) {
- int32_t pid;
- if (!::android::base::ParseInt(pidStr, &pid)) {
+ if (base::StartsWith(line, " node")) {
+ std::vector<std::string> splitString = base::Tokenize(line, " ");
+ bool pids = false;
+ uint64_t ptr = 0;
+ for (const auto& token : splitString) {
+ if (base::StartsWith(token, "u")) {
+ const std::string ptrString = "0x" + token.substr(1);
+ if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+ LOG(ERROR) << "Failed to parse pointer: " << ptrString;
return;
}
- pidInfo->refPids[ptr].push_back(pid);
+ } else {
+ // The last numbers in the line after "proc" are all client PIDs
+ if (token == "proc") {
+ pids = true;
+ } else if (pids) {
+ int32_t pid;
+ if (!::android::base::ParseInt(token, &pid)) {
+ LOG(ERROR) << "Failed to parse pid int: " << token;
+ return;
+ }
+ if (ptr == 0) {
+ LOG(ERROR) << "We failed to parse the pointer, so we can't add the refPids";
+ return;
+ }
+ pidInfo->refPids[ptr].push_back(pid);
+ }
}
}
+ } else if (base::StartsWith(line, " thread")) {
+ auto pos = line.find("l ");
+ if (pos != std::string::npos) {
+ // "1" is waiting in binder driver
+ // "2" is poll. It's impossible to tell if these are in use.
+ // and HIDL default code doesn't use it.
+ bool isInUse = line.at(pos + 2) != '1';
+ // "0" is a thread that has called into binder
+ // "1" is looper thread
+ // "2" is main looper thread
+ bool isBinderThread = line.at(pos + 3) != '0';
+ if (!isBinderThread) {
+ return;
+ }
+ if (isInUse) {
+ pidInfo->threadUsage++;
+ }
- return;
- }
- if (std::regex_search(line, match, kThreadPrefix)) {
- // "1" is waiting in binder driver
- // "2" is poll. It's impossible to tell if these are in use.
- // and HIDL default code doesn't use it.
- bool isInUse = match.str(1) != "1";
- // "0" is a thread that has called into binder
- // "1" is looper thread
- // "2" is main looper thread
- bool isBinderThread = match.str(2) != "0";
- if (!isBinderThread) {
- return;
+ pidInfo->threadCount++;
}
- if (isInUse) {
- pidInfo->threadUsage++;
- }
-
- pidInfo->threadCount++;
- return;
}
- return;
});
return ret;
}
+// Examples of what we are looking at:
+// ref 52493: desc 910 node 52492 s 1 w 1 d 0000000000000000
+// node 29413: u00007803fc982e80 c000078042c982210 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 488 683
status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid,
int32_t handle, std::vector<pid_t>* pids) {
std::smatch match;
@@ -124,51 +137,64 @@
std::string contextStr = contextToString(context);
int32_t node;
status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
- if (std::regex_search(line, match, kNodeNumber)) {
- const std::string& descString = match.str(1);
- int32_t desc;
- if (!::android::base::ParseInt(descString.c_str(), &desc)) {
- LOG(ERROR) << "Failed to parse desc int: " << descString;
- return;
- }
- if (handle != desc) {
- return;
- }
- const std::string& nodeString = match.str(2);
- if (!::android::base::ParseInt(nodeString.c_str(), &node)) {
- LOG(ERROR) << "Failed to parse node int: " << nodeString;
- return;
- }
+ if (!base::StartsWith(line, " ref")) return;
+
+ std::vector<std::string> splitString = base::Tokenize(line, " ");
+ if (splitString.size() < 12) {
+ LOG(ERROR) << "Failed to parse binder_logs ref entry. Expecting size greater than 11, but got: " << splitString.size();
return;
}
- return;
+ int32_t desc;
+ if (!::android::base::ParseInt(splitString[3].c_str(), &desc)) {
+ LOG(ERROR) << "Failed to parse desc int: " << splitString[3];
+ return;
+ }
+ if (handle != desc) {
+ return;
+ }
+ if (!::android::base::ParseInt(splitString[5].c_str(), &node)) {
+ LOG(ERROR) << "Failed to parse node int: " << splitString[5];
+ return;
+ }
+ LOG(INFO) << "Parsed the node: " << node;
});
if (ret != OK) {
return ret;
}
- static const std::regex kClients("^\\s+node\\s+(\\d+).*proc\\s+([\\d+\\s*]*)");
+
ret = scanBinderContext(servicePid, contextStr, [&](const std::string& line) {
- if (std::regex_search(line, match, kClients)) {
- const std::string nodeString = match.str(1);
- int32_t matchedNode;
- if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
- LOG(ERROR) << "Failed to parse node int: " << nodeString;
- return;
- }
- if (node != matchedNode) {
- return;
- }
- const std::string clients = match.str(2);
- for (const std::string& pidStr : base::Split(clients, " ")) {
+ if (!base::StartsWith(line, " node")) return;
+
+ std::vector<std::string> splitString = base::Tokenize(line, " ");
+ if (splitString.size() < 21) {
+ LOG(ERROR) << "Failed to parse binder_logs node entry. Expecting size greater than 20, but got: " << splitString.size();
+ return;
+ }
+
+ // remove the colon
+ const std::string nodeString = splitString[1].substr(0, splitString[1].size() - 1);
+ int32_t matchedNode;
+ if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
+ LOG(ERROR) << "Failed to parse node int: " << nodeString;
+ return;
+ }
+
+ if (node != matchedNode) {
+ return;
+ }
+ bool pidsSection = false;
+ for (const auto& token : splitString) {
+ if (token == "proc") {
+ pidsSection = true;
+ } else if (pidsSection == true) {
int32_t pid;
- if (!::android::base::ParseInt(pidStr, &pid)) {
+ if (!::android::base::ParseInt(token.c_str(), &pid)) {
+ LOG(ERROR) << "Failed to parse PID int: " << token;
return;
}
pids->push_back(pid);
}
- return;
}
- return;
});
return ret;
}
diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h
index dfd5a7c..6ce8edf 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/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp
new file mode 100644
index 0000000..9c26afa
--- /dev/null
+++ b/libs/binderdebug/stats.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+#include <binderdebug/BinderDebug.h>
+#include <utils/Errors.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+extern "C" int main() {
+ // ignore args - we only print csv
+
+ // we should use a csv library here for escaping, because
+ // the name is coming from another process
+ printf("name,binder_threads_in_use,binder_threads_started,client_count\n");
+
+ for (const String16& name : defaultServiceManager()->listServices()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(name);
+ if (binder == nullptr) {
+ fprintf(stderr, "%s is null", String8(name).c_str());
+ continue;
+ }
+
+ BpBinder* remote = binder->remoteBinder();
+ const auto handle = remote->getDebugBinderHandle();
+ CHECK(handle != std::nullopt);
+
+ pid_t pid;
+ CHECK_EQ(OK, binder->getDebugPid(&pid));
+
+ BinderPidInfo info;
+ CHECK_EQ(OK, getBinderPidInfo(BinderDebugContext::BINDER, pid, &info));
+
+ std::vector<pid_t> clientPids;
+ CHECK_EQ(OK,
+ getBinderClientPids(BinderDebugContext::BINDER, getpid(), pid, *handle,
+ &clientPids));
+
+ printf("%s,%" PRIu32 ",%" PRIu32 ",%zu\n", String8(name).c_str(), info.threadUsage,
+ info.threadCount, clientPids.size());
+ }
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
index a3e5026..54259d2 100644
--- a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
+++ b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
@@ -36,8 +36,12 @@
// Based on where we are in recursion of nested binder/hwbinder calls, determine
// which one we are closer to.
inline static BinderCallType getCurrentServingCall() {
- const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer();
- const void* binderSp = android::IPCThreadState::self()->getServingStackPointer();
+ auto* hwState = android::hardware::IPCThreadState::selfOrNull();
+ auto* state = android::IPCThreadState::selfOrNull();
+
+ // getServingStackPointer can also return nullptr
+ const void* hwbinderSp = hwState ? hwState->getServingStackPointer() : nullptr;
+ const void* binderSp = state ? state->getServingStackPointer() : nullptr;
if (hwbinderSp == nullptr && binderSp == nullptr) return BinderCallType::NONE;
if (hwbinderSp == nullptr) return BinderCallType::BINDER;
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index 44e2fd1..b5c4010 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -16,11 +16,16 @@
#include <BnAidlStuff.h>
#include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binderthreadstate/CallerUtils.h>
#include <binderthreadstateutilstest/1.0/IHidlStuff.h>
#include <gtest/gtest.h>
#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+
+#include <thread>
+
#include <linux/prctl.h>
#include <sys/prctl.h>
@@ -68,8 +73,22 @@
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 +98,25 @@
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 +127,24 @@
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();
}
};
@@ -133,6 +159,20 @@
EXPECT_TRUE(server->callLocal().isOk());
}
+TEST(BinderThreadState, DoesntInitializeBinderDriver) {
+ // this is on another thread, because it's testing thread-specific
+ // state and we expect it not to be initialized.
+ std::thread([&] {
+ EXPECT_EQ(nullptr, android::IPCThreadState::selfOrNull());
+ EXPECT_EQ(nullptr, android::hardware::IPCThreadState::selfOrNull());
+
+ (void)getCurrentServingCall();
+
+ EXPECT_EQ(nullptr, android::IPCThreadState::selfOrNull());
+ EXPECT_EQ(nullptr, android::hardware::IPCThreadState::selfOrNull());
+ }).join();
+}
+
TEST(BindThreadState, RemoteHidlCall) {
auto stuff = IHidlStuff::getService(id2name(kP1Id));
ASSERT_NE(nullptr, stuff);
@@ -161,13 +201,14 @@
// 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 7e9bb7d..706704a 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -131,24 +131,24 @@
}
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 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 @@
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 @@
}
}
- 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 @@
}
}
- 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::vector<tis_val_t> vals(gNCpus);
- time_key_t key = {.uid = uid};
for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
- key.bucket = i;
+ const time_key_t key = {.uid = uid, .bucket = i};
if (findMapEntry(gTisMapFd, &key, vals.data())) {
- if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {};
+ time_key_t tmpKey;
+ if (errno != ENOENT || getFirstMapKey(gTisMapFd, &tmpKey)) return {};
continue;
}
@@ -412,10 +412,11 @@
concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)};
for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0);
std::vector<concurrent_val_t> vals(gNCpus);
- time_key_t key = {.uid = uid};
- for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
+ for (uint32_t i = 0; i <= (gNCpus - 1) / CPUS_PER_ENTRY; ++i) {
+ const time_key_t key = {.uid = uid, .bucket = i};
if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
- if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {};
+ time_key_t tmpKey;
+ if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &tmpKey)) return {};
continue;
}
auto offset = key.bucket * CPUS_PER_ENTRY;
@@ -559,10 +560,10 @@
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 @@
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 0000000..2399acd
--- /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 0000000..f835997
--- /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 45a6d47..6ccc6ca 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -462,7 +462,7 @@
++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 @@
{
// 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 @@
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 3551a8f..067ce17 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -85,8 +85,20 @@
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
+ "android.hardware.automotive.audiocontrol.IAudioControl",
+ "android.hardware.automotive.evs.IEvsEnumerator",
+ "android.hardware.biometrics.face.IBiometricsFace",
+ "android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
"android.hardware.camera.provider.ICameraProvider",
+ "android.hardware.drm.IDrmFactory",
+ "android.hardware.graphics.allocator.IAllocator",
+ "android.hardware.graphics.composer3.IComposer",
+ "android.hardware.health.IHealth",
"android.hardware.input.processor.IInputProcessor",
+ "android.hardware.neuralnetworks.IDevice",
+ "android.hardware.power.IPower",
+ "android.hardware.power.stats.IPowerStats",
+ "android.hardware.sensors.ISensors",
};
/* list of extra hal interfaces to dump containing process during native dumps */
@@ -196,5 +208,5 @@
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 47c0657..96dcce1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -11,7 +11,7 @@
name: "fakeservicemanager_defaults",
host_supported: true,
srcs: [
- "ServiceManager.cpp",
+ "FakeServiceManager.cpp",
],
shared_libs: [
@@ -28,6 +28,7 @@
cc_library {
name: "libfakeservicemanager",
defaults: ["fakeservicemanager_defaults"],
+ export_include_dirs: ["include"],
}
cc_test_host {
@@ -37,4 +38,5 @@
"test_sm.cpp",
],
static_libs: ["libgmock"],
+ local_include_dirs: ["include"],
}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
similarity index 62%
rename from libs/fakeservicemanager/ServiceManager.cpp
rename to libs/fakeservicemanager/FakeServiceManager.cpp
index 6c6d7f3..3272bbc 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
namespace android {
-ServiceManager::ServiceManager() {}
+FakeServiceManager::FakeServiceManager() {}
-sp<IBinder> ServiceManager::getService( const String16& name) const {
+sp<IBinder> FakeServiceManager::getService( const String16& name) const {
// Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
return checkService(name);
}
-sp<IBinder> ServiceManager::checkService( const String16& name) const {
+sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
auto it = mNameToService.find(name);
if (it == mNameToService.end()) {
return nullptr;
@@ -33,14 +33,17 @@
return it->second;
}
-status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
+ if (service == nullptr) {
+ return UNEXPECTED_NULL;
+ }
mNameToService[name] = service;
return NO_ERROR;
}
-Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
Vector<String16> services;
for (auto const& [name, service] : mNameToService) {
(void) service;
@@ -49,19 +52,19 @@
return services;
}
-IBinder* ServiceManager::onAsBinder() {
+IBinder* FakeServiceManager::onAsBinder() {
return nullptr;
}
-sp<IBinder> ServiceManager::waitForService(const String16& name) {
+sp<IBinder> FakeServiceManager::waitForService(const String16& name) {
return checkService(name);
}
-bool ServiceManager::isDeclared(const String16& name) {
+bool FakeServiceManager::isDeclared(const String16& name) {
return mNameToService.find(name) != mNameToService.end();
}
-Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
Vector<String16> out;
const String16 prefix = name + String16("/");
for (const auto& [registeredName, service] : mNameToService) {
@@ -73,29 +76,38 @@
return out;
}
-std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+std::optional<String16> FakeServiceManager::updatableViaApex(const String16& name) {
(void)name;
return std::nullopt;
}
-std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
+Vector<String16> FakeServiceManager::getUpdatableNames(const String16& apexName) {
+ (void)apexName;
+ return {};
+}
+
+std::optional<IServiceManager::ConnectionInfo> FakeServiceManager::getConnectionInfo(
const String16& name) {
(void)name;
return std::nullopt;
}
-status_t ServiceManager::registerForNotifications(const String16&,
+status_t FakeServiceManager::registerForNotifications(const String16&,
const sp<LocalRegistrationCallback>&) {
return INVALID_OPERATION;
}
-status_t ServiceManager::unregisterForNotifications(const String16&,
+status_t FakeServiceManager::unregisterForNotifications(const String16&,
const sp<LocalRegistrationCallback>&) {
return INVALID_OPERATION;
}
-std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() {
+std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDebugInfo() {
std::vector<IServiceManager::ServiceDebugInfo> ret;
return ret;
}
+
+void FakeServiceManager::clear() {
+ mNameToService.clear();
+}
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
similarity index 90%
rename from libs/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index e0af5d4..97add24 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -28,9 +28,9 @@
* A local host simple implementation of IServiceManager, that does not
* communicate over binder.
*/
-class ServiceManager : public IServiceManager {
+class FakeServiceManager : public IServiceManager {
public:
- ServiceManager();
+ FakeServiceManager();
sp<IBinder> getService( const String16& name) const override;
@@ -52,6 +52,8 @@
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 @@
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 71e5abe..6fc21c6 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -21,14 +21,14 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
using android::sp;
using android::BBinder;
using android::IBinder;
using android::OK;
using android::status_t;
-using android::ServiceManager;
+using android::FakeServiceManager;
using android::String16;
using android::IServiceManager;
using testing::ElementsAre;
@@ -45,21 +45,36 @@
}
TEST(AddService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
+TEST(AddService, SadNullBinder) {
+ auto sm = new FakeServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
+}
+
TEST(AddService, HappyOverExistingService) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
+TEST(AddService, HappyClearAddedService) {
+ auto sm = new FakeServiceManager();
+ 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();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -69,13 +84,13 @@
}
TEST(GetService, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->getService(String16("foo")), nullptr);
}
TEST(ListServices, AllServices) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
@@ -94,13 +109,13 @@
}
TEST(WaitForService, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
}
TEST(WaitForService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -110,13 +125,13 @@
}
TEST(IsDeclared, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_FALSE(sm->isDeclared(String16("foo")));
}
TEST(IsDeclared, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index c010a2e..09422d3 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -10,9 +10,6 @@
cc_test {
name: "ftl_test",
test_suites: ["device-tests"],
- sanitize: {
- address: true,
- },
srcs: [
"cast_test.cpp",
"concat_test.cpp",
diff --git a/libs/ftl/TEST_MAPPING b/libs/ftl/TEST_MAPPING
new file mode 100644
index 0000000..ec0c671
--- /dev/null
+++ b/libs/ftl/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "ftl_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "ftl_test"
+ }
+ ]
+}
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index bd21fba..f5af425 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -51,14 +51,14 @@
],
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 e86881a..5f5f85a 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -377,26 +377,61 @@
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 @@
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 8c28464..1db8cbe 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,4 +1,4 @@
chrisforbes@google.com
cnorthrop@google.com
+ianelliott@google.com
lpy@google.com
-timvp@google.com
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 098e4a6..73d3196 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -91,17 +91,28 @@
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 @@
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 d634c58..2ac1174 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -170,14 +170,13 @@
],
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 @@
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 @@
"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 @@
min_sdk_version: "29",
cflags: [
- "-DNO_BUFFERHUB",
"-DNO_BINDER",
],
@@ -316,7 +293,6 @@
cc_defaults {
name: "libgui_bufferqueue-defaults",
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -337,7 +313,7 @@
},
whole_static_libs: [
- "LibGuiProperties",
+ "libLibGuiProperties",
],
shared_libs: [
@@ -345,7 +321,7 @@
"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 b8ea080..000f458 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,6 +132,11 @@
}
}
+void BLASTBufferItemConsumer::resizeFrameEventHistory(size_t newSize) {
+ Mutex::Autolock lock(mMutex);
+ mFrameEventHistory.resize(newSize);
+}
+
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
: mSurfaceControl(nullptr),
mSize(1, 1),
@@ -429,8 +434,8 @@
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()) {
@@ -1053,8 +1058,9 @@
// can be non-blocking when the producer is in the client process.
class BBQBufferQueueProducer : public BufferQueueProducer {
public:
- BBQBufferQueueProducer(const sp<BufferQueueCore>& core)
- : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
+ BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq)
+ : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/),
+ mBLASTBufferQueue(std::move(bbq)) {}
status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
QueueBufferOutput* output) override {
@@ -1066,6 +1072,26 @@
producerControlledByApp, output);
}
+ // We want to resize the frame history when changing the size of the buffer queue
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override {
+ int maxBufferCount;
+ status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
+ &maxBufferCount);
+ // if we can't determine the max buffer count, then just skip growing the history size
+ if (status == OK) {
+ size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
+ // optimize away resizing the frame history unless it will grow
+ if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (bbq != nullptr) {
+ ALOGV("increasing frame history size to %zu", newFrameHistorySize);
+ bbq->resizeFrameEventHistory(newFrameHistorySize);
+ }
+ }
+ }
+ return status;
+ }
+
int query(int what, int* value) override {
if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
*value = 1;
@@ -1073,6 +1099,9 @@
}
return BufferQueueProducer::query(what, value);
}
+
+private:
+ const wp<BLASTBufferQueue> mBLASTBufferQueue;
};
// Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
@@ -1087,7 +1116,7 @@
sp<BufferQueueCore> core(new BufferQueueCore());
LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
- sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
+ sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
LOG_ALWAYS_FATAL_IF(producer == nullptr,
"BLASTBufferQueue: failed to create BBQBufferQueueProducer");
@@ -1100,6 +1129,16 @@
*outConsumer = consumer;
}
+void BLASTBufferQueue::resizeFrameEventHistory(size_t newSize) {
+ // This can be null during creation of the buffer queue, but resizing won't do anything at that
+ // point in time, so just ignore. This can go away once the class relationships and lifetimes of
+ // objects are cleaned up with a major refactor of BufferQueue as a whole.
+ if (mBufferItemConsumer != nullptr) {
+ std::unique_lock _lock{mMutex};
+ mBufferItemConsumer->resizeFrameEventHistory(newSize);
+ }
+}
+
PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) {
PixelFormat convertedFormat = format;
switch (format) {
diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp
deleted file mode 100644
index b5cdeb2..0000000
--- 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 1f71e23..0000000
--- 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 c1d92a2..66cad03 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 @@
*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/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5fe5e71..9eb1a9f 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -119,6 +119,12 @@
status_t BufferQueueProducer::setMaxDequeuedBufferCount(
int maxDequeuedBuffers) {
+ int maxBufferCount;
+ return setMaxDequeuedBufferCount(maxDequeuedBuffers, &maxBufferCount);
+}
+
+status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers,
+ int* maxBufferCount) {
ATRACE_CALL();
BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
maxDequeuedBuffers);
@@ -134,6 +140,8 @@
return NO_INIT;
}
+ *maxBufferCount = mCore->getMaxBufferCountLocked();
+
if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) {
return NO_ERROR;
}
@@ -183,6 +191,7 @@
return BAD_VALUE;
}
mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+ *maxBufferCount = mCore->getMaxBufferCountLocked();
VALIDATE_CONSISTENCY();
if (delta < 0) {
listener = mCore->mConsumerListener;
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index e2ea3f9..f3eb4e8 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -168,10 +168,11 @@
} // namespace
-const size_t FrameEventHistory::MAX_FRAME_HISTORY =
+const size_t FrameEventHistory::INITIAL_MAX_FRAME_HISTORY =
sysprop::LibGuiProperties::frame_event_history_size().value_or(8);
-FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {}
+FrameEventHistory::FrameEventHistory()
+ : mFrames(std::vector<FrameEvents>(INITIAL_MAX_FRAME_HISTORY)) {}
FrameEventHistory::~FrameEventHistory() = default;
@@ -227,7 +228,6 @@
}
}
-
// ============================================================================
// ProducerFrameEventHistory
// ============================================================================
@@ -273,6 +273,13 @@
const FrameEventHistoryDelta& delta) {
mCompositorTiming = delta.mCompositorTiming;
+ // Deltas should have enough reserved capacity for the consumer-side, therefore if there's a
+ // different capacity, we re-sized on the consumer side and now need to resize on the producer
+ // side.
+ if (delta.mDeltas.capacity() > mFrames.capacity()) {
+ resize(delta.mDeltas.capacity());
+ }
+
for (auto& d : delta.mDeltas) {
// Avoid out-of-bounds access.
if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
@@ -349,13 +356,48 @@
return std::make_shared<FenceTime>(fence);
}
+void ProducerFrameEventHistory::resize(size_t newSize) {
+ // we don't want to drop events by resizing too small, so don't resize in the negative direction
+ if (newSize <= mFrames.size()) {
+ return;
+ }
+
+ // This algorithm for resizing needs to be the same as ConsumerFrameEventHistory::resize,
+ // because the indexes need to match when communicating the FrameEventDeltas.
+
+ // We need to find the oldest frame, because that frame needs to move to index 0 in the new
+ // frame history.
+ size_t oldestFrameIndex = 0;
+ size_t oldestFrameNumber = INT32_MAX;
+ for (size_t i = 0; i < mFrames.size(); ++i) {
+ if (mFrames[i].frameNumber < oldestFrameNumber && mFrames[i].valid) {
+ oldestFrameNumber = mFrames[i].frameNumber;
+ oldestFrameIndex = i;
+ }
+ }
+
+ // move the existing frame information into a new vector, so that the oldest frames are at
+ // index 0, and the latest frames are at the end of the vector
+ std::vector<FrameEvents> newFrames(newSize);
+ size_t oldI = oldestFrameIndex;
+ size_t newI = 0;
+ do {
+ if (mFrames[oldI].valid) {
+ newFrames[newI++] = std::move(mFrames[oldI]);
+ }
+ oldI = (oldI + 1) % mFrames.size();
+ } while (oldI != oldestFrameIndex);
+
+ mFrames = std::move(newFrames);
+ mAcquireOffset = 0; // this is just a hint, so setting this to anything is fine
+}
// ============================================================================
// ConsumerFrameEventHistory
// ============================================================================
ConsumerFrameEventHistory::ConsumerFrameEventHistory()
- : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {}
+ : mFramesDirty(std::vector<FrameEventDirtyFields>(INITIAL_MAX_FRAME_HISTORY)) {}
ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
@@ -489,6 +531,36 @@
}
}
+void ConsumerFrameEventHistory::resize(size_t newSize) {
+ // we don't want to drop events by resizing too small, so don't resize in the negative direction
+ if (newSize <= mFrames.size()) {
+ return;
+ }
+
+ // This algorithm for resizing needs to be the same as ProducerFrameEventHistory::resize,
+ // because the indexes need to match when communicating the FrameEventDeltas.
+
+ // move the existing frame information into a new vector, so that the oldest frames are at
+ // index 0, and the latest frames are towards the end of the vector
+ std::vector<FrameEvents> newFrames(newSize);
+ std::vector<FrameEventDirtyFields> newFramesDirty(newSize);
+ size_t oldestFrameIndex = mQueueOffset;
+ size_t oldI = oldestFrameIndex;
+ size_t newI = 0;
+ do {
+ if (mFrames[oldI].valid) {
+ newFrames[newI] = std::move(mFrames[oldI]);
+ newFramesDirty[newI] = mFramesDirty[oldI];
+ newI += 1;
+ }
+ oldI = (oldI + 1) % mFrames.size();
+ } while (oldI != oldestFrameIndex);
+
+ mFrames = std::move(newFrames);
+ mFramesDirty = std::move(newFramesDirty);
+ mQueueOffset = newI;
+ mCompositionOffset = 0; // this is just a hint, so setting this to anything is fine
+}
// ============================================================================
// FrameEventsDelta
@@ -558,8 +630,7 @@
return NO_MEMORY;
}
- if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
- mIndex > std::numeric_limits<uint16_t>::max()) {
+ if (mIndex >= UINT8_MAX || mIndex < 0) {
return BAD_VALUE;
}
@@ -601,7 +672,7 @@
uint16_t temp16 = 0;
FlattenableUtils::read(buffer, size, temp16);
mIndex = temp16;
- if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (mIndex >= UINT8_MAX) {
return BAD_VALUE;
}
uint8_t temp8 = 0;
@@ -627,6 +698,25 @@
return NO_ERROR;
}
+uint64_t FrameEventsDelta::getFrameNumber() const {
+ return mFrameNumber;
+}
+
+bool FrameEventsDelta::getLatchTime(nsecs_t* latchTime) const {
+ if (mLatchTime == FrameEvents::TIMESTAMP_PENDING) {
+ return false;
+ }
+ *latchTime = mLatchTime;
+ return true;
+}
+
+bool FrameEventsDelta::getDisplayPresentFence(sp<Fence>* fence) const {
+ if (mDisplayPresentFence.fence == Fence::NO_FENCE) {
+ return false;
+ }
+ *fence = mDisplayPresentFence.fence;
+ return true;
+}
// ============================================================================
// FrameEventHistoryDelta
@@ -665,7 +755,7 @@
status_t FrameEventHistoryDelta::flatten(
void*& buffer, size_t& size, int*& fds, size_t& count) const {
- if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (mDeltas.size() > UINT8_MAX) {
return BAD_VALUE;
}
if (size < getFlattenedSize()) {
@@ -695,7 +785,7 @@
uint32_t deltaCount = 0;
FlattenableUtils::read(buffer, size, deltaCount);
- if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (deltaCount > UINT8_MAX) {
return BAD_VALUE;
}
mDeltas.resize(deltaCount);
@@ -708,5 +798,12 @@
return NO_ERROR;
}
+std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::begin() const {
+ return mDeltas.begin();
+}
+
+std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::end() const {
+ return mDeltas.end();
+}
} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 797069c..918ff2d 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 @@
}
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 45c958e..05b5533 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -2,9 +2,9 @@
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 4c80792..8f45425 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1107,9 +1107,12 @@
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 @@
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::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::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::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/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 55462c3..9667954 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -725,12 +725,7 @@
// These were written as uint8_t for alignment.
uint8_t temp = 0;
FlattenableUtils::read(buffer, size, temp);
- size_t index = static_cast<size_t>(temp);
- if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
- return BAD_VALUE;
- }
- t->index = static_cast<uint32_t>(index);
-
+ t->index = static_cast<uint32_t>(temp);
FlattenableUtils::read(buffer, size, temp);
t->addPostCompositeCalled = static_cast<bool>(temp);
FlattenableUtils::read(buffer, size, temp);
@@ -786,8 +781,7 @@
status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
void*& buffer, size_t& size, int*& fds, size_t numFds) {
// Check that t.index is within a valid range.
- if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
- || t.index > std::numeric_limits<uint8_t>::max()) {
+ if (t.index > UINT8_MAX || t.index < 0) {
return BAD_VALUE;
}
@@ -887,8 +881,7 @@
uint32_t deltaCount = 0;
FlattenableUtils::read(buffer, size, deltaCount);
- if (static_cast<size_t>(deltaCount) >
- ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (deltaCount > UINT8_MAX) {
return BAD_VALUE;
}
t->deltas.resize(deltaCount);
@@ -919,7 +912,7 @@
status_t flatten(
HGraphicBufferProducer::FrameEventHistoryDelta const& t,
void*& buffer, size_t& size, int*& fds, size_t& numFds) {
- if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (t.deltas.size() > UINT8_MAX) {
return BAD_VALUE;
}
if (size < getFlattenedSize(t)) {
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index cee1b81..f684874 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -725,8 +725,7 @@
std::vector<native_handle_t*>* nh,
void*& buffer, size_t& size, int*& fds, size_t numFds) {
// Check that t.index is within a valid range.
- if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
- || t.index > std::numeric_limits<uint8_t>::max()) {
+ if (t.index > UINT8_MAX || t.index < 0) {
return BAD_VALUE;
}
@@ -829,7 +828,7 @@
HGraphicBufferProducer::FrameEventHistoryDelta const& t,
std::vector<std::vector<native_handle_t*> >* nh,
void*& buffer, size_t& size, int*& fds, size_t& numFds) {
- if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (t.deltas.size() > UINT8_MAX) {
return BAD_VALUE;
}
if (size < getFlattenedSize(t)) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 4007667..40ffea6 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -54,6 +54,8 @@
nsecs_t dequeueReadyTime) REQUIRES(mMutex);
void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
+ void resizeFrameEventHistory(size_t newSize);
+
protected:
void onSidebandStreamChanged() override REQUIRES(mMutex);
@@ -129,6 +131,7 @@
private:
friend class BLASTBufferQueueHelper;
+ friend class BBQBufferQueueProducer;
// can't be copied
BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
@@ -136,6 +139,8 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer);
+ void resizeFrameEventHistory(size_t newSize);
+
status_t acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h
deleted file mode 100644
index d380770..0000000
--- 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 0e925ce..0000000
--- 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 91f80d2..690587f 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -82,12 +82,6 @@
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/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 0ad3075..1d13dab 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -202,6 +202,11 @@
// See IGraphicBufferProducer::setAutoPrerotation
virtual status_t setAutoPrerotation(bool autoPrerotation);
+protected:
+ // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
+ // total maximum buffer count for the buffer queue (dequeued AND acquired)
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index dd3de58..968aa2b 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -114,9 +114,11 @@
void checkFencesForCompletion();
void dump(std::string& outString) const;
- static const size_t MAX_FRAME_HISTORY;
+ static const size_t INITIAL_MAX_FRAME_HISTORY;
protected:
+ void resize(size_t newSize);
+
std::vector<FrameEvents> mFrames;
CompositorTiming mCompositorTiming;
@@ -155,6 +157,8 @@
virtual std::shared_ptr<FenceTime> createFenceTime(
const sp<Fence>& fence) const;
+ void resize(size_t newSize);
+
size_t mAcquireOffset{0};
// The consumer updates it's timelines in Layer and SurfaceFlinger since
@@ -225,6 +229,8 @@
void getAndResetDelta(FrameEventHistoryDelta* delta);
+ void resize(size_t newSize);
+
private:
void getFrameDelta(FrameEventHistoryDelta* delta,
const std::vector<FrameEvents>::iterator& frame);
@@ -267,6 +273,10 @@
status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count);
+ uint64_t getFrameNumber() const;
+ bool getLatchTime(nsecs_t* latchTime) const;
+ bool getDisplayPresentFence(sp<Fence>* fence) const;
+
private:
static constexpr size_t minFlattenedSize();
@@ -327,6 +337,9 @@
status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count);
+ std::vector<FrameEventsDelta>::const_iterator begin() const;
+ std::vector<FrameEventsDelta>::const_iterator end() const;
+
private:
static constexpr size_t minFlattenedSize();
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 8d4e15b..4a552b6 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -111,6 +111,24 @@
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 @@
// 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 e58543a..0702e0f 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -15,7 +15,6 @@
name: "libgui_test",
test_suites: ["device-tests"],
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -75,7 +74,6 @@
name: "libgui_multilib_test",
test_suites: ["device-tests"],
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -92,46 +90,14 @@
compile_multilib: "both",
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",
+ data: [
+ ":libgui_test",
],
}
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 2af2fe1..3427731 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 @@
// 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 @@
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, 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 @@
}
}
-#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 686dc82..0000000
--- 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/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
#define LOG_TAG "Surface"
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
namespace android {
namespace view {
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+ Parcel* _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
+ android::view::Surface shimSurface;
+ shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+ shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+ return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+ const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ // Use a android::view::Surface to unparcel the window
+ android::view::Surface shimSurface;
+ status_t ret = shimSurface.readFromParcel(parcel);
+ if (ret != OK) {
+ ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+ auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+ shimSurface.surfaceControlHandle);
+ ANativeWindow* anw = surface.get();
+ ANativeWindow_acquire(anw);
+ *outWindow = anw;
+ return STATUS_OK;
+}
+
+#endif
+
status_t Surface::writeToParcel(Parcel* parcel) const {
return writeToParcel(parcel, false);
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 1335e4d..b2fec79 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -58,8 +58,6 @@
"VirtualKeyMap.cpp",
],
- clang: true,
-
header_libs: ["jni_headers"],
export_header_lib_headers: ["jni_headers"],
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 155cb04..2581668 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -137,10 +137,23 @@
// --- InputEvent ---
+// Due to precision limitations when working with floating points, transforming - namely
+// scaling - floating points can lead to minute errors. We round transformed values to approximately
+// three decimal places so that values like 0.99997 show up as 1.0.
+inline float roundTransformedCoords(float val) {
+ // Use a power to two to approximate three decimal places to potentially reduce some cycles.
+ // This should be at least as precise as MotionEvent::ROUNDING_PRECISION.
+ return std::round(val * 1024.f) / 1024.f;
+}
+
+inline vec2 roundTransformedCoords(vec2 p) {
+ return {roundTransformedCoords(p.x), roundTransformedCoords(p.y)};
+}
+
vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
const vec2 transformedXy = transform.transform(xy);
const vec2 transformedOrigin = transform.transform(0, 0);
- return transformedXy - transformedOrigin;
+ return roundTransformedCoords(transformedXy - transformedOrigin);
}
const char* inputEventTypeToString(int32_t type) {
@@ -548,12 +561,12 @@
float MotionEvent::getXCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
- return vals.x;
+ return roundTransformedCoords(vals.x);
}
float MotionEvent::getYCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
- return vals.y;
+ return roundTransformedCoords(vals.y);
}
void MotionEvent::setCursorPosition(float x, float y) {
@@ -875,7 +888,7 @@
static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
const vec2& xy) {
return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
- : transform.transform(xy);
+ : roundTransformedCoords(transform.transform(xy));
}
vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
@@ -949,6 +962,8 @@
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 170e748..d6b4579 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 @@
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 @@
}
}
return true;
+#else
+ (void)configs; // Suppress 'unused variable' warning
+ return true;
+#endif
}
} // namespace
@@ -199,77 +206,67 @@
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;
- }
- }
- ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
- return NAME_NOT_FOUND;
-}
-
-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::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, "%s: ledCode=%d ~ Not found.", __func__, ledCode);
-
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
+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 std::nullopt;
+}
// --- KeyLayoutMap::Parser ---
@@ -345,8 +342,9 @@
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
- KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::unordered_map<int32_t, Key>& map =
+ mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+ if (map.find(code) != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -387,7 +385,7 @@
Key key;
key.keyCode = keyCode;
key.flags = flags;
- map.add(code, key);
+ map.insert({code, key});
return NO_ERROR;
}
@@ -400,7 +398,7 @@
scanCodeToken.string());
return BAD_VALUE;
}
- if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
+ if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) {
ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
@@ -486,8 +484,7 @@
"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 @@
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 @@
Led led;
led.ledCode = ledCode;
- map.add(code, led);
+ map.insert({code, led});
return NO_ERROR;
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 4b31246..597b389 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -29,6 +29,8 @@
// Default display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
class BaseTest : public testing::Test {
protected:
static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
@@ -231,95 +233,107 @@
static constexpr float RAW_X_OFFSET = 12;
static constexpr float RAW_Y_OFFSET = -41.1;
+ void SetUp() override;
+
int32_t mId;
ui::Transform mTransform;
ui::Transform mRawTransform;
+ PointerProperties mPointerProperties[2];
+ struct Sample {
+ PointerCoords pointerCoords[2];
+ };
+ std::array<Sample, 3> mSamples{};
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+void MotionEventTest::SetUp() {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
- PointerProperties pointerProperties[2];
- pointerProperties[0].clear();
- pointerProperties[0].id = 1;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- pointerProperties[1].clear();
- pointerProperties[1].id = 2;
- pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mPointerProperties[0].clear();
+ mPointerProperties[0].id = 1;
+ mPointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerProperties[1].clear();
+ mPointerProperties[1].id = 2;
+ mPointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
- PointerCoords pointerCoords[2];
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
- pointerCoords[1].clear();
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+ mSamples[0].pointerCoords[0].clear();
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
+ mSamples[0].pointerCoords[1].clear();
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+
+ mSamples[1].pointerCoords[0].clear();
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
+ mSamples[1].pointerCoords[1].clear();
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
+
+ mSamples[2].pointerCoords[0].clear();
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
+ mSamples[2].pointerCoords[1].clear();
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
+}
+
+void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
- pointerProperties, pointerCoords);
-
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
- event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
-
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
- event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+ mPointerProperties, mSamples[0].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
}
void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
@@ -356,51 +370,65 @@
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ // Ensure the underlying PointerCoords are identical.
+ for (int sampleIdx = 0; sampleIdx < 3; sampleIdx++) {
+ for (int pointerIdx = 0; pointerIdx < 2; pointerIdx++) {
+ ASSERT_EQ(mSamples[sampleIdx].pointerCoords[pointerIdx],
+ event->getSamplePointerCoords()[sampleIdx * 2 + pointerIdx]);
+ }
+ }
- ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+ ASSERT_NEAR(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
+ ASSERT_NEAR(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
- ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
- ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
+ ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0),
+ EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1),
+ EPSILON);
- ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
- ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
+ ASSERT_NEAR(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1), EPSILON);
- ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
+ ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1), EPSILON);
- ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
+ ASSERT_NEAR(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 210 * X_SCALE, event->getX(0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 220 * X_SCALE, event->getX(1), EPSILON);
+
+ ASSERT_NEAR(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 211 * Y_SCALE, event->getY(0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 221 * Y_SCALE, event->getY(1), EPSILON);
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -532,10 +560,10 @@
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
- ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
- ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
+ ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
+ ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
+ ASSERT_NEAR((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0), EPSILON);
+ ASSERT_NEAR((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0), EPSILON);
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -773,18 +801,18 @@
// The x and y axes should have the window transform applied.
const auto newPoint = transform.transform(60, 100);
- ASSERT_EQ(newPoint.x, event.getX(0));
- ASSERT_EQ(newPoint.y, event.getY(0));
+ ASSERT_NEAR(newPoint.x, event.getX(0), EPSILON);
+ ASSERT_NEAR(newPoint.y, event.getY(0), EPSILON);
// The raw values should have the display transform applied.
const auto raw = rawTransform.transform(60, 100);
- ASSERT_EQ(raw.x, event.getRawX(0));
- ASSERT_EQ(raw.y, event.getRawY(0));
+ ASSERT_NEAR(raw.x, event.getRawX(0), EPSILON);
+ ASSERT_NEAR(raw.y, event.getRawY(0), EPSILON);
// Relative values should have the window transform applied without any translation.
const auto rel = transformWithoutTranslation(transform, 42, 96);
- ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
+ ASSERT_NEAR(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), EPSILON);
+ ASSERT_NEAR(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), EPSILON);
}
TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -851,4 +879,42 @@
ASSERT_EQ(4, event.getYCursorPosition());
}
+TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) {
+ // These are specifically integral values, since we are testing for rounding.
+ const vec2 EXPECTED{400.f, 700.f};
+
+ // Pick a transform such that transforming the point with its inverse and bringing that
+ // back to the original coordinate space results in a non-zero error amount due to the
+ // nature of floating point arithmetics. This can happen when the display is scaled.
+ // For example, the 'adb shell wm size' command can be used to set an override for the
+ // logical display size, which could result in the display being scaled.
+ constexpr float scale = 720.f / 1080.f;
+ ui::Transform transform;
+ transform.set(scale, 0, 0, scale);
+ ASSERT_NE(EXPECTED, transform.transform(transform.inverse().transform(EXPECTED)));
+
+ // Store the inverse-transformed values in the motion event.
+ const vec2 rawCoords = transform.inverse().transform(EXPECTED);
+ PointerCoords pc{};
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, rawCoords.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, rawCoords.y);
+ PointerProperties pp{};
+ MotionEvent event;
+ event.initialize(InputEvent::nextId(), 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
+ AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
+ MotionClassification::NONE, transform, 2.0f, 2.1f, rawCoords.x, rawCoords.y,
+ transform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 1, &pp, &pc);
+
+ // When using the getters from the MotionEvent to obtain the coordinates, the transformed
+ // values should be rounded by an appropriate amount so that they now precisely equal the
+ // original coordinates.
+ ASSERT_EQ(EXPECTED.x, event.getX(0));
+ ASSERT_EQ(EXPECTED.y, event.getY(0));
+ ASSERT_EQ(EXPECTED.x, event.getRawX(0));
+ ASSERT_EQ(EXPECTED.y, event.getRawY(0));
+ ASSERT_EQ(EXPECTED.x, event.getXCursorPosition());
+ ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 05bc0bc..8393e99 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -32,6 +32,8 @@
namespace android {
+constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
class InputPublisherAndConsumerTest : public testing::Test {
protected:
std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
@@ -233,10 +235,10 @@
EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
- EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
- EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
- EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
- EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON);
+ EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON);
+ EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON);
+ EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON);
EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
@@ -249,10 +251,12 @@
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
const auto& pc = pointerCoords[i];
- EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
- EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
- EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
- EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]);
+
+ EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON);
+ EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON);
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 72d33bc..82ae422 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 ed728dc..4659b96 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -33,7 +33,7 @@
cc_library_headers {
name: "libnativedisplay_headers",
- export_include_dirs: ["include",],
+ export_include_dirs: ["include"],
}
cc_library_shared {
@@ -43,8 +43,6 @@
"include-private",
],
- clang: true,
-
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 4a1784e..2e0add5 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -16,6 +16,9 @@
#define LOG_TAG "AHardwareBuffer"
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_aidl.h>
+#include <android/binder_libbinder.h>
#include <vndk/hardware_buffer.h>
#include <errno.h>
@@ -412,6 +415,25 @@
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 = AParcel_viewPlatformParcel(parcel)->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 AParcel_viewPlatformParcel(parcel)->write(*gb);
+}
+
// ----------------------------------------------------------------------------
// VNDK functions
// ----------------------------------------------------------------------------
@@ -593,15 +615,27 @@
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 +687,7 @@
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 +697,10 @@
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 18a4b2d..73a05fc 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -20,13 +20,53 @@
// 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 <android/binder_libbinder.h>
+#include <dlfcn.h>
+#include <log/log.h>
#include <ui/GraphicBuffer.h>
using namespace android;
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+ status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+ android_view_Surface_writeToParcel write = nullptr;
+ android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+ static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+ SurfaceParcelables ret;
+ void* dl = dlopen("libgui.so", RTLD_NOW);
+ LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+ ret.write =
+ (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.write,
+ "libgui.so missing android_view_Surface_writeToParcel; "
+ "loaded wrong libgui?");
+ ret.read =
+ (android_view_Surface_readFromParcel)dlsym(dl,
+ "android_view_Surface_readFromParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.read,
+ "libgui.so missing android_view_Surface_readFromParcel; "
+ "loaded wrong libgui?");
+ return ret;
+ }();
+ return &funcs;
+}
+
static int32_t query(ANativeWindow* window, int what) {
int value;
int res = window->query(window, what, &value);
@@ -334,6 +374,28 @@
return native_window_set_auto_prerotation(window, autoPrerotation);
}
+binder_status_t ANativeWindow_readFromParcel(
+ const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->read == nullptr) {
+ ALOGE("Failed to load Surface_readFromParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
+ }
+ const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return funcs->read(nativeParcel, outWindow);
+}
+
+binder_status_t ANativeWindow_writeToParcel(
+ ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->write == nullptr) {
+ ALOGE("Failed to load Surface_writeToParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
+ }
+ Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return funcs->write(window, nativeParcel);
+}
+
/**************************************************************************************************
* apex-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index d30efa1..bc0bfc5 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -61,6 +61,9 @@
// Android O
first_version: "26",
+ export_header_libs: [
+ "libnativewindow_ndk_headers",
+ ],
}
cc_library {
@@ -71,14 +74,15 @@
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 @@
"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 c35507b..21798d0 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -173,6 +173,27 @@
* OpenGL ES: GR_GL_R8
*/
AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R16_UINT
+ * OpenGL ES: GL_R16UI
+ */
+ AHARDWAREBUFFER_FORMAT_R16_UINT = 0x39,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R16G16_UINT
+ * OpenGL ES: 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 @@
*/
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 0000000..9fea21e
--- /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 f0e1c4d..a593cd4 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -333,7 +333,7 @@
__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 0000000..a252245
--- /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 988132c..d9ac568 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,10 +2,10 @@
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 @@
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 @@
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 @@
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 @@
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 cb92df3..f6f57dd 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -111,7 +111,6 @@
name: "librenderengine",
defaults: ["librenderengine_defaults"],
double_loadable: true,
- clang: true,
cflags: [
"-fvisibility=hidden",
"-Werror=format",
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index edd453a..b6b9cc4 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,10 +21,10 @@
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 @@
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 0000000..7af22cc
--- /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 0000000..6a61d36
--- /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 0000000..685c549
--- /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 0000000..0e110b7
--- /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 8fdb003..ac4be44 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -24,9 +24,10 @@
cc_test {
name: "libsensor_test",
- clang: true,
-
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
srcs: [
"Sensor_test.cpp",
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 6b936de..8477479 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -28,7 +28,7 @@
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 cf671bc..718d37b 100644
--- a/libs/shaders/tests/Android.bp
+++ b/libs/shaders/tests/Android.bp
@@ -31,7 +31,7 @@
"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 37c9824..eca051d 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -28,7 +28,7 @@
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 58851b4..0002d3a 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -31,7 +31,7 @@
"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 d138495..98d9b94 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -36,7 +36,6 @@
cc_defaults {
name: "libui-defaults",
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -111,7 +110,6 @@
},
double_loadable: true,
- clang: true,
cflags: [
"-Wall",
"-Werror",
@@ -157,6 +155,8 @@
],
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 @@
"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 @@
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 f6ab7b2..53372c9 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 @@
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 3732fee..429760f 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -465,7 +465,7 @@
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 a98e697..f582423 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -109,6 +109,11 @@
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 507fa35..37a2e1c 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -59,6 +59,8 @@
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 f422ce4..cf5c2e8 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -53,16 +53,19 @@
// 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 0a4873c..3fb33b4 100644
--- a/libs/ui/tests/colorspace_test.cpp
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -111,6 +111,7 @@
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 @@
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 @@
{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 c28c303..5d6070c 100644
--- a/libs/ui/tools/Android.bp
+++ b/libs/ui/tools/Android.bp
@@ -25,7 +25,7 @@
cc_defaults {
name: "libui_tools_default",
- clang_cflags: [
+ cflags: [
"-Wall",
"-Wextra",
"-Werror",
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 83c250a..2af51a7 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -21,31 +21,8 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library {
- name: "libvibrator",
- vendor_available: true,
- double_loadable: true,
-
- shared_libs: [
- "libbinder",
- "liblog",
- "libutils",
- ],
-
- header_libs: [
- "libaudio_system_headers",
- ],
-
- aidl: {
- include_dirs: ["frameworks/base/core/java"],
- local_include_dirs: ["include/"],
- export_aidl_headers: true,
- },
-
- srcs: [
- ":libvibrator_aidl",
- "*.cpp",
- ],
+cc_defaults {
+ name: "libvibrator_defaults",
cflags: [
"-Wall",
@@ -64,3 +41,54 @@
},
},
}
+
+cc_library {
+ name: "libvibrator",
+ defaults: ["libvibrator_defaults"],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ whole_static_libs: [
+ "libvibratorutils",
+ ],
+
+ header_libs: [
+ "libaudio_system_headers",
+ ],
+
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ local_include_dirs: ["include/"],
+ export_aidl_headers: true,
+ },
+
+ srcs: [
+ ":libvibrator_aidl",
+ "ExternalVibration.cpp",
+ ],
+}
+
+cc_library {
+ name: "libvibratorutils",
+ defaults: ["libvibrator_defaults"],
+
+ vendor_available: true,
+ double_loadable: true,
+
+ shared_libs: [
+ "libutils",
+ ],
+
+ srcs: [
+ "ExternalVibrationUtils.cpp",
+ ],
+
+ visibility: [
+ "//frameworks/native/libs/vibrator",
+ "//frameworks/av/media/libeffects/hapticgenerator",
+ ],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index f6fc19e..ec90645 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 c6eb3d1..760dbce 100644
--- a/libs/vibrator/include/vibrator/ExternalVibration.h
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -33,7 +33,6 @@
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 84357fc..c588bfd 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 fa449ae..e72ca74 100644
--- a/libs/vr/libbroadcastring/Android.bp
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -26,7 +26,6 @@
cc_test {
name: "broadcast_ring_tests",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
deleted file mode 100644
index 0bda798..0000000
--- 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 e33e03b..0000000
--- 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 b6813eb..0000000
--- 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 2d3fa4a..0000000
--- 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 f705749..0000000
--- 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 74b4b3d..0000000
--- 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 36ab5f6..0000000
--- 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 2f14f7c..0000000
--- 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 33a0d75..0000000
--- 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 6ae603b..0000000
--- 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 fab1097..0000000
--- 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 b0ed950..0000000
--- 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 62856df..0000000
--- 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 fdeeb70..0000000
--- 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 773f9a5..0000000
--- 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 2f8a3c0..0000000
--- 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 81546ac..0000000
--- 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 45aef51..0000000
--- 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 9f4cc4a..0000000
--- 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 20541a6..0000000
--- 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 152464a..0000000
--- 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 6ebf487..0000000
--- 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 2f8a3c0..0000000
--- 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 04d4f30..0000000
--- 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 96023dd..9dbeacb 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -33,87 +33,3 @@
],
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 e099f6a..0000000
--- 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 c11706f..0000000
--- 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 1ca653c..0000000
--- 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 e53a686..0000000
--- 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 df0d54e..0000000
--- 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 7f631e3..0000000
--- 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 4e87cf6..0000000
--- 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 f845cd8..0000000
--- 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 599101f..0000000
--- 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 c379ef5..0000000
--- 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 0c7ec01..0000000
--- 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 73addc9..0000000
--- 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 5ecb498..0000000
--- 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 fe7feb8..0000000
--- 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 5d2ec28..0000000
--- 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 df06097..0000000
--- 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 c72f940..0000000
--- 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 07e2121..0000000
--- 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 5c837e7..0000000
--- 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 3b6d6e1..0000000
--- 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 c1f6da3..c95603b 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -16,7 +16,6 @@
cc_library_static {
name: "libpdx",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -42,7 +41,6 @@
cc_test {
name: "pdx_tests",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -72,7 +70,6 @@
// Code analysis target.
cc_test {
name: "pdx_encoder_performance_test",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
index cc32b18..ac831ce 100644
--- a/libs/vr/libpdx/fuzz/Android.bp
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -9,7 +9,6 @@
cc_fuzz {
name: "libpdx_service_dispatcher_fuzzer",
- clang: true,
srcs: [
"service_dispatcher_fuzzer.cpp",
],
@@ -24,13 +23,12 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils"
+ "libcutils",
],
}
cc_fuzz {
name: "libpdx_message_fuzzer",
- clang: true,
srcs: [
"message_fuzzer.cpp",
],
@@ -45,13 +43,12 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils"
+ "libcutils",
],
}
cc_fuzz {
name: "libpdx_serialization_fuzzer",
- clang: true,
srcs: [
"serialization_fuzzer.cpp",
],
@@ -66,6 +63,6 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils"
+ "libcutils",
],
}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 8046857..a5758b5 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -9,7 +9,6 @@
cc_defaults {
name: "pdx_default_transport_compiler_defaults",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -26,7 +25,10 @@
cc_defaults {
name: "pdx_use_transport_servicefs",
export_include_dirs: ["private/servicefs"],
- whole_static_libs: ["libpdx_servicefs", "libservicefs"],
+ whole_static_libs: [
+ "libpdx_servicefs",
+ "libservicefs",
+ ],
}
cc_defaults {
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 216ca9f..7f88daf 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -9,7 +9,6 @@
cc_library_static {
name: "libpdx_uds",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
@@ -41,7 +40,6 @@
cc_test {
name: "libpdx_uds_tests",
- clang: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
deleted file mode 100644
index 40a5099..0000000
--- 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 2f8a3c0..0000000
--- 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 b663a67..0000000
--- 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 bf0e687..0000000
--- 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 7bf1cd4..0000000
--- 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 39592bb..0000000
--- 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 d3a4521..0000000
--- 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 4ff6a09..0000000
--- 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 a47fb9a..3d60a1d 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,12 +1,6 @@
-abdolrashidi@google.com
-cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
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 c9fce8a..62cf255 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -12,7 +12,10 @@
name: "libETC1",
srcs: ["ETC1/etc1.cpp"],
host_supported: true,
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
target: {
android: {
@@ -37,6 +40,9 @@
symbol_file: "libEGL.map.txt",
first_version: "9",
unversioned_until: "current",
+ export_header_libs: [
+ "libEGL_headers",
+ ],
}
ndk_library {
@@ -44,6 +50,9 @@
symbol_file: "libGLESv1_CM.map.txt",
first_version: "9",
unversioned_until: "current",
+ export_header_libs: [
+ "libGLESv1_CM_headers",
+ ],
}
ndk_library {
@@ -51,6 +60,9 @@
symbol_file: "libGLESv2.map.txt",
first_version: "9",
unversioned_until: "current",
+ export_header_libs: [
+ "libGLESv2_headers",
+ ],
}
ndk_library {
@@ -58,6 +70,9 @@
symbol_file: "libGLESv3.map.txt",
first_version: "18",
unversioned_until: "current",
+ export_header_libs: [
+ "libGLESv3_headers",
+ ],
}
cc_defaults {
@@ -108,7 +123,6 @@
// In particular, DO NOT add libutils nor anything "above" libui
"libgraphicsenv",
"libnativewindow",
- "libbacktrace",
"libbase",
],
}
@@ -165,12 +179,17 @@
"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 86c788d..aecfc6b 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -231,7 +231,7 @@
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 @@
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 @@
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 ff03d30..52078ff 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -117,7 +117,10 @@
// 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 ceea0fb..450c128 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -466,4 +466,31 @@
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 b7fdf97..96437c3 100644
--- a/opengl/libs/EGL/CallStack.h
+++ b/opengl/libs/EGL/CallStack.h
@@ -16,8 +16,8 @@
#pragma once
-#include <backtrace/Backtrace.h>
#include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <memory>
@@ -26,12 +26,15 @@
// Create a callstack with the current thread's stack trace.
// Immediately dump it to logcat using the given logtag.
static void log(const char* logtag) noexcept {
- std::unique_ptr<Backtrace> backtrace(
- Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- if (backtrace->Unwind(2)) {
- for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (unwinder.Unwind(data)) {
+ for (size_t i = 2, c = data.frames.size(); i < c; i++) {
+ auto& frame = data.frames[i];
+ // Trim the first two frames.
+ frame.num -= 2;
__android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
- backtrace->FormatFrameData(i).c_str());
+ unwinder.FormatFrame(frame).c_str());
}
}
}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 76fd7f0..dd14bcf 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -207,7 +207,8 @@
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 @@
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 @@
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_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 0b755aa..c2c856e 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -322,6 +322,16 @@
mExtensionString = gBuiltinExtensionString;
+ // b/269060366 Conditionally enabled EGL_ANDROID_get_frame_timestamps extension if the
+ // device's present timestamps are reliable (which may not be the case on emulators).
+ if (cnx->useAngle) {
+ if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
+ mExtensionString.append("EGL_ANDROID_get_frame_timestamps");
+ }
+ } else {
+ mExtensionString.append("EGL_ANDROID_get_frame_timestamps");
+ }
+
hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
// Note: CDD requires that devices supporting wide color and/or HDR color also support
@@ -361,6 +371,7 @@
findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
mExtensionString.append("EGL_EXT_image_gl_colorspace ");
}
+
if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
mExtensionString.append(ext + " ");
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index f4dbe49..5f441ee 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -84,7 +84,8 @@
// Extensions implemented by the EGL wrapper.
const char* const gBuiltinExtensionString =
"EGL_ANDROID_front_buffer_auto_refresh "
- "EGL_ANDROID_get_frame_timestamps "
+ // b/269060366 Conditionally enabled during display initialization:
+ //"EGL_ANDROID_get_frame_timestamps "
"EGL_ANDROID_get_native_client_buffer "
"EGL_ANDROID_presentation_time "
"EGL_EXT_surface_CTA861_3_metadata "
@@ -1453,7 +1454,11 @@
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ if (err != 0) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ } else if (!s->cnx->useAngle) {
+ return EGL_TRUE;
+ } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
if (attribute == EGL_TIMESTAMPS_ANDROID) {
@@ -1463,7 +1468,11 @@
return EGL_TRUE;
}
int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ if (err != 0) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ } else if (!s->cnx->useAngle) {
+ return EGL_TRUE;
+ } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
if (s->setSmpte2086Attribute(attribute, value)) {
@@ -2175,6 +2184,10 @@
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
+ if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2208,6 +2221,10 @@
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
+ if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2262,6 +2279,10 @@
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
+ if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2292,6 +2313,10 @@
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
+ if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2377,6 +2402,10 @@
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
+ if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index b3d6f74..e0ba946 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -118,70 +118,27 @@
: "rax", "cc" \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
+ #define API_ENTRY(_api) __attribute__((noinline)) _api
- #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(__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 65f50f5..5bd5c14 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -191,73 +191,44 @@
: \
);
-#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 bacd4b4..64c0f97 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -247,73 +247,44 @@
: \
);
-#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 c53f7be..a4674e1 100644
--- a/opengl/tests/gl2_cameraeye/AndroidManifest.xml
+++ b/opengl/tests/gl2_cameraeye/AndroidManifest.xml
@@ -26,7 +26,7 @@
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:glEsVersion="0x00020000" />
<application android:label="@string/gl2cameraeye_name">
- <activity android:name="GL2CameraEye">
+ <activity android:name="GL2CameraEye" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/opengl/tests/gl2_java/AndroidManifest.xml b/opengl/tests/gl2_java/AndroidManifest.xml
index 8bb6840..500adb5 100644
--- a/opengl/tests/gl2_java/AndroidManifest.xml
+++ b/opengl/tests/gl2_java/AndroidManifest.xml
@@ -22,7 +22,8 @@
<activity android:name="GL2JavaActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/opengl/tests/gl2_jni/AndroidManifest.xml b/opengl/tests/gl2_jni/AndroidManifest.xml
index 1827e5f..b4ce99b 100644
--- a/opengl/tests/gl2_jni/AndroidManifest.xml
+++ b/opengl/tests/gl2_jni/AndroidManifest.xml
@@ -21,7 +21,8 @@
<activity android:name="GL2JNIActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/opengl/tests/gl_jni/AndroidManifest.xml b/opengl/tests/gl_jni/AndroidManifest.xml
index 5d0ec96..bedab56 100644
--- a/opengl/tests/gl_jni/AndroidManifest.xml
+++ b/opengl/tests/gl_jni/AndroidManifest.xml
@@ -24,7 +24,8 @@
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
index 6c23d42..d766be9 100644
--- a/opengl/tests/lighting1709/AndroidManifest.xml
+++ b/opengl/tests/lighting1709/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.android.lightingtest">
<application>
- <activity android:name="ClearActivity" android:label="LightingTest">
+ <activity android:name="ClearActivity" android:label="LightingTest" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/opengl/tests/testPauseResume/AndroidManifest.xml b/opengl/tests/testPauseResume/AndroidManifest.xml
index 1879bc3..ae82a82 100644
--- a/opengl/tests/testPauseResume/AndroidManifest.xml
+++ b/opengl/tests/testPauseResume/AndroidManifest.xml
@@ -24,7 +24,8 @@
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden">
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..ee92d9e
--- /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 72bd292..614a78e 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -53,4 +53,6 @@
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 5c7f344..ea1077a 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 178bc29..bf6189d 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -34,6 +34,10 @@
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 8a90a12..2d7072d 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 @@
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 @@
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 5b4ee21..fba64c7 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -99,7 +99,7 @@
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 482fc6d..c51f6aa 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 076affd..680b291 100644
--- a/services/gpuservice/bpfprogs/Android.bp
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -22,8 +22,8 @@
}
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
similarity index 100%
rename from services/gpuservice/bpfprogs/gpu_mem.c
rename to services/gpuservice/bpfprogs/gpuMem.c
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index de691e2..7588b54 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -57,9 +57,9 @@
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 a9a59bc..e204040 100644
--- a/services/gpuservice/gpuwork/Android.bp
+++ b/services/gpuservice/gpuwork/Android.bp
@@ -55,6 +55,6 @@
],
required: [
"bpfloader",
- "gpu_work.o",
+ "gpuWork.o",
],
}
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index 974ae38..fd70323 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 @@
{
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 @@
}
// 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 b3c4eff..fe45c98 100644
--- a/services/gpuservice/gpuwork/bpfprogs/Android.bp
+++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp
@@ -17,8 +17,8 @@
}
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
similarity index 99%
rename from services/gpuservice/gpuwork/bpfprogs/gpu_work.c
rename to services/gpuservice/gpuwork/bpfprogs/gpuWork.c
index d73fff4..f470189 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
similarity index 100%
rename from services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
rename to 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 acecd56..cece999 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 4fb0d2e..51642f9 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -24,9 +24,6 @@
cc_test {
name: "gpuservice_unittest",
test_suites: ["device-tests"],
- sanitize: {
- address: true,
- },
srcs: [
"GpuMemTest.cpp",
"GpuMemTracerTest.cpp",
@@ -35,6 +32,7 @@
header_libs: ["bpf_headers"],
shared_libs: [
"libbase",
+ "libbinder",
"libbpf_bcc",
"libcutils",
"libgfxstats",
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index e916221..8dabe4f 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "gpuservice_unittest"
#include <android-base/stringprintf.h>
+#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
#include <bpf/BpfMap.h>
#include <gmock/gmock.h>
#include <gpumem/GpuMem.h>
@@ -65,11 +66,11 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
mTestableGpuMem.setInitialized();
errno = 0;
- mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC);
+ mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
+ TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC));
EXPECT_EQ(0, errno);
- EXPECT_LE(0, mTestMap.getMap().get());
EXPECT_TRUE(mTestMap.isValid());
}
@@ -89,8 +90,8 @@
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 d76f039..5c04210 100644
--- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "gpuservice_unittest"
+#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
#include <bpf/BpfMap.h>
#include <gpumem/GpuMem.h>
#include <gtest/gtest.h>
@@ -64,11 +65,11 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
errno = 0;
- mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC);
+ mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
+ TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC));
EXPECT_EQ(0, errno);
- EXPECT_LE(0, mTestMap.getMap().get());
EXPECT_TRUE(mTestMap.isValid());
}
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 20c8ccf..7ea2288 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -18,12 +18,14 @@
#define LOG_TAG "gpuservice_unittest"
#include <unistd.h>
+#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <gpustats/GpuStats.h>
#include <gtest/gtest.h>
#include <stats_pull_atom_callback.h>
#include <statslog.h>
+#include <utils/Looper.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -61,8 +63,9 @@
// clang-format on
class GpuStatsTest : public testing::Test {
+ sp<android::Looper> looper;
public:
- GpuStatsTest() {
+ GpuStatsTest() : looper(Looper::prepare(0 /* opts */)) {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
@@ -72,6 +75,16 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ // This is required for test due to GpuStats instance spawns binder transactions
+ // in its destructor. After the gtest destructor test exits immidiatelly.
+ // It results in binder thread not able to process above binder transactions and memory leak
+ // occures. Binder thread needs time to process callbacks transactions.
+ // It leads to GpuStats instance destructor needs to be called in advance.
+ mGpuStats.reset(nullptr);
+ // performs all pending callbacks until all data has been consumed
+ // gives time to process binder transactions by thread pool
+ looper->pollAll(1000);
}
std::string inputCommand(InputCommand cmd);
@@ -79,6 +92,10 @@
void SetUp() override {
mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
mGlesVersion = property_get_int32("ro.opengles.version", 0);
+
+ // start the thread pool
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
}
std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>();
diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp
index 83a40e7..b6362e2 100644
--- a/services/gpuservice/vts/Android.bp
+++ b/services/gpuservice/vts/Android.bp
@@ -21,7 +21,6 @@
srcs: ["src/**/*.java"],
libs: [
"tradefed",
- "vts-core-tradefed-harness",
],
test_suites: [
"general-tests",
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 41878e3..18d670a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -147,6 +147,7 @@
srcs: [":libinputflinger_base_sources"],
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"libinput",
"liblog",
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
index 82c6ee1..c88bfe9 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1,3 +1 @@
-lzye@google.com
-michaelwr@google.com
-svv@google.com
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index b4b617e..8d5d883 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 c6dd621..0f18830 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -108,6 +108,8 @@
constexpr int LOGTAG_INPUT_FOCUS = 62001;
constexpr int LOGTAG_INPUT_CANCEL = 62003;
+const ui::Transform kIdentityTransform;
+
inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -460,8 +462,8 @@
}
// Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
- bool isStylus) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
+ bool isStylus, const ui::Transform& displayTransform) {
const auto inputConfig = windowInfo.inputConfig;
if (windowInfo.displayId != displayId ||
inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
@@ -471,7 +473,17 @@
if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
return false;
}
- if (!windowInfo.touchableRegionContainsPoint(x, y)) {
+
+ // Window Manager works in the logical display coordinate space. When it specifies bounds for a
+ // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside
+ // the window. Points on the right and bottom edges should not be inside the window, so we need
+ // to be careful about performing a hit test when the display is rotated, since the "right" and
+ // "bottom" of the window will be different in the display (un-rotated) space compared to in the
+ // logical display in which WM determined the bounds. Perform the hit test in the logical
+ // display space to ensure these edges are considered correctly in all orientations.
+ const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
+ const auto p = displayTransform.transform(x, y);
+ if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
return false;
}
return true;
@@ -929,10 +941,8 @@
// the touch into the other window.
if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
int32_t displayId = motionEntry.displayId;
- int32_t x = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const float x = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> touchedWindowHandle =
@@ -1059,8 +1069,8 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, TouchState* touchState,
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,
+ TouchState* touchState,
bool isStylus,
bool addOutsideTargets,
bool ignoreDragWindow) {
@@ -1075,7 +1085,8 @@
}
const WindowInfo& info = *windowHandle->getInfo();
- if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!info.isSpy() &&
+ windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
return windowHandle;
}
@@ -1089,14 +1100,14 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
+ int32_t displayId, float x, float y, bool isStylus) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
- if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
continue;
}
if (!info.isSpy()) {
@@ -2058,16 +2069,16 @@
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
- int32_t x;
- int32_t y;
+ float x;
+ float y;
const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
// Always dispatch mouse events to cursor position.
if (isFromMouse) {
- x = int32_t(entry.xCursorPosition);
- y = int32_t(entry.yCursorPosition);
+ x = entry.xCursorPosition;
+ y = entry.yCursorPosition;
} else {
- x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
- y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ x = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X);
+ y = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y);
}
const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2076,8 +2087,7 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
- displayId);
+ ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
@@ -2124,7 +2134,8 @@
}
if (newTouchedWindows.empty()) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
+ ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
+ "%d.",
x, y, displayId);
injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
@@ -2158,7 +2169,8 @@
computeTouchOcclusionInfoLocked(windowHandle, x, y);
if (!isTouchTrustedLocked(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
- ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x,
+ y);
for (const auto& log : occlusionInfo.debugInfo) {
ALOGD("%s", log.c_str());
}
@@ -2242,8 +2254,8 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
- const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const float x = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> oldTouchedWindowHandle =
@@ -2536,8 +2548,8 @@
}
const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
- const int32_t x = entry.pointerCoords[pointerIndex].getX();
- const int32_t y = entry.pointerCoords[pointerIndex].getY();
+ const float x = entry.pointerCoords[pointerIndex].getX();
+ const float y = entry.pointerCoords[pointerIndex].getY();
switch (maskedAction) {
case AMOTION_EVENT_ACTION_MOVE: {
@@ -3642,6 +3654,8 @@
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) {
@@ -3676,7 +3690,10 @@
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(
@@ -3710,6 +3727,8 @@
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: {
@@ -3735,8 +3754,10 @@
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(
@@ -4578,6 +4599,12 @@
return getWindowHandleLocked(focusedToken, displayId);
}
+ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const {
+ auto displayInfoIt = mDisplayInfos.find(displayId);
+ return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
+ : kIdentityTransform;
+}
+
bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
const bool noInputChannel =
@@ -4732,10 +4759,13 @@
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/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 0df5894..7769b9e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -240,11 +240,11 @@
std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
+ int32_t displayId, float x, float y, TouchState* touchState, bool isStylus = false,
bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
+ int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
REQUIRES(mLock);
@@ -370,6 +370,7 @@
int32_t displayId) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
+ ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
// Same function as above, but faster. Since displayId is provided, this avoids the need
// to loop through all displays.
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3bd3275..01146a3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,7 +71,7 @@
"libstatslog",
"libui",
"libutils",
- "PlatformProperties",
+ "libPlatformProperties",
],
static_libs: [
"libc++fs",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index d6a6bd2..20baa42 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -452,8 +452,7 @@
return false;
}
- std::vector<int32_t> scanCodes;
- keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
+ std::vector<int32_t> scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode);
const size_t N = scanCodes.size();
for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
int32_t sc = scanCodes[i];
@@ -548,10 +547,10 @@
return NAME_NOT_FOUND;
}
- int32_t scanCode;
- if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) {
- *outScanCode = scanCode;
+ std::optional<int32_t> scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led);
+ if (scanCode.has_value()) {
+ if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) {
+ *outScanCode = *scanCode;
return NO_ERROR;
}
}
@@ -865,8 +864,7 @@
Device* device = getDeviceLocked(deviceId);
if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
- std::vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
+ std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode);
if (scanCodes.size() != 0) {
if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
for (size_t i = 0; i < scanCodes.size(); i++) {
@@ -890,8 +888,8 @@
device->keyMap.keyLayoutMap == nullptr) {
return AKEYCODE_UNKNOWN;
}
- std::vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes);
+ std::vector<int32_t> scanCodes =
+ device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode);
if (scanCodes.empty()) {
ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input"
"device %d",
@@ -960,20 +958,16 @@
Device* device = getDeviceLocked(deviceId);
if (device != nullptr && device->keyMap.haveKeyLayout()) {
- std::vector<int32_t> scanCodes;
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
- scanCodes.clear();
+ std::vector<int32_t> scanCodes =
+ device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]);
- status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex],
- &scanCodes);
- if (!err) {
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (device->keyBitmask.test(scanCodes[sc])) {
- outFlags[codeIndex] = 1;
- break;
- }
+ // check the possible scan codes identified by the layout map against the
+ // map of codes actually emitted by the driver
+ for (size_t sc = 0; sc < scanCodes.size(); sc++) {
+ if (device->keyBitmask.test(scanCodes[sc])) {
+ outFlags[codeIndex] = 1;
+ break;
}
}
}
@@ -1027,14 +1021,15 @@
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
- if (err == NO_ERROR) {
- return NO_ERROR;
- }
+ if (device == nullptr || !device->keyMap.haveKeyLayout()) {
+ return NAME_NOT_FOUND;
}
-
- return NAME_NOT_FOUND;
+ std::optional<AxisInfo> info = device->keyMap.keyLayoutMap->mapAxis(scanCode);
+ if (!info.has_value()) {
+ return NAME_NOT_FOUND;
+ }
+ *outAxisInfo = *info;
+ return NO_ERROR;
}
base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId,
@@ -1314,7 +1309,8 @@
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 @@
// 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 @@
return vibrators;
}
-EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
- for (const auto& [id, device] : mDevices) {
+/**
+ * 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 device.get();
+ return true;
}
}
- return nullptr;
+
+ for (const auto& [id, device] : mDevices) {
+ if (descriptor == device->identifier.descriptor) {
+ return true;
+ }
+ }
+ return false;
}
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
@@ -1476,25 +1485,35 @@
}
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;
- }
+ 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;
// 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::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 989700f..3f26aaa 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -552,14 +552,6 @@
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 @@
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 9bcf463..905b348 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,6 +38,26 @@
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 @@
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 @@
}
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 130c556..54c6810 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -650,7 +650,6 @@
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 @@
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 728020e..b3a24af 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 @@
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 8233682..f4f3ae9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -69,7 +69,11 @@
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 5a70167..ed3b0ec 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1017,9 +1017,8 @@
: 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 @@
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 @@
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 ef68a84..6ef6e44 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -180,6 +180,20 @@
}
/**
+ * 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 ca0ef2a..b23b88a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -133,10 +133,10 @@
const auto& motionEvent = static_cast<const MotionEvent&>(event);
EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
EXPECT_EQ(motionEvent.getAction(), args.action);
- EXPECT_EQ(motionEvent.getX(0), point.x);
- EXPECT_EQ(motionEvent.getY(0), point.y);
- EXPECT_EQ(motionEvent.getRawX(0), point.x);
- EXPECT_EQ(motionEvent.getRawY(0), point.y);
+ EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION);
});
}
@@ -2201,6 +2201,59 @@
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>();
@@ -2440,8 +2493,7 @@
public:
void SetUp() override {
InputDispatcherTest::SetUp();
- mDisplayInfos.clear();
- mWindowInfos.clear();
+ removeAllWindowsAndDisplays();
}
void addDisplayInfo(int displayId, const ui::Transform& transform) {
@@ -2457,6 +2509,11 @@
mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
}
+ void removeAllWindowsAndDisplays() {
+ mDisplayInfos.clear();
+ mWindowInfos.clear();
+ }
+
// Set up a test scenario where the display has a scaled projection and there are two windows
// on the display.
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
@@ -2488,11 +2545,11 @@
std::vector<gui::WindowInfo> mWindowInfos;
};
-TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestCoordinateSpaceConsistency) {
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
// Send down to the first window. The point is represented in the display space. The point is
- // selected so that if the hit test was done with the transform applied to it, then it would
- // end up in the incorrect window.
+ // selected so that if the hit test was performed with the point and the bounds being in
+ // different coordinate spaces, the event would end up in the incorrect window.
NotifyMotionArgs downMotionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
@@ -2567,6 +2624,78 @@
EXPECT_EQ(80, event->getY(0));
}
+/** Ensure consistent behavior of InputDispatcher in all orientations. */
+class InputDispatcherDisplayOrientationFixture
+ : public InputDispatcherDisplayProjectionTest,
+ public ::testing::WithParamInterface<ui::Rotation> {};
+
+// This test verifies the touchable region of a window for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) {
+ constexpr static int32_t displayWidth = 400;
+ constexpr static int32_t displayHeight = 800;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ const auto rotation = GetParam();
+
+ // Set up the display with the specified rotation.
+ const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+ const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+ const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+ const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+ logicalDisplayWidth, logicalDisplayHeight);
+ addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+ // Create a window with its bounds determined in the logical display.
+ const Rect frameInLogicalDisplay(100, 100, 200, 300);
+ const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(frameInDisplay, displayTransform);
+ addWindow(window);
+
+ // The following points in logical display space should be inside the window.
+ static const std::array<vec2, 4> insidePoints{
+ {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+ for (const auto pointInsideWindow : insidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+ window->consumeMotionDown();
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ window->consumeMotionUp();
+ }
+
+ // The following points in logical display space should be outside the window.
+ static const std::array<vec2, 5> outsidePoints{
+ {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+ for (const auto pointOutsideWindow : outsidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ }
+ window->assertNoEvents();
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
+ InputDispatcherDisplayOrientationFixture,
+ ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+ ui::ROTATION_270));
+
using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
sp<IBinder>, sp<IBinder>)>;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8a97901..03fbf07 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -246,7 +246,7 @@
mSpotsByDisplay[displayId] = newSpots;
}
- void clearSpots() override {}
+ void clearSpots() override { mSpotsByDisplay.clear(); }
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
@@ -1014,7 +1014,9 @@
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 @@
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 @@
// 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 @@
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/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
index 4676167..9e41a93 100644
--- a/services/memtrackproxy/MemtrackProxy.cpp
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -97,9 +97,14 @@
return calling_pid == request_pid;
}
-MemtrackProxy::MemtrackProxy()
- : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
- memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+MemtrackProxy::MemtrackProxy() {
+ memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance();
+
+ // Only check for a HIDL implementation if we failed to get the AIDL service
+ if (!memtrack_aidl_instance_) {
+ memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance();
+ }
+}
ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
std::vector<MemtrackRecord>* _aidl_return) {
diff --git a/services/powermanager/tests/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
index b62be5f..7414958 100644
--- a/services/powermanager/tests/IThermalManagerTest.cpp
+++ b/services/powermanager/tests/IThermalManagerTest.cpp
@@ -33,26 +33,38 @@
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 @@
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 @@
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 @@
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 @@
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 f67c610..f5b360f 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 ::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 3c4f8d9..a2042d6 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -75,6 +75,7 @@
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 0000000..34d1de7
--- /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 0000000..cab53c1
--- /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 0000000..d9ea73d
--- /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 0000000..c394709
--- /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 0000000..0ae1eba
--- /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 0000000..9b03344
--- /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 0000000..0d6e476
--- /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 0000000..ee8ceb3
--- /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 <fakeservicemanager/FakeServiceManager.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::FakeServiceManager> fakeServiceManager = new android::FakeServiceManager();
+ 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 0000000..c77ee88
--- /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 0000000..beb38b9
--- /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 0000000..06ba59e
--- /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 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
::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 0000000..cead3a0
--- /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 7fea616..6b99627 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -13,11 +13,15 @@
"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",
+ "libexpresslog",
"libhidlbase",
"liblog",
"libstatslog",
@@ -29,10 +33,12 @@
],
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 a3b68f1..b22f903 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -14,63 +14,259 @@
* limitations under the License.
*/
-#define DEBUG false // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#define LOG_TAG "StatsAidl"
-#include <log/log.h>
-#include <statslog.h>
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
#include "StatsAidl.h"
+#include <Counter.h>
+#include <log/log.h>
+#include <stats_annotations.h>
+#include <stats_event.h>
+#include <statslog.h>
+
+#include <unordered_map>
+
+namespace {
+ static const char* g_AtomErrorMetricName =
+ "statsd_errors.value_report_vendor_atom_errors_count";
+}
+
namespace aidl {
namespace android {
namespace frameworks {
namespace stats {
-StatsHal::StatsHal() {}
+using ::android::expresslog::Counter;
+
+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);
+ Counter::logIncrement(g_AtomErrorMetricName);
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());
+ Counter::logIncrement(g_AtomErrorMetricName);
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)) {
+ AStatsEvent_release(event);
+ ALOGE("Atom ID %ld has incompatible atom level annotation", (long)vendorAtom.atomId);
+ Counter::logIncrement(g_AtomErrorMetricName);
+ 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);
+ Counter::logIncrement(g_AtomErrorMetricName);
+ 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)) {
+ AStatsEvent_release(event);
+ ALOGE("Atom ID %ld has incompatible field level annotation for field #%ld",
+ (long)vendorAtom.atomId, (long)atomValueIdx + 2);
+ Counter::logIncrement(g_AtomErrorMetricName);
+ 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);
+ Counter::logIncrement(g_AtomErrorMetricName);
+ }
+ 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 ae0a984..19176d9 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 @@
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::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 @@
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
similarity index 93%
rename from services/stats/android.frameworks.stats@1.0-service.xml
rename to services/stats/android.frameworks.stats-service.xml
index c564b7b..7e2635e 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 219e71e..340b539 100644
--- a/services/stats/include/stats/StatsAidl.h
+++ b/services/stats/include/stats/StatsAidl.h
@@ -28,8 +28,7 @@
/**
* 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 071e54f..864ad14 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 @@
using android::hardware::Return;
/**
-* Implements the Stats HAL
-*/
+ * Implements the Stats HAL
+ */
class StatsHal : public IStats {
public:
StatsHal();
@@ -50,12 +49,12 @@
* 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 @@
/**
* 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 000a2cb..cbb95f9 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -280,7 +280,7 @@
"liblog",
],
static_libs: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
@@ -288,6 +288,6 @@
"libui",
],
export_static_lib_headers: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
}
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 3875f15..e06f3c4 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -459,6 +459,7 @@
}
bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+ if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
mDrawingState.surfaceDamageRegion = surfaceDamage;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 8e4e9af..24a7744 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 @@
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 79dcd15..3651231 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -264,17 +264,21 @@
}
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 c52e96d..8364ed9 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -826,25 +826,24 @@
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 a2305af..3611dea 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -314,12 +314,12 @@
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 4593b40..a31cdf0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -72,6 +72,7 @@
#include "LayerProtoHelper.h"
#include "LayerRejecter.h"
#include "MonitoredProducer.h"
+#include "MutexUtils.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
@@ -254,10 +255,12 @@
auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current);
std::sort(layersInTree.begin(), layersInTree.end());
- traverse(LayerVector::StateSet::Current, [&](Layer* layer) {
- layer->removeFromCurrentState();
- layer->removeRelativeZ(layersInTree);
- });
+ REQUIRE_MUTEX(mFlinger->mStateLock);
+ traverse(LayerVector::StateSet::Current,
+ [&](Layer* layer) REQUIRES(layer->mFlinger->mStateLock) {
+ layer->removeFromCurrentState();
+ layer->removeRelativeZ(layersInTree);
+ });
}
void Layer::addToCurrentState() {
@@ -936,10 +939,12 @@
mFlinger->mLayersAdded = true;
// set up SF to handle added color layer
if (isRemovedFromCurrentState()) {
+ MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock);
mDrawingState.bgColorLayer->onRemovedFromCurrentState();
}
mFlinger->setTransactionFlags(eTransactionNeeded);
} else if (mDrawingState.bgColorLayer && alpha == 0) {
+ MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock);
mDrawingState.bgColorLayer->reparent(nullptr);
mDrawingState.bgColorLayer = nullptr;
return true;
@@ -1587,8 +1592,10 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
+ const float parentShadowRadius =
+ newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius;
child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
- newParent->mEffectiveShadowRadius);
+ parentShadowRadius);
}
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1724c15..5ffcabf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -409,7 +409,7 @@
virtual ui::LayerStack getLayerStack() const;
virtual bool setMetadata(const LayerMetadata& data);
virtual void setChildrenDrawingParent(const sp<Layer>&);
- virtual bool reparent(const sp<IBinder>& newParentHandle);
+ virtual bool reparent(const sp<IBinder>& newParentHandle) REQUIRES(mFlinger->mStateLock);
virtual bool setColorTransform(const mat4& matrix);
virtual mat4 getColorTransform() const;
virtual bool hasColorTransform() const;
@@ -433,7 +433,8 @@
virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
virtual bool setTransactionCompletedListeners(
const std::vector<sp<CallbackHandle>>& /*handles*/);
- virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
+ virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace)
+ REQUIRES(mFlinger->mStateLock);
virtual bool setColorSpaceAgnostic(const bool agnostic);
virtual bool setDimmingEnabled(const bool dimmingEnabled);
virtual bool setFrameRateSelectionPriority(int32_t priority);
@@ -730,13 +731,13 @@
/*
* Remove from current state and mark for removal.
*/
- void removeFromCurrentState();
+ void removeFromCurrentState() REQUIRES(mFlinger->mStateLock);
/*
* called with the state lock from a binder thread when the layer is
* removed from the current list to the pending removal list
*/
- void onRemovedFromCurrentState();
+ void onRemovedFromCurrentState() REQUIRES(mFlinger->mStateLock);
/*
* Called when the layer is added back to the current state list.
@@ -914,6 +915,9 @@
virtual bool simpleBufferUpdate(const layer_state_t&) const { return false; }
+ // Exposed so SurfaceFlinger can assert that it's held
+ const sp<SurfaceFlinger> mFlinger;
+
protected:
friend class impl::SurfaceInterceptor;
@@ -989,9 +993,6 @@
*/
virtual Rect getInputBounds() const;
- // constant
- sp<SurfaceFlinger> mFlinger;
-
bool mPremultipliedAlpha{true};
const std::string mName;
const std::string mTransactionName{"TX - " + mName};
diff --git a/services/surfaceflinger/MutexUtils.h b/services/surfaceflinger/MutexUtils.h
index f8be6f3..58f7cb4 100644
--- a/services/surfaceflinger/MutexUtils.h
+++ b/services/surfaceflinger/MutexUtils.h
@@ -50,4 +50,14 @@
const status_t status;
};
+// Require, under penalty of compilation failure, that the compiler thinks that a mutex is held.
+#define REQUIRE_MUTEX(expr) ([]() REQUIRES(expr) {})()
+
+// Tell the compiler that we know that a mutex is held.
+#define ASSERT_MUTEX(expr) ([]() ASSERT_CAPABILITY(expr) {})()
+
+// Specify that one mutex is an alias for another.
+// (e.g. SurfaceFlinger::mStateLock and Layer::mFlinger->mStateLock)
+#define MUTEX_ALIAS(held, alias) (REQUIRE_MUTEX(held), ASSERT_MUTEX(alias))
+
} // namespace android
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 2ece51c..4e7da82 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -2,6 +2,9 @@
alecmouri@google.com
chaviw@google.com
lpy@google.com
+pdwilliams@google.com
racarr@google.com
+ramindani@google.com
+rnlee@google.com
scroggo@google.com
-vishnun@google.com
\ No newline at end of file
+vishnun@google.com
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 5de796d..8df9ba5 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -57,7 +57,4 @@
"libgtest",
"libscheduler",
],
- sanitize: {
- address: true,
- },
}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 13cd304..e23945d 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 1dfb1d3..8c46515 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -328,8 +328,9 @@
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 @@
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 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 @@
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 =
@@ -3810,7 +3830,7 @@
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
@@ -3904,14 +3924,25 @@
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) {
@@ -4403,6 +4434,7 @@
}
return 0;
}
+ MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
// Only set by BLAST adapter layers
if (what & layer_state_t::eProducerDisconnect) {
@@ -7292,6 +7324,7 @@
ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get());
return;
}
+ MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
sp<Layer> parent;
bool addToRoot = state.addToRoot;
@@ -7636,7 +7669,7 @@
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 a78144d..d9add5c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -773,6 +773,9 @@
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 @@
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)
@@ -855,7 +859,7 @@
// this layer meaning it is entirely safe to destroy all
// resources associated to this layer.
void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
- void markLayerPendingRemovalLocked(const sp<Layer>& layer);
+ void markLayerPendingRemovalLocked(const sp<Layer>& layer) REQUIRES(mStateLock);
// add a layer to SurfaceFlinger
status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
@@ -1228,7 +1232,8 @@
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 @@
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/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index cab33ae..57752b7 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -5,6 +5,14 @@
},
{
"name": "libcompositionengine_test"
+ },
+ {
+ "name": "libscheduler_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "libscheduler_test"
}
]
}
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index ec18054..883766b 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -139,11 +139,6 @@
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 ceddf27..6ecf78b 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -63,7 +63,7 @@
"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",
@@ -122,6 +122,9 @@
],
cpp_std: "experimental",
gnu_extensions: false,
+ data: [
+ ":SurfaceFlinger_test",
+ ],
}
subdirs = [
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index d33bc10..434c297 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -52,19 +52,12 @@
#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 @@
}
/**
- * 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 @@
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 @@
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, 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 @@
};
// 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 @@
// 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 fb4458a..9162674 100644
--- a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -80,14 +80,6 @@
} // 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 004f31c..339d746 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -138,7 +138,7 @@
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 54cf5b7..cfa8a08 100644
--- a/services/utils/tests/Android.bp
+++ b/services/utils/tests/Android.bp
@@ -34,5 +34,4 @@
"libgmock",
"libserviceutils",
],
- clang: true,
}
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 2002bdf..5403baf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -59,6 +59,12 @@
"-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 80e9a3c..0000000
--- 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 fa71ed7..0000000
--- 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 be1ec5b..0000000
--- 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 aa70de1..0000000
--- 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 84abc19..0000000
--- 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 a200345..0000000
--- 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 eee9d13..0000000
--- 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 db7d5dc..0000000
--- 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 a82df7f..0000000
--- 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 c3621eb..0000000
--- 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 6d2ac09..0000000
--- 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 9486f3c..0000000
--- 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 c4216f6..0000000
--- 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 dd1603d..0000000
--- 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 1b2b5f4..0000000
--- 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 e530b16..0000000
--- 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 3e3a630..0000000
--- 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 2e70928..0000000
--- 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 d93f370..0000000
--- 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 1273352..0000000
--- 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 ba98696..40cf9fb 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -49,7 +49,13 @@
* 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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 440c5b1..a87f82f 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -27,6 +27,9 @@
symbol_file: "libvulkan.map.txt",
first_version: "24",
unversioned_until: "current",
+ export_header_libs: [
+ "ndk_vulkan_headers",
+ ],
}
cc_library_shared {
@@ -37,7 +40,6 @@
"vulkan_headers",
],
},
- clang: true,
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
"vkGetPhysicalDeviceMemoryProperties2",
"vkGetPhysicalDeviceMemoryProperties2KHR",
"vkGetPhysicalDeviceMultisamplePropertiesEXT",
+ "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
"vkGetPhysicalDevicePresentRectanglesKHR",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7664518..a99355f 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,6 +1027,51 @@
}
}
+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 @@
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 @@
}
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
- VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
ATRACE_END();
@@ -1254,15 +1342,18 @@
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 @@
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 14c516b..4d2bbd6 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -107,6 +107,8 @@
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 b436db1..de98aa7 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -496,6 +496,13 @@
nullptr,
},
{
+ "vkGetSwapchainGrallocUsage3ANDROID",
+ ProcHook::DEVICE,
+ ProcHook::ANDROID_native_buffer,
+ nullptr,
+ nullptr,
+ },
+ {
"vkGetSwapchainGrallocUsageANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -664,6 +671,7 @@
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 079f9cc..2f60086 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -123,6 +123,7 @@
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 f49e8f3..b189c68 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -178,6 +178,7 @@
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 c4b1487..c46036a 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 {
+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,9 @@
case VK_FORMAT_R8_UNORM:
native_format = android::PIXEL_FORMAT_R_8;
break;
+ case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+ native_format = android::PIXEL_FORMAT_RGBA_10101010;
+ break;
default:
ALOGV("unsupported swapchain format %d", format);
break;
@@ -646,10 +671,11 @@
// 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 +707,9 @@
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 +718,17 @@
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 +757,6 @@
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 +767,15 @@
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 +787,16 @@
// 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 +811,21 @@
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 +840,10 @@
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 +852,40 @@
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});
+ }
+ }
+
+ bool rgba10x6_formats_ext = false;
+ uint32_t exts_count;
+ const auto& driver = GetData(pdev).driver;
+ driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
+ nullptr);
+ std::vector<VkExtensionProperties> props(exts_count);
+ driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
+ props.data());
+ for (uint32_t i = 0; i < exts_count; i++) {
+ VkExtensionProperties prop = props[i];
+ if (strcmp(prop.extensionName,
+ VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME) == 0) {
+ rgba10x6_formats_ext = true;
+ }
+ }
+ desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
+ if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) {
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 +963,87 @@
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 +1069,7 @@
// VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and
// VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR. We technically cannot
// know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a
- // surface, and that cannot be relied upon.
- present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
+ // surface, and that cannot be relied upon. Therefore, don't return it.
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
} else {
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -1228,11 +1357,15 @@
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 +1456,48 @@
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 +1508,11 @@
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 +1521,9 @@
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 +1563,7 @@
#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 +1621,7 @@
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 0daad9c..a6d540b 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -27,7 +27,6 @@
proprietary: true,
relative_install_path: "hw",
- clang: true,
cflags: [
"-fvisibility=hidden",
"-fstrict-aliasing",
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3c91150..f998b1a 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -948,6 +948,17 @@
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 f6dcf09..0cb7bd3 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -261,6 +261,7 @@
{"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 3e003e3..5c7fea0 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -209,6 +209,7 @@
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 4176509..c25c6cb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -69,6 +69,7 @@
_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 fa0258b..b544245 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -25,10 +25,8 @@
".",
],
shared_libs: [
- "libvulkan",
- ],
- whole_static_libs: [
"libjsoncpp",
+ "libvulkan",
],
export_shared_lib_headers: [
"libvulkan",
@@ -37,7 +35,6 @@
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 da6b00a..0284192 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -731,7 +731,7 @@
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>