Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I70ea776b8589ac3a7982c710c5c8b2941d86e55b
Change-Id: Ic1d535e9d2d6f80d95215240dbdb024995b045f8
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index fc3572c..2519ffa 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -124,6 +124,7 @@
{ "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } },
{ "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } },
{ "rro", "Runtime Resource Overlay", ATRACE_TAG_RRO, { } },
+ { "sysprop", "System Property", ATRACE_TAG_SYSPROP, { } },
{ k_coreServiceCategory, "Core services", 0, { } },
{ k_pdxServiceCategory, "PDX services", 0, { } },
{ "sched", "CPU Scheduling", 0, {
@@ -172,6 +173,8 @@
{ OPT, "events/clk/clk_enable/enable" },
{ OPT, "events/power/cpu_frequency_limits/enable" },
{ OPT, "events/power/suspend_resume/enable" },
+ { OPT, "events/cpuhp/cpuhp_enter/enable" },
+ { OPT, "events/cpuhp/cpuhp_exit/enable" },
} },
{ "membus", "Memory Bus Utilization", 0, {
{ REQ, "events/memory_bus/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index d2ccef1..994375b 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -47,6 +47,10 @@
chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_enter/enable
+ chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_enter/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit/enable
+ chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_exit/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable
chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 581d3de..af41643 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -78,6 +78,7 @@
#include <hardware_legacy/power.h>
#include <hidl/ServiceManagement.h>
#include <log/log.h>
+#include <log/log_read.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -661,14 +662,24 @@
static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000;
-/* timeout in ms to read a list of buffers */
+// Returns the actual readable size of the given buffer or -1 on error.
+static long logcat_buffer_readable_size(const std::string& buffer) {
+ std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+ android_logger_list_alloc(0, 0, 0), &android_logger_list_free};
+ auto logger = android_logger_open(logger_list.get(), android_name_to_log_id(buffer.c_str()));
+
+ return android_logger_get_log_readable_size(logger);
+}
+
+// Returns timeout in ms to read a list of buffers.
static unsigned long logcat_timeout(const std::vector<std::string>& buffers) {
unsigned long timeout_ms = 0;
for (const auto& buffer : buffers) {
- log_id_t id = android_name_to_log_id(buffer.c_str());
- unsigned long property_size = __android_logger_get_buffer_size(id);
- /* Engineering margin is ten-fold our guess */
- timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf;
+ long readable_size = logcat_buffer_readable_size(buffer);
+ if (readable_size > 0) {
+ // Engineering margin is ten-fold our guess.
+ timeout_ms += 10 * (readable_size + worst_write_perf) / worst_write_perf;
+ }
}
return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
}
@@ -1296,12 +1307,12 @@
static void DumpHals() {
if (!ds.IsZipping()) {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
return;
}
DurationReporter duration_reporter("DUMP HALS");
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
using android::hidl::manager::V1_0::IServiceManager;
@@ -1481,8 +1492,7 @@
RunCommand("LSMOD", {"lsmod"});
}
- if (__android_logger_property_get_bool(
- "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) {
+ if (android::base::GetBoolProperty("ro.logd.kernel", false)) {
DoKernelLogcat();
} else {
do_dmesg();
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 8ff4dd8..523115f 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -17,15 +17,15 @@
"InstalldNativeService.cpp",
"QuotaUtils.cpp",
"dexopt.cpp",
+ "execv_helper.cpp",
"globals.cpp",
+ "run_dex2oat.cpp",
+ "unique_file.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
":installd_aidl",
],
- header_libs: [
- "dex2oat_headers",
- ],
shared_libs: [
"libbase",
"libbinder",
@@ -103,6 +103,28 @@
}
//
+// Unit tests
+//
+
+cc_test_host {
+ name: "run_dex2oat_test",
+ test_suites: ["general-tests"],
+ clang: true,
+ srcs: [
+ "run_dex2oat_test.cpp",
+ "run_dex2oat.cpp",
+ "unique_file.cpp",
+ "execv_helper.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "server_configurable_flags",
+ ],
+ test_config: "run_dex2oat_test.xml",
+}
+
+//
// Executable
//
@@ -205,18 +227,18 @@
srcs: [
"dexopt.cpp",
+ "execv_helper.cpp",
"globals.cpp",
"otapreopt.cpp",
"otapreopt_utils.cpp",
+ "run_dex2oat.cpp",
+ "unique_file.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
],
- header_libs: ["dex2oat_headers"],
-
static_libs: [
- "libartimagevalues",
"libdiskusage",
"libotapreoptparameters",
],
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index 9a21104..fc745d0 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -1,6 +1,5 @@
set noparent
-agampe@google.com
calin@google.com
jsharkey@android.com
maco@google.com
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index c6583a1..3f0fb6d 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -15,6 +15,9 @@
{
"name": "installd_utils_test"
},
+ {
+ "name": "run_dex2oat_test"
+ },
// AdoptableHostTest moves packages, part of which is handled by installd
{
"name": "AdoptableHostTest"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 82be007..5076ae6 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -39,7 +39,6 @@
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
-#include <dex2oat_return_codes.h>
#include <log/log.h> // TODO: Move everything to base/logging.
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
@@ -50,11 +49,15 @@
#include "dexopt.h"
#include "dexopt_return_codes.h"
+#include "execv_helper.h"
#include "globals.h"
#include "installd_deps.h"
#include "otapreopt_utils.h"
+#include "run_dex2oat.h"
+#include "unique_file.h"
#include "utils.h"
+using android::base::Basename;
using android::base::EndsWith;
using android::base::GetBoolProperty;
using android::base::GetProperty;
@@ -67,16 +70,6 @@
namespace android {
namespace installd {
-// Should minidebug info be included in compiled artifacts? Even if this value is
-// "true," usage might still be conditional to other constraints, e.g., system
-// property overrides.
-static constexpr bool kEnableMinidebugInfo = true;
-
-static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
-static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
-static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
-static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
-
// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
struct FreeDelete {
@@ -186,93 +179,6 @@
return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
}
-static std::vector<std::string> SplitBySpaces(const std::string& str) {
- if (str.empty()) {
- return {};
- }
- return android::base::Split(str, " ");
-}
-
-static const char* get_location_from_path(const char* path) {
- static constexpr char kLocationSeparator = '/';
- const char *location = strrchr(path, kLocationSeparator);
- if (location == nullptr) {
- return path;
- } else {
- // Skip the separator character.
- return location + 1;
- }
-}
-
-// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
-// need to be performed between the fork and exec.
-class ExecVHelper {
- public:
- // Store a placeholder for the binary name.
- ExecVHelper() : args_(1u, std::string()) {}
-
- void PrepareArgs(const std::string& bin) {
- CHECK(!args_.empty());
- CHECK(args_[0].empty());
- args_[0] = bin;
- // Write char* into array.
- for (const std::string& arg : args_) {
- argv_.push_back(arg.c_str());
- }
- argv_.push_back(nullptr); // Add null terminator.
- }
-
- [[ noreturn ]]
- void Exec(int exit_code) {
- execv(argv_[0], (char * const *)&argv_[0]);
- PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
- exit(exit_code);
- }
-
- // Add an arg if it's not empty.
- void AddArg(const std::string& arg) {
- if (!arg.empty()) {
- args_.push_back(arg);
- }
- }
-
- // Add a runtime arg if it's not empty.
- void AddRuntimeArg(const std::string& arg) {
- if (!arg.empty()) {
- args_.push_back("--runtime-arg");
- args_.push_back(arg);
- }
- }
-
- protected:
- // Holder arrays for backing arg storage.
- std::vector<std::string> args_;
-
- // Argument poiners.
- std::vector<const char*> argv_;
-};
-
-static std::string MapPropertyToArg(const std::string& property,
- const std::string& format,
- const std::string& default_value = "") {
- std::string prop = GetProperty(property, default_value);
- if (!prop.empty()) {
- return StringPrintf(format.c_str(), prop.c_str());
- }
- return "";
-}
-
-static std::string MapPropertyToArgWithBackup(const std::string& property,
- const std::string& backupProperty,
- const std::string& format,
- const std::string& default_value = "") {
- std::string value = GetProperty(property, default_value);
- if (!value.empty()) {
- return StringPrintf(format.c_str(), value.c_str());
- }
- return MapPropertyToArg(backupProperty, format, default_value);
-}
-
// Determines which binary we should use for execution (the debug or non-debug version).
// e.g. dex2oatd vs dex2oat
static const char* select_execution_binary(const char* binary, const char* debug_binary,
@@ -311,9 +217,6 @@
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
// Feature flag name for running the JIT in Zygote experiment, b/119800099.
static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
-// Location of the JIT Zygote image.
-static const char* kJitZygoteImage =
- "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
// Phenotype property name for enabling profiling the boot class path.
static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
@@ -328,288 +231,11 @@
return profile_boot_class_path == "true";
}
-class RunDex2Oat : public ExecVHelper {
- public:
- RunDex2Oat(int zip_fd,
- int oat_fd,
- int input_vdex_fd,
- int output_vdex_fd,
- int image_fd,
- const char* input_file_name,
- const char* output_file_name,
- int swap_fd,
- const char* instruction_set,
- const char* compiler_filter,
- bool debuggable,
- bool post_bootcomplete,
- bool for_restore,
- bool background_job_compile,
- int profile_fd,
- const char* class_loader_context,
- const std::string& class_loader_context_fds,
- int target_sdk_version,
- bool enable_hidden_api_checks,
- bool generate_compact_dex,
- int dex_metadata_fd,
- const char* compilation_reason) {
- // Get the relative path to the input file.
- const char* relative_input_file_name = get_location_from_path(input_file_name);
-
- std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
- std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
-
- std::string threads_format = "-j%s";
- std::string dex2oat_threads_arg = post_bootcomplete
- ? (for_restore
- ? MapPropertyToArgWithBackup(
- "dalvik.vm.restore-dex2oat-threads",
- "dalvik.vm.dex2oat-threads",
- threads_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
- : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
- std::string cpu_set_format = "--cpu-set=%s";
- std::string dex2oat_cpu_set_arg = post_bootcomplete
- ? (for_restore
- ? MapPropertyToArgWithBackup(
- "dalvik.vm.restore-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);
-
- std::string bootclasspath;
- char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
- if (dex2oat_bootclasspath != nullptr) {
- bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);
- }
- // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
- // BOOTCLASSPATH.
-
- const std::string dex2oat_isa_features_key =
- StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
- std::string instruction_set_features_arg =
- MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
-
- const std::string dex2oat_isa_variant_key =
- StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
- std::string instruction_set_variant_arg =
- MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
-
- const char* dex2oat_norelocation = "-Xnorelocate";
-
- const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
- std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
- ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
-
- // If we are booting without the real /data, don't spend time compiling.
- std::string vold_decrypt = GetProperty("vold.decrypt", "");
- bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
- vold_decrypt == "1";
-
- std::string updatable_bcp_packages =
- MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
- "--updatable-bcp-packages-file=%s");
- if (updatable_bcp_packages.empty()) {
- // Make dex2oat fail by providing non-existent file name.
- updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
- }
-
- std::string resolve_startup_string_arg =
- MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",
- "--resolve-startup-const-strings=%s");
- if (resolve_startup_string_arg.empty()) {
- // If empty, fall back to system property.
- resolve_startup_string_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
- "--resolve-startup-const-strings=%s");
- }
-
- const std::string image_block_size_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
- "--max-image-block-size=%s");
-
- const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
-
- std::string image_format_arg;
- if (image_fd >= 0) {
- image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
- }
-
- std::string dex2oat_large_app_threshold_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
-
-
-
- // Decide whether to use dex2oat64.
- bool use_dex2oat64 = false;
- // Check whether the device even supports 64-bit ABIs.
- if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
- use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false);
- }
- const char* dex2oat_bin = select_execution_binary(
- (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path),
- (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
- background_job_compile);
-
- bool generate_minidebug_info = kEnableMinidebugInfo &&
- GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
-
- std::string boot_image;
- std::string use_jitzygote_image =
- server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
- ENABLE_JITZYGOTE_IMAGE,
- /*default_value=*/ "");
-
- if (use_jitzygote_image == "true" || IsBootClassPathProfilingEnable()) {
- boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
- } else {
- boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
- }
-
- // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
- // use arraysize instead.
- std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
- std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name);
- std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
- std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
- std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
- std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
- std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
- std::string dex2oat_compiler_filter_arg;
- std::string dex2oat_swap_fd;
- std::string dex2oat_image_fd;
- std::string target_sdk_version_arg;
- if (target_sdk_version != 0) {
- target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
- }
- std::string class_loader_context_arg;
- std::string class_loader_context_fds_arg;
- if (class_loader_context != nullptr) {
- class_loader_context_arg = StringPrintf("--class-loader-context=%s",
- class_loader_context);
- if (!class_loader_context_fds.empty()) {
- class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",
- class_loader_context_fds.c_str());
- }
- }
-
- if (swap_fd >= 0) {
- dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
- }
- if (image_fd >= 0) {
- dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
- }
-
- // Compute compiler filter.
- bool have_dex2oat_relocation_skip_flag = false;
- if (skip_compilation) {
- dex2oat_compiler_filter_arg = "--compiler-filter=extract";
- have_dex2oat_relocation_skip_flag = true;
- } else if (compiler_filter != nullptr) {
- dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
- }
-
- if (dex2oat_compiler_filter_arg.empty()) {
- dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
- "--compiler-filter=%s");
- }
-
- // Check whether all apps should be compiled debuggable.
- if (!debuggable) {
- debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
- }
- std::string profile_arg;
- if (profile_fd != -1) {
- profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
- }
-
- // Get the directory of the apk to pass as a base classpath directory.
- std::string base_dir;
- std::string apk_dir(input_file_name);
- unsigned long dir_index = apk_dir.rfind('/');
- bool has_base_dir = dir_index != std::string::npos;
- if (has_base_dir) {
- apk_dir = apk_dir.substr(0, dir_index);
- base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
- }
-
- std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
-
- std::string compilation_reason_arg = compilation_reason == nullptr
- ? ""
- : std::string("--compilation-reason=") + compilation_reason;
-
- ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
-
- // Disable cdex if update input vdex is true since this combination of options is not
- // supported.
- const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
-
- AddArg(zip_fd_arg);
- AddArg(zip_location_arg);
- AddArg(input_vdex_fd_arg);
- AddArg(output_vdex_fd_arg);
- AddArg(oat_fd_arg);
- AddArg(oat_location_arg);
- AddArg(instruction_set_arg);
-
- AddArg(instruction_set_variant_arg);
- AddArg(instruction_set_features_arg);
-
- AddArg(boot_image);
-
- AddRuntimeArg(bootclasspath);
- AddRuntimeArg(dex2oat_Xms_arg);
- AddRuntimeArg(dex2oat_Xmx_arg);
-
- AddArg(updatable_bcp_packages);
- AddArg(resolve_startup_string_arg);
- AddArg(image_block_size_arg);
- AddArg(dex2oat_compiler_filter_arg);
- AddArg(dex2oat_threads_arg);
- AddArg(dex2oat_cpu_set_arg);
- AddArg(dex2oat_swap_fd);
- AddArg(dex2oat_image_fd);
-
- if (generate_debug_info) {
- AddArg("--generate-debug-info");
- }
- if (debuggable) {
- AddArg("--debuggable");
- }
- AddArg(image_format_arg);
- AddArg(dex2oat_large_app_threshold_arg);
-
- if (have_dex2oat_relocation_skip_flag) {
- AddRuntimeArg(dex2oat_norelocation);
- }
- AddArg(profile_arg);
- AddArg(base_dir);
- AddArg(class_loader_context_arg);
- AddArg(class_loader_context_fds_arg);
- if (generate_minidebug_info) {
- AddArg(kMinidebugDex2oatFlag);
- }
- if (disable_cdex) {
- AddArg(kDisableCompactDexFlag);
- }
- AddRuntimeArg(target_sdk_version_arg);
- if (enable_hidden_api_checks) {
- AddRuntimeArg("-Xhidden-api-policy:enabled");
- }
-
- if (dex_metadata_fd > -1) {
- AddArg(dex_metadata_fd_arg);
- }
-
- AddArg(compilation_reason_arg);
-
- // Do not add args after dex2oat_flags, they should override others for debugging.
- args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end());
-
- PrepareArgs(dex2oat_bin);
+static void UnlinkIgnoreResult(const std::string& path) {
+ if (unlink(path.c_str()) < 0) {
+ PLOG(ERROR) << "Failed to unlink " << path;
}
-};
+}
/*
* Whether dexopt should use a swap file when compiling an APK.
@@ -727,6 +353,16 @@
return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
}
+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) {
+ 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);
+ return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
+ clear_profile(path);
+ });
+}
+
static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
const std::string& location) {
std::string profile = create_snapshot_profile_path(package_name, location);
@@ -868,6 +504,7 @@
/*for_boot_image*/false);
}
+ using ExecVHelper::Exec; // To suppress -Wno-overloaded-virtual
void Exec() {
ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
}
@@ -1022,7 +659,7 @@
PLOG(ERROR) << "installd cannot open " << code_path.c_str();
return false;
}
- dex_locations.push_back(get_location_from_path(code_path.c_str()));
+ dex_locations.push_back(Basename(code_path));
apk_fds.push_back(std::move(apk_fd));
@@ -1216,118 +853,14 @@
return true;
}
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-// Dex2oatFileWrapper file(open(...),
-// [name]() {
-// unlink(name.c_str());
-// });
-// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-// wrapper if captured as a reference.
-//
-// if (file.get() == -1) {
-// // Error opening...
-// }
-//
-// ...
-// if (error) {
-// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-// // and delete the file (after the fd is closed).
-// return -1;
-// }
-//
-// (Success case)
-// file.SetCleanup(false);
-// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
-// // (leaving the file around; after the fd is closed).
-//
-class Dex2oatFileWrapper {
- public:
- Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) {
- }
-
- Dex2oatFileWrapper(int value, std::function<void ()> cleanup)
- : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
-
- Dex2oatFileWrapper(Dex2oatFileWrapper&& other) {
- value_ = other.value_;
- cleanup_ = other.cleanup_;
- do_cleanup_ = other.do_cleanup_;
- auto_close_ = other.auto_close_;
- other.release();
- }
-
- Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) {
- value_ = other.value_;
- cleanup_ = other.cleanup_;
- do_cleanup_ = other.do_cleanup_;
- auto_close_ = other.auto_close_;
- other.release();
- return *this;
- }
-
- ~Dex2oatFileWrapper() {
- reset(-1);
- }
-
- int get() {
- return value_;
- }
-
- void SetCleanup(bool cleanup) {
- do_cleanup_ = cleanup;
- }
-
- void reset(int new_value) {
- if (auto_close_ && value_ >= 0) {
- close(value_);
- }
- if (do_cleanup_ && cleanup_ != nullptr) {
- cleanup_();
- }
-
- value_ = new_value;
- }
-
- void reset(int new_value, std::function<void ()> new_cleanup) {
- if (auto_close_ && value_ >= 0) {
- close(value_);
- }
- if (do_cleanup_ && cleanup_ != nullptr) {
- cleanup_();
- }
-
- value_ = new_value;
- cleanup_ = new_cleanup;
- }
-
- void DisableAutoClose() {
- auto_close_ = false;
- }
-
- private:
- void release() {
- value_ = -1;
- do_cleanup_ = false;
- cleanup_ = nullptr;
- }
- int value_;
- std::function<void ()> cleanup_;
- bool do_cleanup_;
- bool auto_close_;
-};
-
// (re)Creates the app image if needed.
-Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
+UniqueFile maybe_open_app_image(const std::string& out_oat_path,
bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// In case there is a stale image, remove it now. Ignore any error.
@@ -1335,18 +868,19 @@
// Not enabled, exit.
if (!generate_app_image) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
if (app_image_format.empty()) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// Recreate is true since we do not want to modify a mapped image. If the app is
// already running and we modify the image file, it can cause crashes (b/27493510).
- Dex2oatFileWrapper wrapper_fd(
+ UniqueFile image_file(
open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
- [image_path]() { unlink(image_path.c_str()); });
- if (wrapper_fd.get() < 0) {
+ image_path,
+ UnlinkIgnoreResult);
+ if (image_file.fd() < 0) {
// Could not create application image file. Go on since we can compile without it.
LOG(ERROR) << "installd could not create '" << image_path
<< "' for image file during dexopt";
@@ -1357,21 +891,21 @@
}
}
} else if (!set_permissions_and_ownership(
- wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
+ image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
- wrapper_fd.reset(-1);
+ image_file.reset();
}
- return wrapper_fd;
+ return image_file;
}
// Creates the dexopt swap file if necessary and return its fd.
// Returns -1 if there's no need for a swap or in case of errors.
-unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+unique_fd maybe_open_dexopt_swap_file(const std::string& out_oat_path) {
if (!ShouldUseSwapFileForDexopt()) {
return invalid_unique_fd();
}
- auto swap_file_name = std::string(out_oat_path) + ".swap";
+ auto swap_file_name = out_oat_path + ".swap";
unique_fd swap_fd(open_output_file(
swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600));
if (swap_fd.get() < 0) {
@@ -1389,13 +923,13 @@
// Opens the reference profiles if needed.
// Note that the reference profile might not exist so it's OK if the fd will be -1.
-Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname,
+UniqueFile maybe_open_reference_profile(const std::string& pkgname,
const std::string& dex_path, const char* profile_name, bool profile_guided,
bool is_public, int uid, bool is_secondary_dex) {
// If we are not profile guided compilation, or we are compiling system server
// do not bother to open the profiles; we won't be using them.
if (!profile_guided || (pkgname[0] == '*')) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// If this is a secondary dex path which is public do not open the profile.
@@ -1407,7 +941,7 @@
// compiling with a public profile from the .dm file the PackageManager will
// set is_public toghether with the profile guided compilation.
if (is_secondary_dex && is_public) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// Open reference profile in read only mode as dex2oat does not get write permissions.
@@ -1417,33 +951,28 @@
} else {
if (profile_name == nullptr) {
// This path is taken for system server re-compilation lunched from ZygoteInit.
- return Dex2oatFileWrapper();
+ return UniqueFile();
} else {
location = profile_name;
}
}
- unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false,
- is_secondary_dex);
- const auto& cleanup = [pkgname, location, is_secondary_dex]() {
- clear_reference_profile(pkgname, location, is_secondary_dex);
- };
- return Dex2oatFileWrapper(ufd.release(), cleanup);
+ return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
+ is_secondary_dex);
}
-// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
-// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
+// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
+// out_vdex_wrapper. Returns true for success or false in case of errors.
bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
- bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
- Dex2oatFileWrapper* out_vdex_wrapper_fd) {
- CHECK(in_vdex_wrapper_fd != nullptr);
- CHECK(out_vdex_wrapper_fd != nullptr);
+ bool profile_guided, UniqueFile* in_vdex_wrapper,
+ UniqueFile* out_vdex_wrapper) {
+ CHECK(in_vdex_wrapper != nullptr);
+ CHECK(out_vdex_wrapper != nullptr);
// Open the existing VDEX. We do this before creating the new output VDEX, which will
// unlink the old one.
char in_odex_path[PKG_PATH_MAX];
int dexopt_action = abs(dexopt_needed);
bool is_odex_location = dexopt_needed < 0;
- std::string in_vdex_path_str;
// Infer the name of the output VDEX.
const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
@@ -1465,7 +994,7 @@
} else {
path = out_oat_path;
}
- in_vdex_path_str = create_vdex_filename(path);
+ std::string in_vdex_path_str = create_vdex_filename(path);
if (in_vdex_path_str.empty()) {
ALOGE("installd cannot compute input vdex location for '%s'\n", path);
return false;
@@ -1483,13 +1012,15 @@
!profile_guided;
if (update_vdex_in_place) {
// Open the file read-write to be able to update it.
- in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
- if (in_vdex_wrapper_fd->get() == -1) {
+ in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
+ in_vdex_path_str);
+ if (in_vdex_wrapper->fd() == -1) {
// If we failed to open the file, we cannot update it in place.
update_vdex_in_place = false;
}
} else {
- in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+ in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
+ in_vdex_path_str);
}
}
@@ -1498,22 +1029,24 @@
if (update_vdex_in_place) {
// We unlink the file in case the invocation of dex2oat fails, to ensure we don't
// have bogus stale vdex files.
- out_vdex_wrapper_fd->reset(
- in_vdex_wrapper_fd->get(),
- [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+ out_vdex_wrapper->reset(
+ in_vdex_wrapper->fd(),
+ out_vdex_path_str,
+ UnlinkIgnoreResult);
// Disable auto close for the in wrapper fd (it will be done when destructing the out
// wrapper).
- in_vdex_wrapper_fd->DisableAutoClose();
+ in_vdex_wrapper->DisableAutoClose();
} else {
- out_vdex_wrapper_fd->reset(
+ out_vdex_wrapper->reset(
open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
- [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
- if (out_vdex_wrapper_fd->get() < 0) {
+ out_vdex_path_str,
+ UnlinkIgnoreResult);
+ if (out_vdex_wrapper->fd() < 0) {
ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
return false;
}
}
- if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
+ if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
out_vdex_path_str.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
return false;
@@ -1524,25 +1057,24 @@
}
// Opens the output oat file for the given apk.
-// If successful it stores the output path into out_oat_path and returns true.
-Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
- bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
- char* out_oat_path) {
+UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
+ bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+ char out_oat_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
- const std::string out_oat_path_str(out_oat_path);
- Dex2oatFileWrapper wrapper_fd(
+ UniqueFile oat(
open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
- [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
- if (wrapper_fd.get() < 0) {
+ out_oat_path,
+ UnlinkIgnoreResult);
+ if (oat.fd() < 0) {
PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path;
} else if (!set_permissions_and_ownership(
- wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) {
+ oat.fd(), is_public, uid, out_oat_path, is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
- wrapper_fd.reset(-1);
+ oat.reset();
}
- return wrapper_fd;
+ return oat;
}
// Creates RDONLY fds for oat and vdex files, if exist.
@@ -2149,8 +1681,8 @@
}
// Open the input file.
- unique_fd input_fd(open(dex_path, O_RDONLY, 0));
- if (input_fd.get() < 0) {
+ UniqueFile in_dex(open(dex_path, O_RDONLY, 0), dex_path);
+ if (in_dex.fd() < 0) {
*error_msg = StringPrintf("installd cannot open '%s' for input during dexopt", dex_path);
LOG(ERROR) << *error_msg;
return -1;
@@ -2164,19 +1696,19 @@
}
// Create the output OAT file.
- char out_oat_path[PKG_PATH_MAX];
- Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
- instruction_set, is_secondary_dex, out_oat_path);
- if (out_oat_fd.get() < 0) {
+ UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+ instruction_set, is_secondary_dex);
+ if (out_oat.fd() < 0) {
*error_msg = "Could not open out oat file.";
return -1;
}
// Open vdex files.
- Dex2oatFileWrapper in_vdex_fd;
- Dex2oatFileWrapper out_vdex_fd;
- if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set,
- is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+ UniqueFile in_vdex;
+ UniqueFile out_vdex;
+ if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
+ instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
+ &out_vdex)) {
*error_msg = "Could not open vdex files.";
return -1;
}
@@ -2196,53 +1728,72 @@
}
// Create a swap file if necessary.
- unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+ unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat.path());
// Open the reference profile if needed.
- Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
+ UniqueFile reference_profile = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
- if (reference_profile_fd.get() == -1) {
+ 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.
generate_app_image = false;
}
// Create the app image file if needed.
- Dex2oatFileWrapper image_fd = maybe_open_app_image(
- out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);
+ UniqueFile out_image = maybe_open_app_image(
+ out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
- unique_fd dex_metadata_fd;
+ UniqueFile dex_metadata;
if (dex_metadata_path != nullptr) {
- dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
- if (dex_metadata_fd.get() < 0) {
+ dex_metadata.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)),
+ dex_metadata_path);
+ if (dex_metadata.fd() < 0) {
PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path;
}
}
+ std::string jitzygote_flag = server_configurable_flags::GetServerConfigurableFlag(
+ RUNTIME_NATIVE_BOOT_NAMESPACE,
+ ENABLE_JITZYGOTE_IMAGE,
+ /*default_value=*/ "");
+ bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable();
+
+ // Decide whether to use dex2oat64.
+ bool use_dex2oat64 = false;
+ // Check whether the device even supports 64-bit ABIs.
+ if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
+ use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false);
+ }
+ const char* dex2oat_bin = select_execution_binary(
+ (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path),
+ (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
+ background_job_compile);
+
+ auto execv_helper = std::make_unique<ExecVHelper>();
+
LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
- RunDex2Oat runner(input_fd.get(),
- out_oat_fd.get(),
- in_vdex_fd.get(),
- out_vdex_fd.get(),
- image_fd.get(),
- dex_path,
- out_oat_path,
+ RunDex2Oat runner(dex2oat_bin, execv_helper.get());
+ runner.Initialize(out_oat,
+ out_vdex,
+ out_image,
+ 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,
- background_job_compile,
- reference_profile_fd.get(),
- class_loader_context,
- join_fds(context_input_fds),
target_sdk_version,
enable_hidden_api_checks,
generate_compact_dex,
- dex_metadata_fd.get(),
+ use_jitzygote_image,
compilation_reason);
pid_t pid = fork();
@@ -2251,8 +1802,8 @@
drop_capabilities(uid);
SetDex2OatScheduling(boot_complete);
- if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
- PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
+ if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
+ PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
_exit(DexoptReturnCodes::kFlock);
}
@@ -2269,13 +1820,13 @@
}
}
- update_out_oat_access_times(dex_path, out_oat_path);
+ update_out_oat_access_times(dex_path, out_oat.path().c_str());
// We've been successful, don't delete output.
- out_oat_fd.SetCleanup(false);
- out_vdex_fd.SetCleanup(false);
- image_fd.SetCleanup(false);
- reference_profile_fd.SetCleanup(false);
+ out_oat.DisableCleanup();
+ out_vdex.DisableCleanup();
+ out_image.DisableCleanup();
+ reference_profile.DisableCleanup();
return 0;
}
diff --git a/cmds/installd/dexopt_return_codes.h b/cmds/installd/dexopt_return_codes.h
index bbecfa4..e5198ad 100644
--- a/cmds/installd/dexopt_return_codes.h
+++ b/cmds/installd/dexopt_return_codes.h
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <dex2oat_return_codes.h>
-
namespace android {
namespace installd {
@@ -70,48 +68,21 @@
return nullptr;
}
-inline const char* get_dex2oat_return_code_name(art::dex2oat::ReturnCode code) {
- switch (code) {
- case art::dex2oat::ReturnCode::kNoFailure:
- return "dex2oat success";
- case art::dex2oat::ReturnCode::kOther:
- return "unspecified dex2oat error";
- case art::dex2oat::ReturnCode::kCreateRuntime:
- return "dex2oat failed to create a runtime";
+inline const char* get_dex2oat_return_code_name(int code) {
+ if (code == 0) {
+ return "dex2oat success";
+ } else {
+ return "dex2oat error";
}
- return nullptr;
}
-// Get some slightly descriptive string for the return code. Handles both DexoptReturnCodes (local
-// exit codes) as well as art::dex2oat::ReturnCode.
+// Get some slightly descriptive string for the return code.
inline const char* get_return_code_name(int code) {
- // Try to enforce non-overlap (see comment on DexoptReturnCodes)
- // TODO: How could switch-case checks be used to enforce completeness?
- switch (code) {
- case kSetGid:
- case kSetUid:
- case kCapSet:
- case kFlock:
- case kProfmanExec:
- case kSetSchedPolicy:
- case kSetPriority:
- case kDex2oatExec:
- case kInstructionSetLength:
- case kHashValidatePath:
- case kHashOpenPath:
- case kHashReadDex:
- case kHashWrite:
- break;
- case static_cast<int>(art::dex2oat::ReturnCode::kNoFailure):
- case static_cast<int>(art::dex2oat::ReturnCode::kOther):
- case static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime):
- break;
- }
const char* value = get_installd_return_code_name(static_cast<DexoptReturnCodes>(code));
if (value != nullptr) {
return value;
}
- value = get_dex2oat_return_code_name(static_cast<art::dex2oat::ReturnCode>(code));
+ value = get_dex2oat_return_code_name(code);
return value;
}
diff --git a/cmds/installd/execv_helper.cpp b/cmds/installd/execv_helper.cpp
new file mode 100644
index 0000000..a2d240a
--- /dev/null
+++ b/cmds/installd/execv_helper.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "installd"
+
+#include "execv_helper.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+namespace android {
+namespace installd {
+
+// Store a placeholder for the binary name.
+ExecVHelper::ExecVHelper() : args_(1u, std::string()) {}
+
+ExecVHelper::~ExecVHelper() {}
+
+void ExecVHelper::PrepareArgs(const std::string& bin) {
+ CHECK(!args_.empty());
+ CHECK(args_[0].empty());
+ args_[0] = bin;
+ // Write char* into array.
+ for (const std::string& arg : args_) {
+ argv_.push_back(arg.c_str());
+ }
+ argv_.push_back(nullptr); // Add null terminator.
+}
+
+void ExecVHelper::Exec(int exit_code) {
+ execv(argv_[0], (char * const *)&argv_[0]);
+ PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
+ exit(exit_code);
+}
+
+void ExecVHelper::AddArg(const std::string& arg) {
+ if (!arg.empty()) {
+ args_.push_back(arg);
+ }
+}
+
+void ExecVHelper::AddRuntimeArg(const std::string& arg) {
+ if (!arg.empty()) {
+ args_.push_back("--runtime-arg");
+ args_.push_back(arg);
+ }
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/execv_helper.h b/cmds/installd/execv_helper.h
new file mode 100644
index 0000000..9adfc0e
--- /dev/null
+++ b/cmds/installd/execv_helper.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_EXECV_HELPER_H
+#define ANDROID_INSTALLD_EXECV_HELPER_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace installd {
+
+// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
+// need to be performed between the fork and exec.
+class ExecVHelper {
+ public:
+ ExecVHelper();
+ virtual ~ExecVHelper();
+
+ [[ noreturn ]]
+ virtual void Exec(int exit_code);
+
+ void PrepareArgs(const std::string& bin);
+
+ // Add an arg if it's not empty.
+ void AddArg(const std::string& arg);
+
+ // Add a runtime arg if it's not empty.
+ void AddRuntimeArg(const std::string& arg);
+
+ protected:
+ // Holder arrays for backing arg storage.
+ std::vector<std::string> args_;
+
+ // Argument poiners.
+ std::vector<const char*> argv_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_EXECV_HELPER_H
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 18f8268..443821c 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -31,10 +31,8 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <art_image_values.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
-#include <dex2oat_return_codes.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -96,7 +94,7 @@
template<typename T>
static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
- return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
+ return (x & -n);
}
template<typename T>
@@ -523,6 +521,7 @@
// Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
constexpr size_t kPageSize = PAGE_SIZE;
+ static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two");
CHECK_EQ(min_delta % kPageSize, 0u);
CHECK_EQ(max_delta % kPageSize, 0u);
CHECK_LT(min_delta, max_delta);
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
new file mode 100644
index 0000000..17ea903
--- /dev/null
+++ b/cmds/installd/run_dex2oat.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "installd"
+
+#include "run_dex2oat.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+
+#include "unique_file.h"
+
+using android::base::Basename;
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+namespace {
+
+// Should minidebug info be included in compiled artifacts? Even if this value is
+// "true," usage might still be conditional to other constraints, e.g., system
+// property overrides.
+static constexpr bool kEnableMinidebugInfo = true;
+
+static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
+static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
+static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
+static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
+
+// Location of the JIT Zygote image.
+static const char* kJitZygoteImage =
+ "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
+
+std::vector<std::string> SplitBySpaces(const std::string& str) {
+ if (str.empty()) {
+ return {};
+ }
+ return android::base::Split(str, " ");
+}
+
+} // namespace
+
+RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper)
+ : dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {}
+
+void RunDex2Oat::Initialize(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds,
+ int swap_fd,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ bool post_bootcomplete,
+ bool for_restore,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ bool use_jitzygote_image,
+ const char* compilation_reason) {
+ PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
+
+ PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
+ dex_metadata, profile, swap_fd, class_loader_context,
+ class_loader_context_fds);
+
+ PrepareCompilerConfigFlags(input_vdex, output_vdex, instruction_set, compiler_filter,
+ debuggable, target_sdk_version, enable_hidden_api_checks,
+ generate_compact_dex, compilation_reason);
+
+ PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore);
+
+ const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
+ std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
+ ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
+
+ // Do not add args after dex2oat_flags, they should override others for debugging.
+ for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) {
+ AddArg(*it);
+ }
+
+ execv_helper_->PrepareArgs(dex2oat_bin_);
+}
+
+RunDex2Oat::~RunDex2Oat() {}
+
+void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+ std::string boot_image;
+ if (use_jitzygote_image) {
+ boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
+ } else {
+ boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
+ }
+ AddArg(boot_image);
+
+ // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
+ // BOOTCLASSPATH.
+ char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
+ if (dex2oat_bootclasspath != nullptr) {
+ AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
+ }
+
+ std::string updatable_bcp_packages =
+ MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
+ "--updatable-bcp-packages-file=%s");
+ if (updatable_bcp_packages.empty()) {
+ // Make dex2oat fail by providing non-existent file name.
+ updatable_bcp_packages =
+ "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
+ }
+ AddArg(updatable_bcp_packages);
+}
+
+void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ int swap_fd,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds) {
+ std::string input_basename = Basename(input_dex.path());
+ LOG(VERBOSE) << "Running " << dex2oat_bin_ << " in=" << input_basename << " out="
+ << output_oat.path();
+
+ AddArg(StringPrintf("--zip-fd=%d", input_dex.fd()));
+ AddArg(StringPrintf("--zip-location=%s", input_basename.c_str()));
+ AddArg(StringPrintf("--oat-fd=%d", output_oat.fd()));
+ AddArg(StringPrintf("--oat-location=%s", output_oat.path().c_str()));
+ AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex.fd()));
+ AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex.fd()));
+
+ if (output_image.fd() >= 0) {
+ AddArg(StringPrintf("--app-image-fd=%d", output_image.fd()));
+ AddArg(MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"));
+ }
+ if (dex_metadata.fd() > -1) {
+ AddArg("--dm-fd=" + std::to_string(dex_metadata.fd()));
+ }
+ if (profile.fd() != -1) {
+ AddArg(StringPrintf("--profile-file-fd=%d", profile.fd()));
+ }
+ if (swap_fd >= 0) {
+ AddArg(StringPrintf("--swap-fd=%d", swap_fd));
+ }
+
+ // Get the directory of the apk to pass as a base classpath directory.
+ {
+ std::string apk_dir(input_dex.path());
+ size_t dir_index = apk_dir.rfind('/');
+ if (dir_index != std::string::npos) {
+ apk_dir = apk_dir.substr(0, dir_index);
+ AddArg(StringPrintf("--classpath-dir=%s", apk_dir.c_str()));
+ }
+ }
+
+ if (class_loader_context != nullptr) {
+ AddArg(StringPrintf("--class-loader-context=%s", class_loader_context));
+ if (!class_loader_context_fds.empty()) {
+ AddArg(StringPrintf("--class-loader-context-fds=%s",
+ class_loader_context_fds.c_str()));
+ }
+ }
+}
+
+void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+ const UniqueFile& output_vdex,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ const char* compilation_reason) {
+ // Disable cdex if update input vdex is true since this combination of options is not
+ // supported.
+ const bool disable_cdex = !generate_compact_dex || (input_vdex.fd() == output_vdex.fd());
+ if (disable_cdex) {
+ AddArg(kDisableCompactDexFlag);
+ }
+
+ // ISA related
+ {
+ AddArg(StringPrintf("--instruction-set=%s", instruction_set));
+
+ const std::string dex2oat_isa_features_key =
+ StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
+ std::string instruction_set_features_arg =
+ MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
+ AddArg(instruction_set_features_arg);
+
+ const std::string dex2oat_isa_variant_key =
+ StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
+ std::string instruction_set_variant_arg =
+ MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
+ AddArg(instruction_set_variant_arg);
+ }
+
+ // Compute compiler filter.
+ {
+ std::string dex2oat_compiler_filter_arg;
+ {
+ // If we are booting without the real /data, don't spend time compiling.
+ std::string vold_decrypt = GetProperty("vold.decrypt", "");
+ bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
+ vold_decrypt == "1";
+
+ bool have_dex2oat_relocation_skip_flag = false;
+ if (skip_compilation) {
+ dex2oat_compiler_filter_arg = "--compiler-filter=extract";
+ have_dex2oat_relocation_skip_flag = true;
+ } else if (compiler_filter != nullptr) {
+ dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s",
+ compiler_filter);
+ }
+ if (have_dex2oat_relocation_skip_flag) {
+ AddRuntimeArg("-Xnorelocate");
+ }
+ }
+
+ if (dex2oat_compiler_filter_arg.empty()) {
+ dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
+ "--compiler-filter=%s");
+ }
+ AddArg(dex2oat_compiler_filter_arg);
+
+ if (compilation_reason != nullptr) {
+ AddArg(std::string("--compilation-reason=") + compilation_reason);
+ }
+ }
+
+ AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
+ "--max-image-block-size=%s"));
+
+ AddArg(MapPropertyToArg("dalvik.vm.dex2oat-very-large",
+ "--very-large-app-threshold=%s"));
+
+ std::string resolve_startup_string_arg = MapPropertyToArg(
+ "persist.device_config.runtime.dex2oat_resolve_startup_strings",
+ "--resolve-startup-const-strings=%s");
+ if (resolve_startup_string_arg.empty()) {
+ // If empty, fall back to system property.
+ resolve_startup_string_arg =
+ MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
+ "--resolve-startup-const-strings=%s");
+ }
+ AddArg(resolve_startup_string_arg);
+
+ // Debug related
+ {
+ // Check whether all apps should be compiled debuggable.
+ if (!debuggable) {
+ debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
+ }
+ if (debuggable) {
+ AddArg("--debuggable");
+ }
+
+ const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
+ if (generate_debug_info) {
+ AddArg("--generate-debug-info");
+ }
+ {
+ bool generate_minidebug_info = kEnableMinidebugInfo &&
+ GetBoolProperty(kMinidebugInfoSystemProperty,
+ kMinidebugInfoSystemPropertyDefault);
+ if (generate_minidebug_info) {
+ AddArg(kMinidebugDex2oatFlag);
+ }
+ }
+ }
+
+ if (target_sdk_version != 0) {
+ AddRuntimeArg(StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version));
+ }
+
+ if (enable_hidden_api_checks) {
+ AddRuntimeArg("-Xhidden-api-policy:enabled");
+ }
+}
+
+void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
+ bool for_restore) {
+ // CPU set
+ {
+ std::string cpu_set_format = "--cpu-set=%s";
+ std::string dex2oat_cpu_set_arg = post_bootcomplete
+ ? (for_restore
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.restore-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);
+ }
+
+ // Number of threads
+ {
+ std::string threads_format = "-j%s";
+ std::string dex2oat_threads_arg = post_bootcomplete
+ ? (for_restore
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.restore-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);
+ }
+
+ AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"));
+ AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"));
+}
+
+void RunDex2Oat::Exec(int exit_code) {
+ execv_helper_->Exec(exit_code);
+}
+
+void RunDex2Oat::AddArg(const std::string& arg) {
+ execv_helper_->AddArg(arg);
+}
+
+void RunDex2Oat::AddRuntimeArg(const std::string& arg) {
+ execv_helper_->AddRuntimeArg(arg);
+}
+
+std::string RunDex2Oat::GetProperty(const std::string& key,
+ const std::string& default_value) {
+ return android::base::GetProperty(key, default_value);
+}
+
+bool RunDex2Oat::GetBoolProperty(const std::string& key, bool default_value) {
+ return android::base::GetBoolProperty(key, default_value);
+}
+
+std::string RunDex2Oat::MapPropertyToArg(const std::string& property,
+ const std::string& format,
+ const std::string& default_value) {
+ std::string prop = GetProperty(property, default_value);
+ if (!prop.empty()) {
+ return StringPrintf(format.c_str(), prop.c_str());
+ }
+ return "";
+}
+
+std::string RunDex2Oat::MapPropertyToArgWithBackup(
+ const std::string& property,
+ const std::string& backupProperty,
+ const std::string& format,
+ const std::string& default_value) {
+ std::string value = GetProperty(property, default_value);
+ if (!value.empty()) {
+ return StringPrintf(format.c_str(), value.c_str());
+ }
+ return MapPropertyToArg(backupProperty, format, default_value);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
new file mode 100644
index 0000000..325a3a2
--- /dev/null
+++ b/cmds/installd/run_dex2oat.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_RUN_DEX2OAT_H
+#define ANDROID_INSTALLD_RUN_DEX2OAT_H
+
+#include <memory>
+#include <string>
+
+#include "execv_helper.h"
+
+namespace android {
+namespace installd {
+
+class UniqueFile;
+
+class RunDex2Oat {
+ public:
+ explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper);
+ virtual ~RunDex2Oat();
+
+ void Initialize(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds,
+ int swap_fd,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ bool post_bootcomplete,
+ bool for_restore,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ bool use_jitzygote_image,
+ const char* compilation_reason);
+
+ void Exec(int exit_code);
+
+ protected:
+ void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+ void PrepareInputFileFlags(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ int swap_fd,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds);
+ void PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+ const UniqueFile& output_vdex,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ const char* compilation_reason);
+ void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore);
+
+ virtual std::string GetProperty(const std::string& key, const std::string& default_value);
+ virtual bool GetBoolProperty(const std::string& key, bool default_value);
+
+ private:
+ void AddArg(const std::string& arg);
+ void AddRuntimeArg(const std::string& arg);
+
+ std::string MapPropertyToArg(const std::string& property,
+ const std::string& format,
+ const std::string& default_value = "");
+
+ std::string MapPropertyToArgWithBackup(const std::string& property,
+ const std::string& backupProperty,
+ const std::string& format,
+ const std::string& default_value = "");
+
+ const std::string dex2oat_bin_;
+ ExecVHelper* execv_helper_; // not owned
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_RUN_DEX2OAT_H
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
new file mode 100644
index 0000000..3813cf7
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include <gtest/gtest.h>
+
+#include "execv_helper.h"
+#include "run_dex2oat.h"
+#include "unique_file.h"
+
+namespace android {
+namespace installd {
+
+class RunDex2OatTest : public testing::Test {
+ public:
+ static constexpr const char* INPUT_PATH = "/dir/input/basename.apk";
+ static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat";
+ static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}";
+
+ // UniqueFile closes FD. Avoid using standard I/O since the test is expected to print gtest
+ // results. Alternatively, mock out UniqueFile to avoid the side effect of close(2).
+ static constexpr int ZIP_FD = 3;
+ static constexpr int OAT_FD = 4;
+ static constexpr int INPUT_VDEX_FD = 5;
+ static constexpr int OUTPUT_VDEX_FD = 6;
+ static constexpr int IMAGE_FD = 7;
+ static constexpr int PROFILE_FD = 8;
+ static constexpr int DEX_METADATA_FD = 9;
+ static constexpr int SWAP_FD = 10;
+
+ using FakeSystemProperties = std::map<std::string, std::string>;
+
+ // A fake RunDex2Oat that allows to override (fake) system properties and starts with none.
+ class FakeRunDex2Oat : public RunDex2Oat {
+ private:
+ static constexpr const char* TRUE_STR = "true";
+ static constexpr const char* FALSE_STR = "false";
+
+ public:
+ FakeRunDex2Oat(ExecVHelper* execv_helper, FakeSystemProperties* properties)
+ : RunDex2Oat("/dir/bin/dex2oat", execv_helper), properties_(properties) { }
+
+ virtual ~FakeRunDex2Oat() {}
+
+ virtual std::string GetProperty(const std::string& key,
+ const std::string& default_value) override {
+ if (!properties_) {
+ return default_value;
+ }
+ auto iter = properties_->find(key);
+ if (iter == properties_->end()) {
+ return default_value;
+ }
+ return iter->second;
+ }
+
+ virtual bool GetBoolProperty(const std::string& key, bool default_value) override {
+ std::string value = GetProperty(key, "");
+ if (value == "") {
+ return default_value;
+ }
+ return value == TRUE_STR;
+ }
+
+ private:
+ FakeSystemProperties* properties_;
+ };
+
+ struct RunDex2OatArgs {
+ static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() {
+ auto args = std::make_unique<RunDex2OatArgs>();
+ args->input_dex.reset(ZIP_FD, INPUT_PATH);
+ args->output_oat.reset(OAT_FD, OUTPUT_PATH);
+ args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+ args->output_vdex.reset(OUTPUT_VDEX_FD, "UNUSED_PATH");
+ args->instruction_set = "arm64";
+ args->compilation_reason = "rundex2oattest";
+ return args;
+ }
+
+ UniqueFile output_oat;
+ UniqueFile output_vdex;
+ UniqueFile output_image;
+ UniqueFile input_dex;
+ UniqueFile input_vdex;
+ UniqueFile dex_metadata;
+ UniqueFile profile;
+ int swap_fd = -1;
+ const char* instruction_set = nullptr;
+ const char* compiler_filter = "extract";
+ bool debuggable = false;
+ bool post_bootcomplete = false;
+ bool for_restore = false;
+ const char* class_loader_context = nullptr;
+ std::string class_loader_context_fds;
+ int target_sdk_version = 0;
+ bool enable_hidden_api_checks = false;
+ bool generate_compact_dex = true;
+ bool use_jitzygote_image = false;
+ const char* compilation_reason = nullptr;
+ };
+
+ class FakeExecVHelper : public ExecVHelper {
+ public:
+ bool HasArg(const std::string& arg) const {
+ auto end = argv_.end() - 1; // To exclude the terminating nullptr
+ return find(argv_.begin(), end, arg) != end;
+ }
+
+ bool FlagNotUsed(const std::string& flag) const {
+ auto has_prefix = [flag](const char* arg) {
+ return strncmp(arg, flag.c_str(), flag.size()) == 0;
+ };
+ auto end = argv_.end() - 1; // To exclude the terminating nullptr
+ return find_if(argv_.begin(), end, has_prefix) == end;
+ }
+
+ virtual void Exec(int exit_code) override {
+ std::string cmd;
+ for (auto arg : argv_) {
+ if (arg == nullptr) {
+ continue;
+ }
+ cmd += arg;
+ cmd += " ";
+ }
+ LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd << "\n";
+ }
+ };
+
+ virtual void SetUp() override {
+ execv_helper_.reset(new FakeExecVHelper());
+ system_properties_.clear();
+ initializeDefaultExpectedFlags();
+ }
+
+ // Initializes the default flags expected to a run. It currently matches to the expected flags
+ // with RunDex2OatArgs::MakeDefaultTestArgs.
+ //
+ // default_expected_flags_ defines a mapping of <flag_name, expected_value>, where flag_name is
+ // something like "--flag-name", and expected_value can be "=value" or ":value" (depending on
+ // its delimiter), "" (if no value is needed), or a special value of FLAG_UNUSED to indicates
+ // that it should not be used.
+ void initializeDefaultExpectedFlags() {
+ default_expected_flags_.clear();
+
+ // Files
+ default_expected_flags_["--zip-fd"] = "=" + std::to_string(ZIP_FD);
+ default_expected_flags_["--zip-location"] = "=basename.apk";
+ default_expected_flags_["--oat-fd"] = "=" + std::to_string(OAT_FD);
+ default_expected_flags_["--oat-location"] = "=" + std::string(OUTPUT_PATH);
+ default_expected_flags_["--input-vdex-fd"] = "=" + std::to_string(INPUT_VDEX_FD);
+ default_expected_flags_["--output-vdex-fd"] = "=" + std::to_string(OUTPUT_VDEX_FD);
+ default_expected_flags_["--classpath-dir"] = "=/dir/input";
+ default_expected_flags_["--app-image-fd"] = FLAG_UNUSED;
+ default_expected_flags_["--profile-file-fd"] = FLAG_UNUSED;
+ default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
+ default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
+ default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
+ default_expected_flags_["--updatable-bcp-packages-file"] =
+ "=/nonx/updatable-bcp-packages.txt";
+
+ // Arch
+ default_expected_flags_["--instruction-set"] = "=arm64";
+ default_expected_flags_["--instruction-set-features"] = FLAG_UNUSED;
+ default_expected_flags_["--instruction-set-variant"] = FLAG_UNUSED;
+ default_expected_flags_["--cpu-set"] = FLAG_UNUSED;
+
+ // Misc
+ default_expected_flags_["--compiler-filter"] = "=extract";
+ default_expected_flags_["--compilation-reason"] = "=rundex2oattest";
+ default_expected_flags_["--compact-dex-level"] = FLAG_UNUSED;
+ default_expected_flags_["-j"] = FLAG_UNUSED;
+ default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED;
+ default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED;
+ default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED;
+
+ // Debug
+ default_expected_flags_["--debuggable"] = FLAG_UNUSED;
+ default_expected_flags_["--generate-debug-info"] = FLAG_UNUSED;
+ default_expected_flags_["--generate-mini-debug-info"] = FLAG_UNUSED;
+
+ // Runtime
+ // TODO(victorhsieh): Check if the previous flag is actually --runtime-arg.
+ default_expected_flags_["-Xms"] = FLAG_UNUSED;
+ default_expected_flags_["-Xmx"] = FLAG_UNUSED;
+ default_expected_flags_["-Xbootclasspath"] = FLAG_UNUSED;
+ default_expected_flags_["-Xtarget-sdk-version"] = FLAG_UNUSED;
+ default_expected_flags_["-Xhidden-api-policy"] = FLAG_UNUSED;
+ default_expected_flags_["-Xnorelocate"] = FLAG_UNUSED;
+
+ // Test only
+ default_expected_flags_["--foo"] = FLAG_UNUSED;
+ default_expected_flags_["--bar"] = FLAG_UNUSED;
+ default_expected_flags_["--baz"] = FLAG_UNUSED;
+ }
+
+ void SetExpectedFlagUsed(const std::string& flag, const std::string& value) {
+ auto iter = default_expected_flags_.find(flag);
+ ASSERT_NE(iter, default_expected_flags_.end()) << "Must define the default value";
+ iter->second = value;
+ }
+
+ void VerifyExpectedFlags() {
+ for (auto const& [flag, value] : default_expected_flags_) {
+ if (value == FLAG_UNUSED) {
+ EXPECT_TRUE(execv_helper_->FlagNotUsed(flag))
+ << "Flag " << flag << " should be unused, but got the value " << value;
+ } else if (value == "") {
+ EXPECT_TRUE(execv_helper_->HasArg(flag))
+ << "Flag " << flag << " should be specified without value, but got " << value;
+ } else {
+ EXPECT_TRUE(execv_helper_->HasArg(flag + value))
+ << "Flag " << flag << value << " is not specificed";
+ }
+ }
+ }
+
+ void setSystemProperty(const std::string& key, const std::string& value) {
+ system_properties_[key] = value;
+ }
+
+ void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) {
+ FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_);
+ runner.Initialize(args->output_oat,
+ args->output_vdex,
+ args->output_image,
+ args->input_dex,
+ args->input_vdex,
+ args->dex_metadata,
+ args->profile,
+ args->class_loader_context,
+ args->class_loader_context_fds,
+ args->swap_fd,
+ args->instruction_set,
+ args->compiler_filter,
+ args->debuggable,
+ args->post_bootcomplete,
+ args->for_restore,
+ args->target_sdk_version,
+ args->enable_hidden_api_checks,
+ args->generate_compact_dex,
+ args->use_jitzygote_image,
+ args->compilation_reason);
+ runner.Exec(/*exit_code=*/ 0);
+ }
+
+ private:
+ std::unique_ptr<FakeExecVHelper> execv_helper_;
+ std::map<std::string, std::string> default_expected_flags_;
+ FakeSystemProperties system_properties_;
+};
+
+TEST_F(RunDex2OatTest, BasicInputOutput) {
+ auto execv_helper = std::make_unique<FakeExecVHelper>();
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithAllOtherInputFds) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->output_image.reset(IMAGE_FD, "UNUSED_PATH");
+ args->profile.reset(PROFILE_FD, "UNUSED_PATH");
+ args->swap_fd = SWAP_FD;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--app-image-fd", "=" + std::to_string(IMAGE_FD));
+ SetExpectedFlagUsed("--profile-file-fd", "=" + std::to_string(PROFILE_FD));
+ SetExpectedFlagUsed("--swap-fd", "=" + std::to_string(SWAP_FD));
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithClassLoaderContext) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->class_loader_context = "CLASS_LOADER_CONTEXT";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT");
+ SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED);
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithClassLoaderContextAndFds) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->class_loader_context = "CLASS_LOADER_CONTEXT";
+ args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT");
+ SetExpectedFlagUsed("--class-loader-context-fds", "=CLASS_LOADER_CONTEXT_FDS");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithOnlyClassLoaderContextFds) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--class-loader-context", FLAG_UNUSED);
+ SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED);
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
+ ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
+ ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
+ << "Failed to setenv: " << strerror(errno);
+
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
+ VerifyExpectedFlags();
+
+ ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
+ << "Failed to setenv: " << strerror(errno);
+}
+
+TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
+ setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->generate_compact_dex = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--compact-dex-level", "=none");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->generate_compact_dex = true;
+ args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+ args->output_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--compact-dex-level", "=none");
+ SetExpectedFlagUsed("--output-vdex-fd", "=" + std::to_string(INPUT_VDEX_FD));
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ISA) {
+ setSystemProperty("dalvik.vm.isa.x86.features", "a-x86-feature");
+ setSystemProperty("dalvik.vm.isa.x86.variant", "a-x86-variant");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->instruction_set = "x86";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--instruction-set", "=x86");
+ SetExpectedFlagUsed("--instruction-set-features", "=a-x86-feature");
+ SetExpectedFlagUsed("--instruction-set-variant", "=a-x86-variant");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPreBootComplete) {
+ setSystemProperty("dalvik.vm.boot-dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) {
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = false;
+ 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");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore_Backup) {
+ setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Runtime) {
+ setSystemProperty("dalvik.vm.dex2oat-Xms", "1234m");
+ setSystemProperty("dalvik.vm.dex2oat-Xmx", "5678m");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->target_sdk_version = 30;
+ args->enable_hidden_api_checks = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-Xms", "1234m");
+ SetExpectedFlagUsed("-Xmx", "5678m");
+ SetExpectedFlagUsed("-Xtarget-sdk-version", ":30");
+ SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled");
+ SetExpectedFlagUsed("-Xnorelocate", FLAG_UNUSED);
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) {
+ setSystemProperty("vold.decrypt", "trigger_restart_min_framework");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--compiler-filter", "=extract");
+ SetExpectedFlagUsed("-Xnorelocate", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) {
+ setSystemProperty("vold.decrypt", "1");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--compiler-filter", "=extract");
+ SetExpectedFlagUsed("-Xnorelocate", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) {
+ setSystemProperty("dalvik.vm.dex2oat-filter", "speed");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->compiler_filter = nullptr;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--compiler-filter", "=speed");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ResolveStartupStartings) {
+ setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--resolve-startup-const-strings", "=false");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ResolveStartupStartingsOverride) {
+ setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false");
+ setSystemProperty("persist.device_config.runtime.dex2oat_resolve_startup_strings", "true");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--resolve-startup-const-strings", "=true");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPreBootComplete) {
+ setSystemProperty("dalvik.vm.boot-dex2oat-threads", "2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) {
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = false;
+ 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");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "4");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore_Backup) {
+ setSystemProperty("dalvik.vm.restore-dex2oat-threads", "");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "5");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "5");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Debuggable) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->debuggable = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--debuggable", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, AlwaysDebuggable) {
+ setSystemProperty("dalvik.vm.always_debuggable", "1");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--debuggable", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, GenerateDebugInfo) {
+ setSystemProperty("debug.generate-debug-info", "true");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--generate-debug-info", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, HiddenApiCheck) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->enable_hidden_api_checks = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Misc) {
+ setSystemProperty("dalvik.vm.dex2oat-max-image-block-size", "524288");
+ setSystemProperty("dalvik.vm.dex2oat-very-large", "100000");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--max-image-block-size", "=524288");
+ SetExpectedFlagUsed("--very-large-app-threshold", "=100000");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ExtraFlags) {
+ setSystemProperty("dalvik.vm.dex2oat-flags", "--foo=123 --bar:456 --baz");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--foo", "=123");
+ SetExpectedFlagUsed("--bar", ":456");
+ SetExpectedFlagUsed("--baz", "");
+ VerifyExpectedFlags();
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/run_dex2oat_test.xml b/cmds/installd/run_dex2oat_test.xml
new file mode 100644
index 0000000..2467fe4
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Unittest of run_dex2oat">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <option name="null-device" value="true" />
+ <test class="com.android.tradefed.testtype.HostGTest" >
+ <option name="module-name" value="run_dex2oat_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 96f5e44..fbf1e0c 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -663,7 +663,7 @@
&status);
EXPECT_STREQ(status.toString8().c_str(),
"Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for "
- "/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'");
+ "/data/app/com.installd.test.dexopt/base.jar failed: dex2oat error'");
}
TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) {
diff --git a/cmds/installd/unique_file.cpp b/cmds/installd/unique_file.cpp
new file mode 100644
index 0000000..e99ce1e
--- /dev/null
+++ b/cmds/installd/unique_file.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unique_file.h"
+
+#include <string>
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace installd {
+
+UniqueFile::UniqueFile() : UniqueFile(-1, "") {}
+
+UniqueFile::UniqueFile(int value, std::string path) : UniqueFile(value, path, nullptr) {}
+
+UniqueFile::UniqueFile(int value, std::string path, CleanUpFunction cleanup)
+ : value_(value), path_(path), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
+
+UniqueFile::UniqueFile(UniqueFile&& other) {
+ *this = std::move(other);
+}
+
+UniqueFile::~UniqueFile() {
+ reset();
+}
+
+UniqueFile& UniqueFile::operator=(UniqueFile&& other) {
+ value_ = other.value_;
+ path_ = other.path_;
+ cleanup_ = other.cleanup_;
+ do_cleanup_ = other.do_cleanup_;
+ auto_close_ = other.auto_close_;
+ other.release();
+ return *this;
+}
+
+void UniqueFile::reset() {
+ reset(-1, "");
+}
+
+void UniqueFile::reset(int new_value, std::string path, CleanUpFunction new_cleanup) {
+ if (auto_close_ && value_ >= 0) {
+ if (close(value_) < 0) {
+ PLOG(ERROR) << "Failed to close fd " << value_ << ", with path " << path;
+ }
+ }
+ if (do_cleanup_ && cleanup_ != nullptr) {
+ cleanup_(path_);
+ }
+
+ value_ = new_value;
+ path_ = path;
+ cleanup_ = new_cleanup;
+}
+
+void UniqueFile::release() {
+ value_ = -1;
+ path_ = "";
+ do_cleanup_ = false;
+ cleanup_ = nullptr;
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/unique_file.h b/cmds/installd/unique_file.h
new file mode 100644
index 0000000..e85e23b
--- /dev/null
+++ b/cmds/installd/unique_file.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_UNIQUE_FILE_H
+#define ANDROID_INSTALLD_UNIQUE_FILE_H
+
+#include <functional>
+#include <string>
+
+namespace android {
+namespace installd {
+
+// A file management helper that serves two purposes:
+//
+// 1. Closes the file description on destruction, similar unique_fd.
+// 2. Runs a cleanup function on after close, if not cancelled.
+//
+// The class does not assume the relationship between the given fd and file path.
+//
+// Example:
+//
+// UniqueFile file(open(...),
+// filepath,
+// [](const std::string& path) {
+// unlink(path.c_str());
+// });
+// if (file.fd() == -1) {
+// // Error opening...
+// }
+//
+// ...
+// if (error) {
+// // At this point, when the UniqueFile is destructed, the cleanup function will run
+// // (e.g. to delete the file) after the fd is closed.
+// return -1;
+// }
+//
+// (Success case)
+// file.DisableCleanup();
+// // At this point, when the UniqueFile is destructed, the cleanup function will not run
+// // (e.g. leaving the file around) after the fd is closed.
+//
+class UniqueFile {
+ private:
+ using CleanUpFunction = std::function<void (const std::string&)>;
+
+ public:
+ UniqueFile();
+ UniqueFile(int value, std::string path);
+ UniqueFile(int value, std::string path, CleanUpFunction cleanup);
+ UniqueFile(UniqueFile&& other);
+ ~UniqueFile();
+
+ UniqueFile& operator=(UniqueFile&& other);
+
+ int fd() const {
+ return value_;
+ }
+
+ const std::string& path() const {
+ return path_;
+ }
+
+ void DisableAutoClose() {
+ auto_close_ = false;
+ }
+
+ void DisableCleanup() {
+ do_cleanup_ = false;
+ }
+
+ void reset();
+ void reset(int new_value, std::string path, CleanUpFunction new_cleanup = nullptr);
+
+ private:
+ void release();
+
+ int value_;
+ std::string path_;
+ CleanUpFunction cleanup_;
+ bool do_cleanup_;
+ bool auto_close_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_UNIQUE_FILE_H
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a805a48..92958d9 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -916,6 +916,18 @@
}
}
+// Get all values of enum type T, assuming the first value is 0 and the last value is T::LAST.
+// T::LAST is not included in the returned list.
+template <typename T>
+std::vector<T> GetAllValues() {
+ using BaseType = std::underlying_type_t<T>;
+ std::vector<T> ret;
+ for (BaseType i = 0; i < static_cast<BaseType>(T::LAST); ++i) {
+ ret.push_back(static_cast<T>(i));
+ }
+ return ret;
+}
+
void ListCommand::registerAllOptions() {
int v = mOptions.size();
// A list of acceptable command line options
@@ -989,6 +1001,15 @@
" - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n"
" - N/A: no information for passthrough HALs."});
+ mOptions.push_back({'A', "all", no_argument, v++,
+ [](ListCommand* thiz, const char*) {
+ auto allColumns = GetAllValues<TableColumnType>();
+ thiz->mSelectedColumns.insert(thiz->mSelectedColumns.end(),
+ allColumns.begin(), allColumns.end());
+ return OK;
+ },
+ "print all columns"});
+
// long options without short alternatives
mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
thiz->mVintf = true;
@@ -1019,46 +1040,55 @@
thiz->mNeat = true;
return OK;
}, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
- mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) {
- if (!arg) { return USAGE; }
+ mOptions.push_back(
+ {'\0', "types", required_argument, v++,
+ [](ListCommand* thiz, const char* arg) {
+ if (!arg) {
+ return USAGE;
+ }
- static const std::map<std::string, HalType> kHalTypeMap {
- {"binderized", HalType::BINDERIZED_SERVICES},
- {"b", HalType::BINDERIZED_SERVICES},
- {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS},
- {"c", HalType::PASSTHROUGH_CLIENTS},
- {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES},
- {"l", HalType::PASSTHROUGH_LIBRARIES},
- {"vintf", HalType::VINTF_MANIFEST},
- {"v", HalType::VINTF_MANIFEST},
- {"lazy", HalType::LAZY_HALS},
- {"z", HalType::LAZY_HALS},
- };
+ static const std::map<std::string, std::vector<HalType>> kHalTypeMap{
+ {"binderized", {HalType::BINDERIZED_SERVICES}},
+ {"b", {HalType::BINDERIZED_SERVICES}},
+ {"passthrough_clients", {HalType::PASSTHROUGH_CLIENTS}},
+ {"c", {HalType::PASSTHROUGH_CLIENTS}},
+ {"passthrough_libs", {HalType::PASSTHROUGH_LIBRARIES}},
+ {"l", {HalType::PASSTHROUGH_LIBRARIES}},
+ {"vintf", {HalType::VINTF_MANIFEST}},
+ {"v", {HalType::VINTF_MANIFEST}},
+ {"lazy", {HalType::LAZY_HALS}},
+ {"z", {HalType::LAZY_HALS}},
+ {"all", GetAllValues<HalType>()},
+ {"a", GetAllValues<HalType>()},
+ };
- std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
- for (const auto& halTypeArg : halTypesArgs) {
- if (halTypeArg.empty()) continue;
+ std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
+ for (const auto& halTypeArg : halTypesArgs) {
+ if (halTypeArg.empty()) continue;
- const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
- if (halTypeIter == kHalTypeMap.end()) {
+ const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
+ if (halTypeIter == kHalTypeMap.end()) {
+ thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
+ return USAGE;
+ }
- thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
- return USAGE;
- }
+ // Append unique (non-repeated) HAL types to the reporting list
+ for (auto halType : halTypeIter->second) {
+ if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
+ thiz->mListTypes.end()) {
+ thiz->mListTypes.push_back(halType);
+ }
+ }
+ }
- // Append unique (non-repeated) HAL types to the reporting list
- HalType halType = halTypeIter->second;
- if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
- thiz->mListTypes.end()) {
- thiz->mListTypes.push_back(halType);
- }
- }
-
- if (thiz->mListTypes.empty()) { return USAGE; }
- return OK;
- }, "comma-separated list of one or more sections.\nThe output is restricted to the selected "
- "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
- "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
+ if (thiz->mListTypes.empty()) {
+ return USAGE;
+ }
+ return OK;
+ },
+ "comma-separated list of one or more sections.\nThe output is restricted to the "
+ "selected section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
+ "passthrough_libs), (v|vintf), (z|lazy), and (a|all).\nDefault is `b,c,l`."});
}
// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index acc0dcf..412aadd 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -52,6 +52,9 @@
PASSTHROUGH_LIBRARIES,
VINTF_MANIFEST,
LAZY_HALS,
+
+ // Not a real HalType. Used to determine all HalTypes.
+ LAST,
};
class ListCommand : public Command {
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 0ff0c96..3c36813 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -35,19 +35,25 @@
using Pids = std::vector<int32_t>;
enum class TableColumnType : unsigned int {
- INTERFACE_NAME,
+ INTERFACE_NAME = 0,
TRANSPORT,
SERVER_PID,
- SERVER_CMD,
SERVER_ADDR,
- CLIENT_PIDS,
- CLIENT_CMDS,
ARCH,
THREADS,
RELEASED,
HASH,
VINTF,
SERVICE_STATUS,
+ CLIENT_PIDS,
+
+ // Not a real TableColumnType. Used to determine all TableColumnTypes.
+ LAST,
+
+ // Not included in all TableColumnTypes because they replace *PID(S) when the
+ // --cmdline option is set.
+ SERVER_CMD,
+ CLIENT_CMDS,
};
enum : unsigned int {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index afe5d63..9964888 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -708,8 +708,8 @@
TEST_F(ListTest, UnknownHalType) {
optind = 1; // mimic Lshal::parseArg()
- EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"})));
- EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a"));
+ EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,r"})));
+ EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: r"));
}
TEST_F(ListTest, Vintf) {
@@ -793,6 +793,71 @@
EXPECT_EQ("", err.str());
}
+TEST_F(ListTest, AllColumns) {
+ // clang-format off
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive 3 5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough N/A N/A 32 N/A ? X N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough N/A N/A 32 N/A ? X N/A 5 7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough N/A N/A 32 N/A ? X N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough N/A N/A 32 N/A ? X N/A 7 9\n"
+ "\n";
+ // clang-format on
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--all"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllColumnsWithCmd) {
+ // clang-format off
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder command_line_1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive command_line_2;command_line_4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder command_line_2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive command_line_3;command_line_5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough N/A 32 N/A ? X N/A command_line_4;command_line_6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough N/A 32 N/A ? X N/A command_line_5;command_line_7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough N/A 32 N/A ? X N/A command_line_6;command_line_8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough N/A 32 N/A ? X N/A command_line_7;command_line_9\n"
+ "\n";
+ // clang-format on
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Am"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllSections) {
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=all"})));
+ using HalTypeBase = std::underlying_type_t<HalType>;
+ for (HalTypeBase i = 0; i < static_cast<HalTypeBase>(HalType::LAST); ++i) {
+ EXPECT_THAT(out.str(), HasSubstr("[fake description " + std::to_string(i) + "]"));
+ }
+ EXPECT_THAT(out.str(),
+ Not(HasSubstr("[fake description " +
+ std::to_string(static_cast<HalTypeBase>(HalType::LAST)) + "]")));
+ EXPECT_EQ("", err.str());
+}
+
// Fake service returned by mocked IServiceManager::get for DumpDebug.
// The interfaceChain and getHashChain functions returns
// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 7277e85..b139251 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,6 +44,9 @@
cflags: [
"-DVENDORSERVICEMANAGER=1",
],
+ required: [
+ "vndservice",
+ ],
srcs: ["main.cpp"],
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 1f9892a..e80c321 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -213,17 +213,18 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- auto entry = mNameToService.emplace(name, Service {
+ // Overwrite the old service if it exists
+ mNameToService[name] = Service {
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.debugPid = ctx.debugPid,
- });
+ };
auto it = mNameToRegistrationCallback.find(name);
if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
- entry.first->second.guaranteeClient = true;
+ mNameToService[name].guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 25245be..fb9f9df 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -135,6 +135,26 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
}
+TEST(AddService, OverwriteExistingService) {
+ auto sm = getPermissiveServiceManager();
+ sp<IBinder> serviceA = getBinder();
+ EXPECT_TRUE(sm->addService("foo", serviceA, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> outA;
+ EXPECT_TRUE(sm->getService("foo", &outA).isOk());
+ EXPECT_EQ(serviceA, outA);
+
+ // serviceA should be overwritten by serviceB
+ sp<IBinder> serviceB = getBinder();
+ EXPECT_TRUE(sm->addService("foo", serviceB, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> outB;
+ EXPECT_TRUE(sm->getService("foo", &outB).isOk());
+ EXPECT_EQ(serviceB, outB);
+}
+
TEST(AddService, NoPermissions) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index dc37bbd..e65b224 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -321,6 +321,46 @@
OMX_VIDEO_DolbyVisionLevelmax = 0x7FFFFFFF
} OMX_VIDEO_DOLBYVISIONLEVELTYPE;
+/** AV1 Profile enum type */
+typedef enum OMX_VIDEO_AV1PROFILETYPE {
+ OMX_VIDEO_AV1ProfileMain8 = 0x00000001,
+ OMX_VIDEO_AV1ProfileMain10 = 0x00000002,
+ OMX_VIDEO_AV1ProfileMain10HDR10 = 0x00001000,
+ OMX_VIDEO_AV1ProfileMain10HDR10Plus = 0x00002000,
+ OMX_VIDEO_AV1ProfileUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_AV1ProfileMax = 0x7FFFFFFF
+} OMX_VIDEO_AV1PROFILETYPE;
+
+/** AV1 Level enum type */
+typedef enum OMX_VIDEO_AV1LEVELTYPE {
+ OMX_VIDEO_AV1Level2 = 0x1,
+ OMX_VIDEO_AV1Level21 = 0x2,
+ OMX_VIDEO_AV1Level22 = 0x4,
+ OMX_VIDEO_AV1Level23 = 0x8,
+ OMX_VIDEO_AV1Level3 = 0x10,
+ OMX_VIDEO_AV1Level31 = 0x20,
+ OMX_VIDEO_AV1Level32 = 0x40,
+ OMX_VIDEO_AV1Level33 = 0x80,
+ OMX_VIDEO_AV1Level4 = 0x100,
+ OMX_VIDEO_AV1Level41 = 0x200,
+ OMX_VIDEO_AV1Level42 = 0x400,
+ OMX_VIDEO_AV1Level43 = 0x800,
+ OMX_VIDEO_AV1Level5 = 0x1000,
+ OMX_VIDEO_AV1Level51 = 0x2000,
+ OMX_VIDEO_AV1Level52 = 0x4000,
+ OMX_VIDEO_AV1Level53 = 0x8000,
+ OMX_VIDEO_AV1Level6 = 0x10000,
+ OMX_VIDEO_AV1Level61 = 0x20000,
+ OMX_VIDEO_AV1Level62 = 0x40000,
+ OMX_VIDEO_AV1Level63 = 0x80000,
+ OMX_VIDEO_AV1Level7 = 0x100000,
+ OMX_VIDEO_AV1Level71 = 0x200000,
+ OMX_VIDEO_AV1Level72 = 0x400000,
+ OMX_VIDEO_AV1Level73 = 0x800000,
+ OMX_VIDEO_AV1LevelUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_AV1LevelMax = 0x7FFFFFFF
+} OMX_VIDEO_AV1LEVELTYPE;
+
/**
* Structure for configuring video compression intra refresh period
*
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index bdf11e4..e9f559c 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -130,6 +130,13 @@
* This api is thread-safe. Any thread is allowed to register a new refresh
* rate callback for the choreographer instance.
*
+ * Note that in API level 30, this api is not guaranteed to be atomic with
+ * DisplayManager. That is, calling Display#getRefreshRate very soon after
+ * a refresh rate callback is invoked may return a stale refresh rate. If any
+ * Display properties would be required by this callback, then it is recommended
+ * to listen directly to DisplayManager.DisplayListener#onDisplayChanged events
+ * instead.
+ *
* Available since API level 30.
*/
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 258a4e3..80aa891 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -22,6 +22,8 @@
cc_library_headers {
name: "libarect_headers",
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
export_include_dirs: ["include"],
}
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index b24a577..9675a53 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -78,7 +78,7 @@
// or dessert updates. Instead, apex users should use libbinder_ndk.
apex_available: [
"//apex_available:platform",
- // TODO(b/139016109) remove these three
+ // TODO(b/166468760) remove these three
"com.android.media.swcodec",
"test_com.android.media.swcodec",
],
@@ -127,6 +127,10 @@
export_aidl_headers: true,
},
+ // TODO(b/142684679): for com.android.media which is compiled
+ // as vendor and used as system code.
+ use_apex_name_macro: true,
+
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index e0fb543..6ca3b16 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -24,6 +24,7 @@
#include <binder/IShellCallback.h>
#include <binder/Parcel.h>
+#include <linux/sched.h>
#include <stdio.h>
namespace android {
@@ -133,6 +134,8 @@
// unlocked objects
bool mRequestingSid = false;
sp<IBinder> mExtension;
+ int mPolicy = SCHED_NORMAL;
+ int mPriority = 0;
// for below objects
Mutex mLock;
@@ -279,6 +282,47 @@
return e->mExtension;
}
+void BBinder::setMinSchedulerPolicy(int policy, int priority) {
+ switch (policy) {
+ case SCHED_NORMAL:
+ LOG_ALWAYS_FATAL_IF(priority < -20 || priority > 19, "Invalid priority for SCHED_NORMAL: %d", priority);
+ break;
+ case SCHED_RR:
+ case SCHED_FIFO:
+ LOG_ALWAYS_FATAL_IF(priority < 1 || priority > 99, "Invalid priority for sched %d: %d", policy, priority);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unrecognized scheduling policy: %d", policy);
+ }
+
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (e == nullptr) {
+ // Avoid allocations if called with default.
+ if (policy == SCHED_NORMAL && priority == 0) {
+ return;
+ }
+
+ e = getOrCreateExtras();
+ if (!e) return; // out of memory
+ }
+
+ e->mPolicy = policy;
+ e->mPriority = priority;
+}
+
+int BBinder::getMinSchedulerPolicy() {
+ Extras* e = mExtras.load(std::memory_order_acquire);
+ if (e == nullptr) return SCHED_NORMAL;
+ return e->mPolicy;
+}
+
+int BBinder::getMinSchedulerPriority() {
+ Extras* e = mExtras.load(std::memory_order_acquire);
+ if (e == nullptr) return 0;
+ return e->mPriority;
+}
+
pid_t BBinder::getDebugPid() {
return getpid();
}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index d2b9b8f..c183d29 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -224,7 +224,7 @@
using android::internal::Stability;
auto stability = Stability::get(this);
- auto required = privateVendor ? Stability::VENDOR : Stability::kLocalStability;
+ auto required = privateVendor ? Stability::VENDOR : Stability::getLocalStability();
if (CC_UNLIKELY(!Stability::check(stability, required))) {
ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index d67ce15..157538e 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -614,7 +614,7 @@
talkWithDriver(false);
}
-int IPCThreadState::setupPolling(int* fd)
+status_t IPCThreadState::setupPolling(int* fd)
{
if (mProcess->mDriverFD < 0) {
return -EBADF;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 25c0b19..9aa82d9 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -18,6 +18,9 @@
#include <binder/IServiceManager.h>
+#include <inttypes.h>
+#include <unistd.h>
+
#include <android/os/BnServiceCallback.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
@@ -36,8 +39,6 @@
#include "Static.h"
-#include <unistd.h>
-
namespace android {
using AidlServiceManager = android::os::IServiceManager;
@@ -219,7 +220,8 @@
const bool isVendorService =
strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
- const long timeout = uptimeMillis() + 5000;
+ const long timeout = 5000;
+ int64_t startTime = uptimeMillis();
// Vendor code can't access system properties
if (!gSystemBootCompleted && !isVendorService) {
#ifdef __ANDROID__
@@ -233,15 +235,21 @@
// retry interval in millisecond; note that vendor services stay at 100ms
const long sleepTime = gSystemBootCompleted ? 1000 : 100;
+ ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
+ ProcessState::self()->getDriverName().c_str());
+
int n = 0;
- while (uptimeMillis() < timeout) {
+ while (uptimeMillis() - startTime < timeout) {
n++;
- ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
- ProcessState::self()->getDriverName().c_str());
usleep(1000*sleepTime);
sp<IBinder> svc = checkService(name);
- if (svc != nullptr) return svc;
+ if (svc != nullptr) {
+ ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms",
+ String8(name).string(), ProcessState::self()->getDriverName().c_str(),
+ uptimeMillis() - startTime);
+ return svc;
+ }
}
ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
return nullptr;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 166c1d8..19f3606 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
@@ -40,7 +41,6 @@
#include <binder/TextOutput.h>
#include <cutils/ashmem.h>
-#include <utils/Debug.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/misc.h>
@@ -188,16 +188,18 @@
return OK;
}
+static constexpr inline int schedPolicyMask(int policy, int priority) {
+ return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT);
+}
+
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
flat_binder_object obj;
+ obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
- if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
- /* minimum priority for all nodes is nice 0 */
- obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
- } else {
- /* minimum priority for all nodes is MAX_NICE(19) */
- obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ int schedBits = 0;
+ if (!IPCThreadState::self()->backgroundSchedulingDisabled()) {
+ schedBits = schedPolicyMask(SCHED_NORMAL, 19);
}
if (binder != nullptr) {
@@ -213,6 +215,13 @@
obj.handle = handle;
obj.cookie = 0;
} else {
+ int policy = local->getMinSchedulerPolicy();
+ int priority = local->getMinSchedulerPriority();
+
+ if (policy != 0 || priority != 0) {
+ // override value, since it is set explicitly
+ schedBits = schedPolicyMask(policy, priority);
+ }
if (local->isRequestingSid()) {
obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
}
@@ -226,6 +235,8 @@
obj.cookie = 0;
}
+ obj.flags |= schedBits;
+
return finishFlattenBinder(binder, obj);
}
@@ -1514,7 +1525,7 @@
template<class T>
status_t Parcel::readAligned(T *pArg) const {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(T)) <= mDataSize) {
if (mObjectsSize > 0) {
@@ -1547,7 +1558,7 @@
template<class T>
status_t Parcel::writeAligned(T val) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index f3861bb..ef93ba1 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -32,6 +32,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <mutex>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -73,38 +74,49 @@
sp<ProcessState> ProcessState::self()
{
- Mutex::Autolock _l(gProcessMutex);
- if (gProcess != nullptr) {
- return gProcess;
- }
- gProcess = new ProcessState(kDefaultDriver);
- return gProcess;
+ return init(kDefaultDriver, false /*requireDefault*/);
}
sp<ProcessState> ProcessState::initWithDriver(const char* driver)
{
- Mutex::Autolock _l(gProcessMutex);
- if (gProcess != nullptr) {
- // Allow for initWithDriver to be called repeatedly with the same
- // driver.
- if (!strcmp(gProcess->getDriverName().c_str(), driver)) {
- return gProcess;
- }
- LOG_ALWAYS_FATAL("ProcessState was already initialized.");
- }
-
- if (access(driver, R_OK) == -1) {
- ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
- driver = "/dev/binder";
- }
-
- gProcess = new ProcessState(driver);
- return gProcess;
+ return init(driver, true /*requireDefault*/);
}
sp<ProcessState> ProcessState::selfOrNull()
{
- Mutex::Autolock _l(gProcessMutex);
+ return init(nullptr, false /*requireDefault*/);
+}
+
+sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
+{
+ [[clang::no_destroy]] static sp<ProcessState> gProcess;
+ [[clang::no_destroy]] static std::mutex gProcessMutex;
+
+ if (driver == nullptr) {
+ std::lock_guard<std::mutex> l(gProcessMutex);
+ return gProcess;
+ }
+
+ [[clang::no_destroy]] static std::once_flag gProcessOnce;
+ std::call_once(gProcessOnce, [&](){
+ if (access(driver, R_OK) == -1) {
+ ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
+ driver = "/dev/binder";
+ }
+
+ std::lock_guard<std::mutex> l(gProcessMutex);
+ gProcess = new ProcessState(driver);
+ });
+
+ if (requireDefault) {
+ // Detect if we are trying to initialize with a different driver, and
+ // consider that an error. ProcessState will only be initialized once above.
+ LOG_ALWAYS_FATAL_IF(gProcess->getDriverName() != driver,
+ "ProcessState was already initialized with %s,"
+ " can't initialize with %s.",
+ gProcess->getDriverName().c_str(), driver);
+ }
+
return gProcess;
}
@@ -148,8 +160,8 @@
if (result != 0) {
android_errorWriteLog(0x534e4554, "121035042");
- int dummy = 0;
- result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+ int unused = 0;
+ result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused);
}
if (result == -1) {
@@ -392,7 +404,7 @@
, mCallRestriction(CallRestriction::NONE)
{
-// TODO(b/139016109): enforce in build system
+// TODO(b/166468760): enforce in build system
#if defined(__ANDROID_APEX__)
LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index e1565fa..6115aec 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -22,7 +22,7 @@
namespace internal {
void Stability::markCompilationUnit(IBinder* binder) {
- status_t result = set(binder, kLocalStability, true /*log*/);
+ status_t result = set(binder, getLocalStability(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
@@ -45,7 +45,26 @@
}
void Stability::tryMarkCompilationUnit(IBinder* binder) {
- (void) set(binder, kLocalStability, false /*log*/);
+ (void) set(binder, getLocalStability(), false /*log*/);
+}
+
+Stability::Level Stability::getLocalStability() {
+#ifdef __ANDROID_VNDK__
+ #ifdef __ANDROID_APEX__
+ // TODO(b/142684679) avoid use_vendor on system APEXes
+ #if !defined(__ANDROID_APEX_COM_ANDROID_MEDIA_SWCODEC__) \
+ && !defined(__ANDROID_APEX_TEST_COM_ANDROID_MEDIA_SWCODEC__)
+ #error VNDK + APEX only defined for com.android.media.swcodec
+ #endif
+ // TODO(b/142684679) avoid use_vendor on system APEXes
+ return Level::SYSTEM;
+ #else
+ return Level::VENDOR;
+ #endif
+#else
+ // TODO(b/139325195): split up stability levels for system/APEX.
+ return Level::SYSTEM;
+#endif
}
status_t Stability::set(IBinder* binder, int32_t stability, bool log) {
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index 779ed41..db0f1c7 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -68,9 +68,4 @@
TextOutput& aout(*new FdTextOutput(STDOUT_FILENO));
TextOutput& aerr(*new FdTextOutput(STDERR_FILENO));
-// ------------ ProcessState.cpp
-
-Mutex& gProcessMutex = *new Mutex;
-sp<ProcessState> gProcess;
-
} // namespace android
diff --git a/libs/binder/Static.h b/libs/binder/Static.h
index f8e0ee5..83524e8 100644
--- a/libs/binder/Static.h
+++ b/libs/binder/Static.h
@@ -27,8 +27,4 @@
// For TextStream.cpp
extern Vector<int32_t> gTextBuffers;
-// For ProcessState.cpp
-extern Mutex& gProcessMutex;
-extern sp<ProcessState> gProcess;
-
} // namespace android
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 74e52db..f3fea16 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -72,6 +72,22 @@
// This must be called before the object is sent to another process. Not thread safe.
void setExtension(const sp<IBinder>& extension);
+ // This must be called before the object is sent to another process. Not thread safe.
+ //
+ // This function will abort if improper parameters are set. This is like
+ // sched_setscheduler. However, it sets the minimum scheduling policy
+ // only for the duration that this specific binder object is handling the
+ // call in a threadpool. By default, this API is set to SCHED_NORMAL/0. In
+ // this case, the scheduling priority will not actually be modified from
+ // binder defaults. See also IPCThreadState::disableBackgroundScheduling.
+ //
+ // Appropriate values are:
+ // SCHED_NORMAL: -20 <= priority <= 19
+ // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99
+ void setMinSchedulerPolicy(int policy, int priority);
+ int getMinSchedulerPolicy();
+ int getMinSchedulerPriority();
+
pid_t getDebugPid();
protected:
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 8d51cdc..2bd39a7 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -85,8 +85,8 @@
int64_t clearCallingIdentity();
// Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
-
- int setupPolling(int* fd);
+
+ status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 6d711bc..d18c88e 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -26,7 +26,19 @@
class ClientCounterCallback;
} // namespace internal
-/** Exits when all services registered through this object have 0 clients */
+/**
+ * Exits when all services registered through this object have 0 clients
+ *
+ * In order to use this class, it's expected that your service:
+ * - registers all services in the process with this API
+ * - configures services as oneshot in init .rc files
+ * - configures services as disabled in init.rc files, unless a client is
+ * guaranteed early in boot, in which case, forcePersist should also be used
+ * to avoid races.
+ * - uses 'interface' declarations in init .rc files
+ *
+ * For more information on init .rc configuration, see system/core/init/README.md
+ **/
class LazyServiceRegistrar {
public:
static LazyServiceRegistrar& getInstance();
@@ -47,4 +59,4 @@
};
} // namespace binder
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index edada3d..52bd5de 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -71,14 +71,6 @@
/* this closes this heap -- use carefully */
void dispose();
- /* this is only needed as a workaround, use only if you know
- * what you are doing */
- status_t setDevice(const char* device) {
- if (mDevice == nullptr)
- mDevice = device;
- return mDevice ? NO_ERROR : ALREADY_EXISTS;
- }
-
protected:
MemoryHeapBase();
// init() takes ownership of fd
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
deleted file mode 100644
index a98583d..0000000
--- a/libs/binder/include/binder/Nullable.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <optional>
-#include <utility>
-
-namespace android {
-
-namespace aidl {
-
-// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr
-// usage:
-// nullable<Foo> a;
-// nullable<Foo> b = make_nullable<Foo>(...);
-// auto c = make_nullable<Foo>(...);
-// c.reset();
-// c = make_nullable<Foo>(...);
-// c = std::move(a);
-
-template <typename T>
-using nullable = std::optional<T>;
-
-template <typename T, typename... Args>
-inline nullable<T> make_nullable(Args&&... args) {
- return std::make_optional<T>(std::forward<Args>(args)...);
-}
-
-} // namespace aidl
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index c113279..83c2f19 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -53,11 +53,20 @@
// Returns android::OK on success and an appropriate error otherwise.
virtual status_t readFromParcel(const Parcel* parcel) = 0;
- // 'Stable' means this parcelable is guaranteed to be stable for multiple years.
+ // WARNING: for use by auto-generated code only (AIDL). Should not be used
+ // manually, or there is a risk of breaking CTS, GTS, VTS, or CTS-on-GSI
+ // tests.
+ enum class Stability {
+ STABILITY_LOCAL,
+ STABILITY_VINTF, // corresponds to @VintfStability
+ };
+
+ // 'Stable' means this parcelable is guaranteed to be stable for multiple
+ // years.
// It must be guaranteed by setting stability field in aidl_interface.
- // WARNING: isStable() is only expected to be overridden by auto-generated code.
- // Returns true if this parcelable is stable.
- virtual bool isStable() const { return false; }
+ // WARNING: getStability() is only expected to be overridden by auto-generated
+ // code. Returns true if this parcelable is stable.
+ virtual Stability getStability() const { return Stability::STABILITY_LOCAL; }
}; // class Parcelable
#if defined(__clang__)
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index e57ff1c..9f5346a 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -42,6 +42,8 @@
* any call to ProcessState::self(). The default is /dev/vndbinder
* for processes built with the VNDK and /dev/binder for those
* which are not.
+ *
+ * If this is called with nullptr, the behavior is the same as selfOrNull.
*/
static sp<ProcessState> initWithDriver(const char *driver);
@@ -90,6 +92,8 @@
void setCallRestriction(CallRestriction restriction);
private:
+ static sp<ProcessState> init(const char *defaultDriver, bool requireDefault);
+
friend class IPCThreadState;
explicit ProcessState(const char* driver);
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 2894482..6566285 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -81,11 +81,8 @@
VINTF = 0b111111,
};
-#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
- static constexpr Level kLocalStability = Level::VENDOR;
-#else
- static constexpr Level kLocalStability = Level::SYSTEM;
-#endif
+ // returns the stability according to how this was built
+ static Level getLocalStability();
// applies stability to binder if stability level is known
__attribute__((warn_unused_result))
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 2b61cf1..f59bb75 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -189,6 +189,7 @@
explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {}
~ScopedAParcel() {}
ScopedAParcel(ScopedAParcel&&) = default;
+ ScopedAParcel& operator=(ScopedAParcel&&) = default;
};
/**
@@ -273,6 +274,7 @@
: ScopedAResource(a) {}
~ScopedAIBinder_DeathRecipient() {}
ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default;
+ ScopedAIBinder_DeathRecipient& operator=(ScopedAIBinder_DeathRecipient&&) = default;
};
/**
@@ -287,6 +289,7 @@
explicit ScopedAIBinder_Weak(AIBinder_Weak* a = nullptr) : ScopedAResource(a) {}
~ScopedAIBinder_Weak() {}
ScopedAIBinder_Weak(ScopedAIBinder_Weak&&) = default;
+ ScopedAIBinder_Weak& operator=(ScopedAIBinder_Weak&&) = default;
/**
* See AIBinder_Weak_promote.
@@ -305,6 +308,7 @@
explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
+ ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
};
} // namespace ndk
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 33e4586..f44ce0c 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -88,13 +88,21 @@
static void operator delete(void* p) { std::free(p); }
+ // Once minSdkVersion is 30, we are guaranteed to be building with the
+ // Android 11 AIDL compiler which supports the SharedRefBase::make API.
+ //
+ // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
+ // ownership. Making this operator private to avoid double-ownership.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30
+ private:
+#else
+ [[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
+#endif
+ static void* operator new(size_t s) { return std::malloc(s); }
+
private:
std::once_flag mFlagThis;
std::weak_ptr<SharedRefBase> mThis;
-
- // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
- // ownership. Making this operator private to avoid double-ownership.
- static void* operator new(size_t s) { return std::malloc(s); }
};
/**
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 055c79b..52bcd20 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,6 +18,7 @@
#include <android/binder_ibinder.h>
#include <android/binder_status.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -50,4 +51,47 @@
*/
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+/**
+ * Registers a lazy service with the default service manager under the 'instance' name.
+ * Does not take ownership of binder.
+ * The service must be configured statically with init so it can be restarted with
+ * ctl.interface.* messages from servicemanager.
+ * AServiceManager_registerLazyService cannot safely be used with AServiceManager_addService
+ * in the same process. If one service is registered with AServiceManager_registerLazyService,
+ * the entire process will have its lifetime controlled by servicemanager.
+ * Instead, all services in the process should be registered using
+ * AServiceManager_registerLazyService.
+ *
+ * \param binder object to register globally with the service manager.
+ * \param instance identifier of the service. This will be used to lookup the service.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance)
+ __INTRODUCED_IN(31);
+
+/**
+ * Gets a binder object with this specific instance name. Efficiently waits for the service.
+ * If the service is not declared, it will wait indefinitely. Requires the threadpool
+ * to be started in the service.
+ * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
+ * for calling AIBinder_decStrong).
+ *
+ * \param instance identifier of the service used to lookup the service.
+ *
+ * \return service if registered, null if not.
+ */
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(const char* instance)
+ __INTRODUCED_IN(31);
+
+/**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * \param instance identifier of the service.
+ *
+ * \return true on success, meaning AServiceManager_waitForService should always
+ * be able to return the service.
+ */
+bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31);
+
__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index fdefbb4..f408fad 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -19,10 +19,15 @@
#include <stdint.h>
#include <sys/cdefs.h>
+#include <android/binder_status.h>
+
__BEGIN_DECLS
/**
* This creates a threadpool for incoming binder transactions if it has not already been created.
+ *
+ * When using this, it is expected that ABinderProcess_setupPolling and
+ * ABinderProcess_handlePolledCommands are not used.
*/
void ABinderProcess_startThreadPool();
/**
@@ -37,4 +42,27 @@
*/
void ABinderProcess_joinThreadPool();
+/**
+ * This gives you an fd to wait on. Whenever data is available on the fd,
+ * ABinderProcess_handlePolledCommands can be called to handle binder queries.
+ * This is expected to be used in a single threaded process which waits on
+ * events from multiple different fds.
+ *
+ * When using this, it is expected ABinderProcess_startThreadPool and
+ * ABinderProcess_joinThreadPool are not used.
+ *
+ * \param fd out param corresponding to the binder domain opened in this
+ * process.
+ * \return STATUS_OK on success
+ */
+__attribute__((weak)) binder_status_t ABinderProcess_setupPolling(int* fd) __INTRODUCED_IN(31);
+
+/**
+ * This will handle all queued binder commands in this process and then return.
+ * It is expected to be called whenever there is data on the fd.
+ *
+ * \return STATUS_OK on success
+ */
+__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands() __INTRODUCED_IN(31);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 9b5fa26..1701fb5 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -113,8 +113,13 @@
LIBBINDER_NDK31 { # introduced=31
global:
+ ABinderProcess_handlePolledCommands; # apex
+ ABinderProcess_setupPolling; # apex
AIBinder_getCallingSid; # apex
AIBinder_setRequestingSid; # apex
+ AServiceManager_isDeclared; # apex llndk
+ AServiceManager_registerLazyService; # llndk
+ AServiceManager_waitForService; # apex llndk
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index c89caaf..ac582a4 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -34,3 +34,11 @@
void ABinderProcess_joinThreadPool() {
IPCThreadState::self()->joinThreadPool();
}
+
+binder_status_t ABinderProcess_setupPolling(int* fd) {
+ return IPCThreadState::self()->setupPolling(fd);
+}
+
+binder_status_t ABinderProcess_handlePolledCommands() {
+ return IPCThreadState::self()->handlePolledCommands();
+}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index d0b166d..6b2184e 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -20,6 +20,7 @@
#include "status_internal.h"
#include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
using ::android::defaultServiceManager;
using ::android::IBinder;
@@ -61,3 +62,33 @@
AIBinder_incStrong(ret.get());
return ret.get();
}
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance) {
+ if (binder == nullptr || instance == nullptr) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+
+ auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
+ status_t status = serviceRegistrar.registerService(binder->getBinder(), instance);
+
+ return PruneStatusT(status);
+}
+AIBinder* AServiceManager_waitForService(const char* instance) {
+ if (instance == nullptr) {
+ return nullptr;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->waitForService(String16(instance));
+
+ sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder_incStrong(ret.get());
+ return ret.get();
+}
+bool AServiceManager_isDeclared(const char* instance) {
+ if (instance == nullptr) {
+ return false;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ return sm->isDeclared(String16(instance));
+}
diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp
index 5f5265c..7c271f6 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -40,6 +40,7 @@
cc_defaults {
name: "test_libbinder_ndk_test_defaults",
defaults: ["test_libbinder_ndk_defaults"],
+ // critical that libbinder/libbinder_ndk are shared for VTS
shared_libs: [
"libandroid_runtime_lazy",
"libbase",
@@ -63,7 +64,7 @@
"IBinderNdkUnitTest-cpp",
"IBinderNdkUnitTest-ndk_platform",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts"],
require_root: true,
// force since binderVendorDoubleLoadTest has its own
@@ -81,13 +82,14 @@
"IBinderVendorDoubleLoadTest-ndk_platform",
"libbinder_aidl_test_stub-ndk_platform",
],
+ // critical that libbinder/libbinder_ndk are shared for VTS
shared_libs: [
"libbase",
"libbinder",
"libbinder_ndk",
"libutils",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts"],
}
aidl_interface {
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index e3fdb4b..b2c412d 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -24,6 +24,7 @@
#include <android/binder_process.h>
#include <gtest/gtest.h>
#include <iface/iface.h>
+#include <utils/Looper.h>
// warning: this is assuming that libbinder_ndk is using the same copy
// of libbinder that we are.
@@ -42,6 +43,7 @@
constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) {
@@ -107,26 +109,66 @@
}
};
-int manualService(const char* instance) {
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
+void manualService(const char* instance) {
// Strong reference to MyFoo kept by service manager.
binder_status_t status = (new MyFoo)->addService(instance);
if (status != STATUS_OK) {
LOG(FATAL) << "Could not register: " << status << " " << instance;
}
+}
+int manualPollingService(const char* instance) {
+ int fd;
+ CHECK(STATUS_OK == ABinderProcess_setupPolling(&fd));
+ manualService(instance);
+
+ class Handler : public LooperCallback {
+ int handleEvent(int /*fd*/, int /*events*/, void* /*data*/) override {
+ ABinderProcess_handlePolledCommands();
+ return 1; // Continue receiving callbacks.
+ }
+ };
+
+ sp<Looper> looper = Looper::prepare(0 /* opts */);
+ looper->addFd(fd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, new Handler(), nullptr /*data*/);
+ // normally, would add additional fds
+ while (true) {
+ looper->pollAll(-1 /* timeoutMillis */);
+ }
+ return 1; // should not reach
+}
+int manualThreadPoolService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ manualService(instance);
+ ABinderProcess_joinThreadPool();
+ return 1;
+}
+
+int lazyService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ // Wait to register this service to make sure the main test process will
+ // actually wait for the service to be available. Tested with sleep(60),
+ // and reduced for sake of time.
+ sleep(1);
+ // Strong reference to MyBinderNdkUnitTest kept by service manager.
+ // This is just for testing, it has no corresponding init behavior.
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ auto binder = service->asBinder();
+
+ binder_status_t status = AServiceManager_registerLazyService(binder.get(), instance);
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
ABinderProcess_joinThreadPool();
return 1; // should not return
}
-// This is too slow
-// TEST(NdkBinder, GetServiceThatDoesntExist) {
-// sp<IFoo> foo = IFoo::getService("asdfghkl;");
-// EXPECT_EQ(nullptr, foo.get());
-// }
+TEST(NdkBinder, GetServiceThatDoesntExist) {
+ sp<IFoo> foo = IFoo::getService("asdfghkl;");
+ EXPECT_EQ(nullptr, foo.get());
+}
TEST(NdkBinder, CheckServiceThatDoesntExist) {
AIBinder* binder = AServiceManager_checkService("asdfghkl;");
@@ -150,6 +192,33 @@
EXPECT_EQ(2, out);
}
+TEST(NdkBinder, GetLazyService) {
+ // Not declared in the vintf manifest
+ ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService));
+ ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+ ASSERT_NE(service, nullptr);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+// This is too slow
+TEST(NdkBinder, CheckLazyServiceShutDown) {
+ ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+ ASSERT_NE(service, nullptr);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+ binder = nullptr;
+ service = nullptr;
+ IPCThreadState::self()->flushCommands();
+ // Make sure the service is dead after some time of no use
+ sleep(10);
+ ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService));
+}
+
void LambdaOnDeath(void* cookie) {
auto onDeath = static_cast<std::function<void(void)>*>(cookie);
(*onDeath)();
@@ -448,11 +517,15 @@
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
- return manualService(IFoo::kInstanceNameToDieFor);
+ return manualThreadPoolService(IFoo::kInstanceNameToDieFor);
}
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
- return manualService(IFoo::kSomeInstanceName);
+ return manualPollingService(IFoo::kSomeInstanceName);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return lazyService(kLazyBinderNdkUnitTestService);
}
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
new file mode 100644
index 0000000..0234820
--- /dev/null
+++ b/libs/binder/rust/Android.bp
@@ -0,0 +1,86 @@
+rust_library {
+ name: "libbinder_rs",
+ crate_name: "binder",
+ srcs: ["src/lib.rs"],
+ shared_libs: [
+ "libutils",
+ ],
+ rustlibs: [
+ "liblibc",
+ "libbinder_ndk_sys",
+ ],
+ host_supported: true,
+}
+
+rust_library {
+ name: "libbinder_ndk_sys",
+ crate_name: "binder_ndk_sys",
+ srcs: [
+ "sys/lib.rs",
+ ":libbinder_ndk_bindgen",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ host_supported: true,
+}
+
+rust_bindgen {
+ name: "libbinder_ndk_bindgen",
+ crate_name: "binder_ndk_bindgen",
+ wrapper_src: "sys/BinderBindings.h",
+ source_stem: "bindings",
+ cflags: [
+ "-x c++",
+ ],
+ bindgen_flags: [
+ // Unfortunately the only way to specify the rust_non_exhaustive enum
+ // style for a type is to make it the default
+ "--default-enum-style", "rust_non_exhaustive",
+ // and then specify constified enums for the enums we don't want
+ // rustified
+ "--constified-enum", "android::c_interface::consts::.*",
+
+ "--whitelist-type", "android::c_interface::.*",
+ "--whitelist-type", "AStatus",
+ "--whitelist-type", "AIBinder_Class",
+ "--whitelist-type", "AIBinder",
+ "--whitelist-type", "AIBinder_Weak",
+ "--whitelist-type", "AIBinder_DeathRecipient",
+ "--whitelist-type", "AParcel",
+ "--whitelist-type", "binder_status_t",
+ "--whitelist-function", ".*",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ host_supported: true,
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ host: {
+ cflags: [
+ "-D__INTRODUCED_IN(n)=",
+ "-D__assert(a,b,c)=",
+ // We want all the APIs to be available on the host.
+ "-D__ANDROID_API__=10000",
+ ],
+ },
+ },
+}
+
+rust_test {
+ name: "libbinder_rs-internal_test",
+ crate_name: "binder",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ rustlibs: [
+ "liblibc",
+ "libbinder_ndk_sys",
+ ],
+}
diff --git a/libs/binder/rust/TEST_MAPPING b/libs/binder/rust/TEST_MAPPING
new file mode 100644
index 0000000..50c474c
--- /dev/null
+++ b/libs/binder/rust/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "libbinder_rs-internal_test"
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "rustBinderTest"
+ }
+ ]
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
new file mode 100644
index 0000000..d55eafe
--- /dev/null
+++ b/libs/binder/rust/src/binder.rs
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Trait definitions for binder objects
+
+use crate::error::{status_t, Result};
+use crate::parcel::Parcel;
+use crate::proxy::{DeathRecipient, SpIBinder};
+use crate::sys;
+
+use std::ffi::{c_void, CString};
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+
+/// Binder action to perform.
+///
+/// This must be a number between [`IBinder::FIRST_CALL_TRANSACTION`] and
+/// [`IBinder::LAST_CALL_TRANSACTION`].
+pub type TransactionCode = u32;
+
+/// Additional operation flags.
+///
+/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a
+/// one-way RPC.
+pub type TransactionFlags = u32;
+
+/// Super-trait for Binder interfaces.
+///
+/// This trait allows conversion of a Binder interface trait object into an
+/// IBinder object for IPC calls. All Binder remotable interface (i.e. AIDL
+/// interfaces) must implement this trait.
+///
+/// This is equivalent `IInterface` in C++.
+pub trait Interface {
+ /// Convert this binder object into a generic [`SpIBinder`] reference.
+ fn as_binder(&self) -> SpIBinder {
+ panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
+ }
+}
+
+/// A local service that can be remotable via Binder.
+///
+/// An object that implement this interface made be made into a Binder service
+/// via `Binder::new(object)`.
+///
+/// This is a low-level interface that should normally be automatically
+/// generated from AIDL via the [`declare_binder_interface!`] macro. When using
+/// the AIDL backend, users need only implement the high-level AIDL-defined
+/// interface. The AIDL compiler then generates a container struct that wraps
+/// the user-defined service and implements `Remotable`.
+pub trait Remotable: Send + Sync {
+ /// The Binder interface descriptor string.
+ ///
+ /// This string is a unique identifier for a Binder interface, and should be
+ /// the same between all implementations of that interface.
+ fn get_descriptor() -> &'static str;
+
+ /// 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: &Parcel, reply: &mut Parcel) -> Result<()>;
+
+ /// Retrieve the class of this remote object.
+ ///
+ /// This method should always return the same InterfaceClass for the same
+ /// type.
+ fn get_class() -> InterfaceClass;
+}
+
+/// Interface of binder local or remote objects.
+///
+/// This trait corresponds to the interface of the C++ `IBinder` class.
+pub trait IBinder {
+ /// First transaction code available for user commands (inclusive)
+ const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
+ /// Last transaction code available for user commands (inclusive)
+ const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
+
+ /// Corresponds to TF_ONE_WAY -- an asynchronous call.
+ const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+
+ /// Is this object still alive?
+ fn is_binder_alive(&self) -> bool;
+
+ /// Send a ping transaction to this object
+ fn ping_binder(&mut self) -> Result<()>;
+
+ /// Dump this object to the given file handle
+ fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()>;
+
+ /// Get a new interface that exposes additional extension functionality, if
+ /// available.
+ fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
+
+ /// Perform a generic operation with the object.
+ ///
+ /// # Arguments
+ /// * `code` - Transaction code for the operation
+ /// * `data` - [`Parcel`] with input data
+ /// * `reply` - Optional [`Parcel`] for reply data
+ /// * `flags` - Transaction flags, e.g. marking the transaction as
+ /// asynchronous ([`FLAG_ONEWAY`](IBinder::FLAG_ONEWAY))
+ fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+ &self,
+ code: TransactionCode,
+ flags: TransactionFlags,
+ input_callback: F,
+ ) -> Result<Parcel>;
+
+ /// Register the recipient for a notification if this binder
+ /// goes away. If this binder object unexpectedly goes away
+ /// (typically because its hosting process has been killed),
+ /// then DeathRecipient::binder_died() will be called with a reference
+ /// to this.
+ ///
+ /// You will only receive death notifications for remote binders,
+ /// as local binders by definition can't die without you dying as well.
+ /// Trying to use this function on a local binder will result in an
+ /// INVALID_OPERATION code being returned and nothing happening.
+ ///
+ /// This link always holds a weak reference to its recipient.
+ ///
+ /// You will only receive a weak reference to the dead
+ /// binder. You should not try to promote this to a strong reference.
+ /// (Nor should you need to, as there is nothing useful you can
+ /// directly do with it now that it has passed on.)
+ fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+ /// Remove a previously registered death notification.
+ /// The recipient will no longer be called if this object
+ /// dies.
+ fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+}
+
+/// Opaque reference to the type of a Binder interface.
+///
+/// This object encapsulates the Binder interface descriptor string, along with
+/// the binder transaction callback, if the class describes a local service.
+///
+/// A Binder remotable object may only have a single interface class, and any
+/// given object can only be associated with one class. Two objects with
+/// different classes are incompatible, even if both classes have the same
+/// interface descriptor.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct InterfaceClass(*const sys::AIBinder_Class);
+
+impl InterfaceClass {
+ /// Get a Binder NDK `AIBinder_Class` pointer for this object type.
+ ///
+ /// Note: the returned pointer will not be constant. Calling this method
+ /// multiple times for the same type will result in distinct class
+ /// pointers. A static getter for this value is implemented in
+ /// [`declare_binder_interface!`].
+ pub fn new<I: InterfaceClassMethods>() -> InterfaceClass {
+ let descriptor = CString::new(I::get_descriptor()).unwrap();
+ let ptr = unsafe {
+ // Safety: `AIBinder_Class_define` expects a valid C string, and
+ // three valid callback functions, all non-null pointers. The C
+ // string is copied and need not be valid for longer than the call,
+ // so we can drop it after the call. We can safely assign null to
+ // the onDump and handleShellCommand callbacks as long as the class
+ // pointer was non-null. Rust None for a Option<fn> is guaranteed to
+ // be a NULL pointer. Rust retains ownership of the pointer after it
+ // is defined.
+ let class = sys::AIBinder_Class_define(
+ descriptor.as_ptr(),
+ Some(I::on_create),
+ Some(I::on_destroy),
+ Some(I::on_transact),
+ );
+ if class.is_null() {
+ panic!("Expected non-null class pointer from AIBinder_Class_define!");
+ }
+ sys::AIBinder_Class_setOnDump(class, None);
+ sys::AIBinder_Class_setHandleShellCommand(class, None);
+ class
+ };
+ InterfaceClass(ptr)
+ }
+
+ /// Construct an `InterfaceClass` out of a raw, non-null `AIBinder_Class`
+ /// pointer.
+ ///
+ /// # Safety
+ ///
+ /// This function is safe iff `ptr` is a valid, non-null pointer to an
+ /// `AIBinder_Class`.
+ pub(crate) unsafe fn from_ptr(ptr: *const sys::AIBinder_Class) -> InterfaceClass {
+ InterfaceClass(ptr)
+ }
+}
+
+impl From<InterfaceClass> for *const sys::AIBinder_Class {
+ fn from(class: InterfaceClass) -> *const sys::AIBinder_Class {
+ class.0
+ }
+}
+
+/// Create a function implementing a static getter for an interface class.
+///
+/// Each binder interface (i.e. local [`Remotable`] service or remote proxy
+/// [`Interface`]) must have global, static class that uniquely identifies
+/// it. This macro implements an [`InterfaceClass`] getter to simplify these
+/// implementations.
+///
+/// The type of a structure that implements [`InterfaceClassMethods`] must be
+/// passed to this macro. For local services, this should be `Binder<Self>`
+/// since [`Binder`] implements [`InterfaceClassMethods`].
+///
+/// # Examples
+///
+/// When implementing a local [`Remotable`] service `ExampleService`, the
+/// `get_class` method is required in the [`Remotable`] impl block. This macro
+/// should be used as follows to implement this functionality:
+///
+/// ```rust
+/// impl Remotable for ExampleService {
+/// fn get_descriptor() -> &'static str {
+/// "android.os.IExampleInterface"
+/// }
+///
+/// fn on_transact(
+/// &self,
+/// code: TransactionCode,
+/// data: &Parcel,
+/// reply: &mut Parcel,
+/// ) -> Result<()> {
+/// // ...
+/// }
+///
+/// binder_fn_get_class!(Binder<Self>);
+/// }
+/// ```
+macro_rules! binder_fn_get_class {
+ ($class:ty) => {
+ binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+ };
+
+ ($constructor:expr) => {
+ fn get_class() -> $crate::InterfaceClass {
+ static CLASS_INIT: std::sync::Once = std::sync::Once::new();
+ static mut CLASS: Option<$crate::InterfaceClass> = None;
+
+ CLASS_INIT.call_once(|| unsafe {
+ // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+ // variable, and therefore is thread-safe, as it can only occur
+ // once.
+ CLASS = Some($constructor);
+ });
+ unsafe {
+ // Safety: The `CLASS` variable can only be mutated once, above,
+ // and is subsequently safe to read from any thread.
+ CLASS.unwrap()
+ }
+ }
+ };
+}
+
+pub trait InterfaceClassMethods {
+ /// Get the interface descriptor string for this object type.
+ fn get_descriptor() -> &'static str
+ where
+ Self: Sized;
+
+ /// Called during construction of a new `AIBinder` object of this interface
+ /// class.
+ ///
+ /// The opaque pointer parameter will be the parameter provided to
+ /// `AIBinder_new`. Returns an opaque userdata to be associated with the new
+ /// `AIBinder` object.
+ ///
+ /// # Safety
+ ///
+ /// Callback called from C++. The parameter argument provided to
+ /// `AIBinder_new` must match the type expected here. The `AIBinder` object
+ /// will take ownership of the returned pointer, which it will free via
+ /// `on_destroy`.
+ unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void;
+
+ /// Called when a transaction needs to be processed by the local service
+ /// implementation.
+ ///
+ /// # Safety
+ ///
+ /// Callback called from C++. The `binder` parameter must be a valid pointer
+ /// to a binder object of this class with userdata initialized via this
+ /// class's `on_create`. The parcel parameters must be valid pointers to
+ /// parcel objects.
+ unsafe extern "C" fn on_transact(
+ binder: *mut sys::AIBinder,
+ code: u32,
+ data: *const sys::AParcel,
+ reply: *mut sys::AParcel,
+ ) -> status_t;
+
+ /// Called whenever an `AIBinder` object is no longer referenced and needs
+ /// to be destroyed.
+ ///
+ /// # Safety
+ ///
+ /// Callback called from C++. The opaque pointer parameter must be the value
+ /// returned by `on_create` for this class. This function takes ownership of
+ /// the provided pointer and destroys it.
+ unsafe extern "C" fn on_destroy(object: *mut c_void);
+}
+
+/// Interface for transforming a generic SpIBinder into a specific remote
+/// interface trait.
+///
+/// # Example
+///
+/// For Binder interface `IFoo`, the following implementation should be made:
+/// ```no_run
+/// # use binder::{FromIBinder, SpIBinder, Result};
+/// # trait IFoo {}
+/// impl FromIBinder for dyn IFoo {
+/// fn try_from(ibinder: SpIBinder) -> Result<Box<Self>> {
+/// // ...
+/// # Err(binder::StatusCode::OK)
+/// }
+/// }
+/// ```
+pub trait FromIBinder {
+ /// Try to interpret a generic Binder object as this interface.
+ ///
+ /// Returns a trait object for the `Self` interface if this object
+ /// implements that interface.
+ fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>;
+}
+
+/// Trait for transparent Rust wrappers around android C++ native types.
+///
+/// The pointer return by this trait's methods should be immediately passed to
+/// C++ and not stored by Rust. The pointer is valid only as long as the
+/// underlying C++ object is alive, so users must be careful to take this into
+/// account, as Rust cannot enforce this.
+///
+/// # Safety
+///
+/// For this trait to be a correct implementation, `T` must be a valid android
+/// C++ type. Since we cannot constrain this via the type system, this trait is
+/// marked as unsafe.
+pub unsafe trait AsNative<T> {
+ /// Return a pointer to the native version of `self`
+ fn as_native(&self) -> *const T;
+
+ /// Return a mutable pointer to the native version of `self`
+ fn as_native_mut(&mut self) -> *mut T;
+}
+
+unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> {
+ fn as_native(&self) -> *const T {
+ self.as_ref().map_or(ptr::null(), |v| v.as_native())
+ }
+
+ fn as_native_mut(&mut self) -> *mut T {
+ self.as_mut().map_or(ptr::null_mut(), |v| v.as_native_mut())
+ }
+}
+
+/// Declare typed interfaces for a binder object.
+///
+/// Given an interface trait and descriptor string, create a native and remote
+/// proxy wrapper for this interface. The native service object (`$native`)
+/// implements `Remotable` and will dispatch to the function `$on_transact` to
+/// handle transactions. The typed proxy object (`$proxy`) wraps remote binder
+/// objects for this interface and can optionally contain additional fields.
+///
+/// Assuming the interface trait is `Interface`, `$on_transact` function must
+/// have the following type:
+///
+/// ```
+/// # use binder::{Interface, TransactionCode, Parcel};
+/// # trait Placeholder {
+/// fn on_transact(
+/// service: &dyn Interface,
+/// code: TransactionCode,
+/// data: &Parcel,
+/// reply: &mut Parcel,
+/// ) -> binder::Result<()>;
+/// # }
+/// ```
+///
+/// # Examples
+///
+/// The following example declares the local service type `BnServiceManager` and
+/// a remote proxy type `BpServiceManager` (the `n` and `p` stand for native and
+/// proxy respectively) for the `IServiceManager` Binder interface. The
+/// interfaces will be identified by the descriptor string
+/// "android.os.IServiceManager". The local service will dispatch transactions
+/// using the provided function, `on_transact`.
+///
+/// ```
+/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel};
+///
+/// pub trait IServiceManager: Interface {
+/// // remote methods...
+/// }
+///
+/// declare_binder_interface! {
+/// IServiceManager["android.os.IServiceManager"] {
+/// native: BnServiceManager(on_transact),
+/// proxy: BpServiceManager,
+/// }
+/// }
+///
+/// fn on_transact(
+/// service: &dyn IServiceManager,
+/// code: TransactionCode,
+/// data: &Parcel,
+/// reply: &mut Parcel,
+/// ) -> binder::Result<()> {
+/// // ...
+/// Ok(())
+/// }
+///
+/// impl IServiceManager for BpServiceManager {
+/// // parceling/unparceling code for the IServiceManager emitted here
+/// }
+///
+/// impl IServiceManager for Binder<BnServiceManager> {
+/// // Forward calls to local implementation
+/// }
+/// ```
+#[macro_export]
+macro_rules! declare_binder_interface {
+ {
+ $interface:path[$descriptor:expr] {
+ native: $native:ident($on_transact:path),
+ proxy: $proxy:ident,
+ }
+ } => {
+ $crate::declare_binder_interface! {
+ $interface[$descriptor] {
+ native: $native($on_transact),
+ proxy: $proxy {},
+ }
+ }
+ };
+
+ {
+ $interface:path[$descriptor:expr] {
+ native: $native:ident($on_transact:path),
+ proxy: $proxy:ident {
+ $($fname:ident: $fty:ty = $finit:expr),*
+ },
+ }
+ } => {
+ $crate::declare_binder_interface! {
+ $interface[$descriptor] {
+ @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+ native: $native($on_transact),
+ @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+ proxy: $proxy {
+ $($fname: $fty = $finit),*
+ },
+ }
+ }
+ };
+
+ {
+ $interface:path[$descriptor:expr] {
+ @doc[$native_doc:expr]
+ native: $native:ident($on_transact:path),
+
+ @doc[$proxy_doc:expr]
+ proxy: $proxy:ident {
+ $($fname:ident: $fty:ty = $finit:expr),*
+ },
+ }
+ } => {
+ #[doc = $proxy_doc]
+ pub struct $proxy {
+ binder: $crate::SpIBinder,
+ $($fname: $fty,)*
+ }
+
+ impl $crate::Interface for $proxy {
+ fn as_binder(&self) -> $crate::SpIBinder {
+ self.binder.clone()
+ }
+ }
+
+ impl $crate::Proxy for $proxy
+ where
+ $proxy: $interface,
+ {
+ fn get_descriptor() -> &'static str {
+ $descriptor
+ }
+
+ fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+ use $crate::AssociateClass;
+ if binder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ Ok(Self { binder, $($fname: $finit),* })
+ } else {
+ Err($crate::StatusCode::BAD_TYPE)
+ }
+ }
+ }
+
+ #[doc = $native_doc]
+ #[repr(transparent)]
+ pub struct $native(Box<dyn $interface + Sync + Send + 'static>);
+
+ impl $native {
+ /// Create a new binder service.
+ pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface {
+ $crate::Binder::new($native(Box::new(inner)))
+ }
+ }
+
+ impl $crate::Remotable for $native {
+ fn get_descriptor() -> &'static str {
+ $descriptor
+ }
+
+ fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> {
+ $on_transact(&*self.0, code, data, reply)
+ }
+
+ fn get_class() -> $crate::InterfaceClass {
+ static CLASS_INIT: std::sync::Once = std::sync::Once::new();
+ static mut CLASS: Option<$crate::InterfaceClass> = None;
+
+ CLASS_INIT.call_once(|| unsafe {
+ // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+ // variable, and therefore is thread-safe, as it can only occur
+ // once.
+ CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+ });
+ unsafe {
+ // Safety: The `CLASS` variable can only be mutated once, above,
+ // and is subsequently safe to read from any thread.
+ CLASS.unwrap()
+ }
+ }
+ }
+
+ impl $crate::FromIBinder for dyn $interface {
+ fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> {
+ use $crate::AssociateClass;
+ if !ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ return Err($crate::StatusCode::BAD_TYPE.into());
+ }
+
+ let service: $crate::Result<$crate::Binder<$native>> = std::convert::TryFrom::try_from(ibinder.clone());
+ if let Ok(service) = service {
+ Ok(Box::new(service))
+ } else {
+ Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))
+ }
+ }
+ }
+
+ impl $crate::parcel::Serialize for dyn $interface + '_
+ where
+ $interface: $crate::Interface
+ {
+ fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ let binder = $crate::Interface::as_binder(self);
+ parcel.write(&binder)
+ }
+ }
+
+ impl $crate::parcel::SerializeOption for dyn $interface + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ parcel.write(&this.map($crate::Interface::as_binder))
+ }
+ }
+ };
+}
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
new file mode 100644
index 0000000..4492cf7
--- /dev/null
+++ b/libs/binder/rust/src/error.rs
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::AsNative;
+use crate::sys;
+
+use std::error;
+use std::ffi::CStr;
+use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
+use std::result;
+
+pub use sys::binder_status_t as status_t;
+
+/// Low-level status codes from Android `libutils`.
+// All error codes are negative integer values. Derived from the anonymous enum
+// in utils/Errors.h
+pub use sys::android_c_interface_StatusCode as StatusCode;
+
+/// A specialized [`Result`](result::Result) for binder operations.
+pub type Result<T> = result::Result<T, StatusCode>;
+
+/// Convert a low-level status code into an empty result.
+///
+/// An OK status is converted into an `Ok` result, any other status is converted
+/// into an `Err` result holding the status code.
+pub fn status_result(status: status_t) -> Result<()> {
+ match parse_status_code(status) {
+ StatusCode::OK => Ok(()),
+ e => Err(e),
+ }
+}
+
+fn parse_status_code(code: i32) -> StatusCode {
+ match code {
+ e if e == StatusCode::OK as i32 => StatusCode::OK,
+ e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY,
+ e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION,
+ e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE,
+ e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE,
+ e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND,
+ e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED,
+ e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT,
+ e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS,
+ e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT,
+ e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION,
+ e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX,
+ e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA,
+ e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK,
+ e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT,
+ e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION,
+ e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED,
+ e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL,
+ _ => StatusCode::UNKNOWN_ERROR,
+ }
+}
+
+pub use sys::android_c_interface_ExceptionCode as ExceptionCode;
+
+fn parse_exception_code(code: i32) -> ExceptionCode {
+ match code {
+ e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE,
+ e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY,
+ e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE,
+ e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
+ e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
+ e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
+ e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => {
+ ExceptionCode::NETWORK_MAIN_THREAD
+ }
+ e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
+ ExceptionCode::UNSUPPORTED_OPERATION
+ }
+ e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC,
+ _ => ExceptionCode::TRANSACTION_FAILED,
+ }
+}
+
+// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
+// lifetime of the contained pointer is the same as the `Status` object.
+/// High-level binder status object that encapsulates a standard way to keep
+/// track of and chain binder errors along with service specific errors.
+///
+/// Used in AIDL transactions to represent failed transactions.
+pub struct Status(*mut sys::AStatus);
+
+impl Status {
+ /// Create a status object representing a successful transaction.
+ pub fn ok() -> Self {
+ let ptr = unsafe {
+ // Safety: `AStatus_newOk` always returns a new, heap allocated
+ // pointer to an `ASTatus` object, so we know this pointer will be
+ // valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ sys::AStatus_newOk()
+ };
+ Self(ptr)
+ }
+
+ /// Create a status object from a service specific error
+ pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
+ let ptr = if let Some(message) = message {
+ unsafe {
+ // Safety: Any i32 is a valid service specific error for the
+ // error code parameter. We construct a valid, null-terminated
+ // `CString` from the message, which must be a valid C-style
+ // string to pass as the message. This function always returns a
+ // new, heap allocated pointer to an `AStatus` object, so we
+ // know the returned pointer will be valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
+ }
+ } else {
+ unsafe {
+ // Safety: Any i32 is a valid service specific error for the
+ // error code parameter. This function always returns a new,
+ // heap allocated pointer to an `AStatus` object, so we know the
+ // returned pointer will be valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ sys::AStatus_fromServiceSpecificError(err)
+ }
+ };
+ Self(ptr)
+ }
+
+ /// Create a status object from an exception code
+ pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
+ if let Some(message) = message {
+ let ptr = unsafe {
+ sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
+ };
+ Self(ptr)
+ } else {
+ exception.into()
+ }
+ }
+
+ /// Create a status object from a raw `AStatus` pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
+ pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
+ Self(ptr)
+ }
+
+ /// Returns `true` if this status represents a successful transaction.
+ pub fn is_ok(&self) -> bool {
+ unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_isOk` here.
+ sys::AStatus_isOk(self.as_native())
+ }
+ }
+
+ /// Returns a description of the status.
+ pub fn get_description(&self) -> String {
+ let description_ptr = unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getDescription`
+ // here.
+ //
+ // `AStatus_getDescription` always returns a valid pointer to a null
+ // terminated C string. Rust is responsible for freeing this pointer
+ // via `AStatus_deleteDescription`.
+ sys::AStatus_getDescription(self.as_native())
+ };
+ let description = unsafe {
+ // Safety: `AStatus_getDescription` always returns a valid C string,
+ // which can be safely converted to a `CStr`.
+ CStr::from_ptr(description_ptr)
+ };
+ let description = description.to_string_lossy().to_string();
+ unsafe {
+ // Safety: `description_ptr` was returned from
+ // `AStatus_getDescription` above, and must be freed via
+ // `AStatus_deleteDescription`. We must not access the pointer after
+ // this call, so we copy it into an owned string above and return
+ // that string.
+ sys::AStatus_deleteDescription(description_ptr);
+ }
+ description
+ }
+
+ /// Returns the exception code of the status.
+ pub fn exception_code(&self) -> ExceptionCode {
+ let code = unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getExceptionCode`
+ // here.
+ sys::AStatus_getExceptionCode(self.as_native())
+ };
+ parse_exception_code(code)
+ }
+
+ /// Return a status code representing a transaction failure, or
+ /// `StatusCode::OK` if there was no transaction failure.
+ ///
+ /// If this method returns `OK`, the status may still represent a different
+ /// exception or a service specific error. To find out if this transaction
+ /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
+ pub fn transaction_error(&self) -> StatusCode {
+ let code = unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getStatus` here.
+ sys::AStatus_getStatus(self.as_native())
+ };
+ parse_status_code(code)
+ }
+
+ /// Return a service specific error if this status represents one.
+ ///
+ /// This function will only ever return a non-zero result if
+ /// [`exception_code`](Self::exception_code) returns
+ /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
+ /// status object may still represent a different exception or status. To
+ /// find out if this transaction as a whole is okay, use
+ /// [`is_ok`](Self::is_ok) instead.
+ pub fn service_specific_error(&self) -> i32 {
+ unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to
+ // `AStatus_getServiceSpecificError` here.
+ sys::AStatus_getServiceSpecificError(self.as_native())
+ }
+ }
+
+ /// Calls `op` if the status was ok, otherwise returns an `Err` value of
+ /// `self`.
+ pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status>
+ where
+ F: FnOnce() -> result::Result<T, Status>,
+ {
+ <result::Result<(), Status>>::from(self)?;
+ op()
+ }
+}
+
+impl error::Error for Status {}
+
+impl Display for Status {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ f.write_str(&self.get_description())
+ }
+}
+
+impl Debug for Status {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ f.write_str(&self.get_description())
+ }
+}
+
+impl PartialEq for Status {
+ fn eq(&self, other: &Status) -> bool {
+ let self_code = self.exception_code();
+ let other_code = other.exception_code();
+
+ match (self_code, other_code) {
+ (ExceptionCode::NONE, ExceptionCode::NONE) => true,
+ (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => {
+ self.transaction_error() == other.transaction_error()
+ && self.get_description() == other.get_description()
+ }
+ (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => {
+ self.service_specific_error() == other.service_specific_error()
+ && self.get_description() == other.get_description()
+ }
+ (e1, e2) => e1 == e2 && self.get_description() == other.get_description(),
+ }
+ }
+}
+
+impl Eq for Status {}
+
+impl From<StatusCode> for Status {
+ fn from(status: StatusCode) -> Status {
+ (status as status_t).into()
+ }
+}
+
+impl From<status_t> for Status {
+ fn from(status: status_t) -> Status {
+ let ptr = unsafe {
+ // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
+ // this is a safe FFI call. Unknown values will be coerced into
+ // UNKNOWN_ERROR.
+ sys::AStatus_fromStatus(status)
+ };
+ Self(ptr)
+ }
+}
+
+impl From<ExceptionCode> for Status {
+ fn from(code: ExceptionCode) -> Status {
+ let ptr = unsafe {
+ // Safety: `AStatus_fromExceptionCode` expects any
+ // `binder_exception_t` (i32) integer, so this is a safe FFI call.
+ // Unknown values will be coerced into EX_TRANSACTION_FAILED.
+ sys::AStatus_fromExceptionCode(code as i32)
+ };
+ Self(ptr)
+ }
+}
+
+// TODO: impl Try for Status when try_trait is stabilized
+// https://github.com/rust-lang/rust/issues/42327
+impl From<Status> for result::Result<(), Status> {
+ fn from(status: Status) -> result::Result<(), Status> {
+ if status.is_ok() {
+ Ok(())
+ } else {
+ Err(status)
+ }
+ }
+}
+
+impl From<Status> for status_t {
+ fn from(status: Status) -> status_t {
+ status.transaction_error() as status_t
+ }
+}
+
+impl Drop for Status {
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: `Status` manages the lifetime of its inner `AStatus`
+ // pointee, so we need to delete it here. We know that the pointer
+ // will be valid here since `Status` always contains a valid pointer
+ // while it is alive.
+ sys::AStatus_delete(self.0);
+ }
+ }
+}
+
+/// # Safety
+///
+/// `Status` always contains a valid pointer to an `AStatus` object, so we can
+/// trivially convert it to a correctly-typed raw pointer.
+///
+/// Care must be taken that the returned pointer is only dereferenced while the
+/// `Status` object is still alive.
+unsafe impl AsNative<sys::AStatus> for Status {
+ fn as_native(&self) -> *const sys::AStatus {
+ self.0
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AStatus {
+ self.0
+ }
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
new file mode 100644
index 0000000..8ee6a62
--- /dev/null
+++ b/libs/binder/rust/src/lib.rs
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Safe Rust interface to Android `libbinder`.
+//!
+//! This crate is primarily designed as an target for a Rust AIDL compiler
+//! backend, and should generally not be used directly by users. It is built on
+//! top of the binder NDK library to be usable by APEX modules, and therefore
+//! only exposes functionality available in the NDK interface.
+//!
+//! # Example
+//!
+//! The following example illustrates how the AIDL backend will use this crate.
+//!
+//! ```
+//! use binder::{
+//! declare_binder_interface, Binder, IBinder, Interface, Remotable, Parcel, SpIBinder,
+//! StatusCode, TransactionCode,
+//! };
+//!
+//! // Generated by AIDL compiler
+//! pub trait ITest: Interface {
+//! fn test(&self) -> binder::Result<String>;
+//! }
+//!
+//! // Creates a new local (native) service object, BnTest, and a remote proxy
+//! // object, BpTest, that are the typed interfaces for their respective ends
+//! // of the binder transaction. Generated by AIDL compiler.
+//! declare_binder_interface! {
+//! ITest["android.os.ITest"] {
+//! native: BnTest(on_transact),
+//! proxy: BpTest,
+//! }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! fn on_transact(
+//! service: &dyn ITest,
+//! code: TransactionCode,
+//! _data: &Parcel,
+//! reply: &mut Parcel,
+//! ) -> binder::Result<()> {
+//! match code {
+//! SpIBinder::FIRST_CALL_TRANSACTION => {
+//! reply.write(&service.test()?)?;
+//! Ok(())
+//! }
+//! _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+//! }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! impl ITest for Binder<BnTest> {
+//! fn test(&self) -> binder::Result<String> {
+//! self.0.test()
+//! }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! impl ITest for BpTest {
+//! fn test(&self) -> binder::Result<String> {
+//! let reply = self
+//! .as_binder()
+//! .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+//! reply.read()
+//! }
+//! }
+//!
+//! // User implemented:
+//!
+//! // Local implementation of the ITest remotable interface.
+//! struct TestService;
+//!
+//! impl Interface for TestService {}
+//!
+//! impl ITest for TestService {
+//! fn test(&self) -> binder::Result<String> {
+//! Ok("testing service".to_string())
+//! }
+//! }
+//! ```
+
+#[macro_use]
+mod proxy;
+
+#[macro_use]
+mod binder;
+mod error;
+mod native;
+mod state;
+
+use binder_ndk_sys as sys;
+
+pub mod parcel;
+
+pub use crate::binder::{
+ FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags,
+};
+pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
+pub use native::add_service;
+pub use native::Binder;
+pub use parcel::Parcel;
+pub use proxy::{get_interface, get_service};
+pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use state::{ProcessState, ThreadState};
+
+/// The public API usable outside AIDL-generated interface crates.
+pub mod public_api {
+ pub use super::parcel::ParcelFileDescriptor;
+ pub use super::{add_service, get_interface};
+ pub use super::{
+ ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode,
+ };
+
+ /// Binder result containing a [`Status`] on error.
+ pub type Result<T> = std::result::Result<T, Status>;
+}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
new file mode 100644
index 0000000..185645e
--- /dev/null
+++ b/libs/binder/rust/src/native.rs
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
+use crate::error::{status_result, status_t, Result, StatusCode};
+use crate::parcel::{Parcel, Serialize};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryFrom;
+use std::ffi::{c_void, CString};
+use std::mem::ManuallyDrop;
+use std::ops::Deref;
+use std::ptr;
+
+/// Rust wrapper around Binder remotable objects.
+///
+/// Implements the C++ `BBinder` class, and therefore implements the C++
+/// `IBinder` interface.
+#[repr(C)]
+pub struct Binder<T: Remotable> {
+ ibinder: *mut sys::AIBinder,
+ rust_object: *mut T,
+}
+
+/// # Safety
+///
+/// A `Binder<T>` is a pair of unique owning pointers to two values:
+/// * a C++ ABBinder which the C++ API guarantees can be passed between threads
+/// * a Rust object which implements `Remotable`; this trait requires `Send + Sync`
+///
+/// Both pointers are unique (never escape the `Binder<T>` object and are not copied)
+/// so we can essentially treat `Binder<T>` as a box-like containing the two objects;
+/// the box-like object inherits `Send` from the two inner values, similarly
+/// to how `Box<T>` is `Send` if `T` is `Send`.
+unsafe impl<T: Remotable> Send for Binder<T> {}
+
+impl<T: Remotable> Binder<T> {
+ /// Create a new Binder remotable object.
+ ///
+ /// This moves the `rust_object` into an owned [`Box`] and Binder will
+ /// manage its lifetime.
+ pub fn new(rust_object: T) -> Binder<T> {
+ let class = T::get_class();
+ let rust_object = Box::into_raw(Box::new(rust_object));
+ let ibinder = unsafe {
+ // Safety: `AIBinder_new` expects a valid class pointer (which we
+ // initialize via `get_class`), and an arbitrary pointer
+ // argument. The caller owns the returned `AIBinder` pointer, which
+ // is a strong reference to a `BBinder`. This reference should be
+ // decremented via `AIBinder_decStrong` when the reference lifetime
+ // ends.
+ sys::AIBinder_new(class.into(), rust_object as *mut c_void)
+ };
+ Binder {
+ ibinder,
+ rust_object,
+ }
+ }
+
+ /// Set the extension of a binder interface. This allows a downstream
+ /// developer to add an extension to an interface without modifying its
+ /// interface file. This should be called immediately when the object is
+ /// created before it is passed to another thread.
+ ///
+ /// # Examples
+ ///
+ /// For instance, imagine if we have this Binder AIDL interface definition:
+ /// interface IFoo { void doFoo(); }
+ ///
+ /// If an unrelated owner (perhaps in a downstream codebase) wants to make a
+ /// change to the interface, they have two options:
+ ///
+ /// 1) Historical option that has proven to be BAD! Only the original
+ /// author of an interface should change an interface. If someone
+ /// downstream wants additional functionality, they should not ever
+ /// change the interface or use this method.
+ /// ```AIDL
+ /// BAD TO DO: interface IFoo { BAD TO DO
+ /// BAD TO DO: void doFoo(); BAD TO DO
+ /// BAD TO DO: + void doBar(); // adding a method BAD TO DO
+ /// BAD TO DO: } BAD TO DO
+ /// ```
+ ///
+ /// 2) Option that this method enables!
+ /// Leave the original interface unchanged (do not change IFoo!).
+ /// Instead, create a new AIDL interface in a downstream package:
+ /// ```AIDL
+ /// package com.<name>; // new functionality in a new package
+ /// interface IBar { void doBar(); }
+ /// ```
+ ///
+ /// When registering the interface, add:
+ ///
+ /// # use binder::{Binder, Interface};
+ /// # type MyFoo = ();
+ /// # type MyBar = ();
+ /// # let my_foo = ();
+ /// # let my_bar = ();
+ /// let mut foo: Binder<MyFoo> = Binder::new(my_foo); // class in AOSP codebase
+ /// let bar: Binder<MyBar> = Binder::new(my_bar); // custom extension class
+ /// foo.set_extension(&mut bar.as_binder()); // use method in Binder
+ ///
+ /// Then, clients of `IFoo` can get this extension:
+ ///
+ /// # use binder::{declare_binder_interface, Binder, TransactionCode, Parcel};
+ /// # trait IBar {}
+ /// # declare_binder_interface! {
+ /// # IBar["test"] {
+ /// # native: BnBar(on_transact),
+ /// # proxy: BpBar,
+ /// # }
+ /// # }
+ /// # fn on_transact(
+ /// # service: &dyn IBar,
+ /// # code: TransactionCode,
+ /// # data: &Parcel,
+ /// # reply: &mut Parcel,
+ /// # ) -> binder::Result<()> {
+ /// # Ok(())
+ /// # }
+ /// # impl IBar for BpBar {}
+ /// # impl IBar for Binder<BnBar> {}
+ /// # fn main() -> binder::Result<()> {
+ /// # let binder = Binder::new(());
+ /// if let Some(barBinder) = binder.get_extension()? {
+ /// let bar = BpBar::new(barBinder)
+ /// .expect("Extension was not of type IBar");
+ /// } else {
+ /// // There was no extension
+ /// }
+ /// # }
+ pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> {
+ let status = unsafe {
+ // Safety: `AIBinder_setExtension` expects two valid, mutable
+ // `AIBinder` pointers. We are guaranteed that both `self` and
+ // `extension` contain valid `AIBinder` pointers, because they
+ // cannot be initialized without a valid
+ // pointer. `AIBinder_setExtension` does not take ownership of
+ // either parameter.
+ sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut())
+ };
+ status_result(status)
+ }
+
+ /// Retrieve the interface descriptor string for this object's Binder
+ /// interface.
+ pub fn get_descriptor() -> &'static str {
+ T::get_descriptor()
+ }
+}
+
+impl<T: Remotable> Interface for Binder<T> {
+ /// Converts the local remotable object into a generic `SpIBinder`
+ /// reference.
+ ///
+ /// The resulting `SpIBinder` will hold its own strong reference to this
+ /// remotable object, which will prevent the object from being dropped while
+ /// the `SpIBinder` is alive.
+ fn as_binder(&self) -> SpIBinder {
+ unsafe {
+ // Safety: `self.ibinder` is guaranteed to always be a valid pointer
+ // to an `AIBinder` by the `Binder` constructor. We are creating a
+ // copy of the `self.ibinder` strong reference, but
+ // `SpIBinder::from_raw` assumes it receives an owned pointer with
+ // its own strong reference. We first increment the reference count,
+ // so that the new `SpIBinder` will be tracked as a new reference.
+ sys::AIBinder_incStrong(self.ibinder);
+ SpIBinder::from_raw(self.ibinder).unwrap()
+ }
+ }
+}
+
+impl<T: Remotable> InterfaceClassMethods for Binder<T> {
+ fn get_descriptor() -> &'static str {
+ <T as Remotable>::get_descriptor()
+ }
+
+ /// Called whenever a transaction needs to be processed by a local
+ /// implementation.
+ ///
+ /// # Safety
+ ///
+ /// Must be called with a non-null, valid pointer to a local `AIBinder` that
+ /// contains a `T` pointer in its user data. The `data` and `reply` parcel
+ /// parameters must be valid pointers to `AParcel` objects. This method does
+ /// not take ownership of any of its parameters.
+ ///
+ /// These conditions hold when invoked by `ABBinder::onTransact`.
+ unsafe extern "C" fn on_transact(
+ binder: *mut sys::AIBinder,
+ code: u32,
+ data: *const sys::AParcel,
+ reply: *mut sys::AParcel,
+ ) -> status_t {
+ let res = {
+ let mut reply = Parcel::borrowed(reply).unwrap();
+ let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap();
+ let object = sys::AIBinder_getUserData(binder);
+ let binder: &T = &*(object as *const T);
+ binder.on_transact(code, &data, &mut reply)
+ };
+ match res {
+ Ok(()) => 0i32,
+ Err(e) => e as i32,
+ }
+ }
+
+ /// Called whenever an `AIBinder` object is no longer referenced and needs
+ /// destroyed.
+ ///
+ /// # Safety
+ ///
+ /// 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) {
+ ptr::drop_in_place(object as *mut T)
+ }
+
+ /// Called whenever a new, local `AIBinder` object is needed of a specific
+ /// class.
+ ///
+ /// Constructs the user data pointer that will be stored in the object,
+ /// which will be a heap-allocated `T` object.
+ ///
+ /// # Safety
+ ///
+ /// Must be called with a valid pointer to a `T` object allocated via `Box`.
+ unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void {
+ // We just return the argument, as it is already a pointer to the rust
+ // object created by Box.
+ args
+ }
+}
+
+impl<T: Remotable> Drop for Binder<T> {
+ // This causes C++ to decrease the strong ref count of the `AIBinder`
+ // object. We specifically do not drop the `rust_object` here. When C++
+ // actually destroys the object, it calls `on_destroy` and we can drop the
+ // `rust_object` then.
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: When `self` is dropped, we can no longer access the
+ // reference, so can decrement the reference count. `self.ibinder`
+ // is always a valid `AIBinder` pointer, so is valid to pass to
+ // `AIBinder_decStrong`.
+ sys::AIBinder_decStrong(self.ibinder);
+ }
+ }
+}
+
+impl<T: Remotable> Deref for Binder<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ unsafe {
+ // Safety: While `self` is alive, the reference count of the
+ // underlying object is > 0 and therefore `on_destroy` cannot be
+ // called. Therefore while `self` is alive, we know that
+ // `rust_object` is still a valid pointer to a heap allocated object
+ // of type `T`.
+ &*self.rust_object
+ }
+ }
+}
+
+impl<B: Remotable> Serialize for Binder<B> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ parcel.write_binder(Some(&self.as_binder()))
+ }
+}
+
+// This implementation is an idiomatic implementation of the C++
+// `IBinder::localBinder` interface if the binder object is a Rust binder
+// service.
+impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> {
+ type Error = StatusCode;
+
+ fn try_from(mut ibinder: SpIBinder) -> Result<Self> {
+ let class = B::get_class();
+ if Some(class) != ibinder.get_class() {
+ return Err(StatusCode::BAD_TYPE);
+ }
+ let userdata = unsafe {
+ // Safety: `SpIBinder` always holds a valid pointer pointer to an
+ // `AIBinder`, which we can safely pass to
+ // `AIBinder_getUserData`. `ibinder` retains ownership of the
+ // returned pointer.
+ sys::AIBinder_getUserData(ibinder.as_native_mut())
+ };
+ if userdata.is_null() {
+ return Err(StatusCode::UNEXPECTED_NULL);
+ }
+ // 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,
+ })
+ }
+}
+
+/// # Safety
+///
+/// The constructor for `Binder` guarantees that `self.ibinder` will contain a
+/// valid, non-null pointer to an `AIBinder`, so this implementation is type
+/// safe. `self.ibinder` will remain valid for the entire lifetime of `self`
+/// because we hold a strong reference to the `AIBinder` until `self` is
+/// dropped.
+unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> {
+ fn as_native(&self) -> *const sys::AIBinder {
+ self.ibinder
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+ self.ibinder
+ }
+}
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ let status = unsafe {
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both
+ // pointers. `AServiceManager_addService` creates a new strong reference
+ // and copies the string, so both pointers need only be valid until the
+ // call returns.
+ sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr())
+ };
+ status_result(status)
+}
+
+/// Tests often create a base BBinder instance; so allowing the unit
+/// type to be remotable translates nicely to Binder::new(()).
+impl Remotable for () {
+ fn get_descriptor() -> &'static str {
+ ""
+ }
+
+ fn on_transact(
+ &self,
+ _code: TransactionCode,
+ _data: &Parcel,
+ _reply: &mut Parcel,
+ ) -> Result<()> {
+ Ok(())
+ }
+
+ binder_fn_get_class!(Binder::<Self>);
+}
+
+impl Interface for () {}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
new file mode 100644
index 0000000..a248f5c
--- /dev/null
+++ b/libs/binder/rust/src/parcel.rs
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Container for messages that are sent via binder.
+
+use crate::binder::AsNative;
+use crate::error::{status_result, Result, StatusCode};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::cell::RefCell;
+use std::convert::TryInto;
+use std::mem::ManuallyDrop;
+use std::ptr;
+
+mod file_descriptor;
+mod parcelable;
+
+pub use self::file_descriptor::ParcelFileDescriptor;
+pub use self::parcelable::{
+ Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
+};
+
+/// Container for a message (data and object references) that can be sent
+/// through Binder.
+///
+/// A Parcel can contain both serialized data that will be deserialized on the
+/// other side of the IPC, and references to live Binder objects that will
+/// result in the other side receiving a proxy Binder connected with the
+/// original Binder in the Parcel.
+pub enum Parcel {
+ /// Owned parcel pointer
+ Owned(*mut sys::AParcel),
+ /// Borrowed parcel pointer (will not be destroyed on drop)
+ Borrowed(*mut sys::AParcel),
+}
+
+/// # Safety
+///
+/// The `Parcel` constructors guarantee that a `Parcel` object will always
+/// contain a valid pointer to an `AParcel`.
+unsafe impl AsNative<sys::AParcel> for Parcel {
+ fn as_native(&self) -> *const sys::AParcel {
+ match *self {
+ Self::Owned(x) | Self::Borrowed(x) => x,
+ }
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AParcel {
+ match *self {
+ Self::Owned(x) | Self::Borrowed(x) => x,
+ }
+ }
+}
+
+impl Parcel {
+ /// Create a borrowed reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object.
+ pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
+ ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
+ }
+
+ /// Create an owned reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object. The
+ /// parcel object must be owned by the caller prior to this call, as this
+ /// constructor takes ownership of the parcel and will destroy it on drop.
+ pub(crate) unsafe fn owned(ptr: *mut sys::AParcel) -> Option<Parcel> {
+ ptr.as_mut().map(|ptr| Self::Owned(ptr))
+ }
+
+ /// Consume the parcel, transferring ownership to the caller if the parcel
+ /// was owned.
+ pub(crate) fn into_raw(mut self) -> *mut sys::AParcel {
+ let ptr = self.as_native_mut();
+ let _ = ManuallyDrop::new(self);
+ ptr
+ }
+}
+
+// Data serialization methods
+impl Parcel {
+ /// Write a type that implements [`Serialize`] to the `Parcel`.
+ pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+ parcelable.serialize(self)
+ }
+
+ /// Writes the length of a slice to the `Parcel`.
+ ///
+ /// This is used in AIDL-generated client side code to indicate the
+ /// allocated space for an output array parameter.
+ pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> {
+ if let Some(slice) = slice {
+ let len: i32 = slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?;
+ self.write(&len)
+ } else {
+ self.write(&-1i32)
+ }
+ }
+
+ /// Perform a series of writes to the `Parcel`, prepended with the length
+ /// (in bytes) of the written data.
+ ///
+ /// The length `0i32` will be written to the parcel first, followed by the
+ /// writes performed by the callback. The initial length will then be
+ /// updated to the length of all data written by the callback, plus the
+ /// size of the length elemement itself (4 bytes).
+ ///
+ /// # Examples
+ ///
+ /// After the following call:
+ ///
+ /// ```
+ /// # use binder::{Binder, Interface, Parcel};
+ /// # let mut parcel = Parcel::Owned(std::ptr::null_mut());
+ /// parcel.sized_write(|subparcel| {
+ /// subparcel.write(&1u32)?;
+ /// subparcel.write(&2u32)?;
+ /// subparcel.write(&3u32)
+ /// });
+ /// ```
+ ///
+ /// `parcel` will contain the following:
+ ///
+ /// ```ignore
+ /// [16i32, 1u32, 2u32, 3u32]
+ /// ```
+ pub fn sized_write<F>(&mut self, f: F) -> Result<()>
+ where for<'a>
+ F: Fn(&'a WritableSubParcel<'a>) -> Result<()>
+ {
+ let start = self.get_data_position();
+ self.write(&0i32)?;
+ {
+ let subparcel = WritableSubParcel(RefCell::new(self));
+ f(&subparcel)?;
+ }
+ let end = self.get_data_position();
+ unsafe {
+ self.set_data_position(start)?;
+ }
+ assert!(end >= start);
+ self.write(&(end - start))?;
+ unsafe {
+ self.set_data_position(end)?;
+ }
+ Ok(())
+ }
+
+ /// Returns the current position in the parcel data.
+ pub fn get_data_position(&self) -> i32 {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
+ // and this call is otherwise safe.
+ sys::AParcel_getDataPosition(self.as_native())
+ }
+ }
+
+ /// Move the current read/write position in the parcel.
+ ///
+ /// The new position must be a position previously returned by
+ /// `self.get_data_position()`.
+ ///
+ /// # Safety
+ ///
+ /// This method is safe if `pos` is less than the current size of the parcel
+ /// data buffer. Otherwise, we are relying on correct bounds checking in the
+ /// Parcel C++ code on every subsequent read or write to this parcel. If all
+ /// accesses are bounds checked, this call is still safe, but we can't rely
+ /// on that.
+ pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
+ status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
+ }
+}
+
+/// A segment of a writable parcel, used for [`Parcel::sized_write`].
+pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>);
+
+impl<'a> WritableSubParcel<'a> {
+ /// Write a type that implements [`Serialize`] to the sub-parcel.
+ pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> {
+ parcelable.serialize(&mut *self.0.borrow_mut())
+ }
+}
+
+// Data deserialization methods
+impl Parcel {
+ /// Attempt to read a type that implements [`Deserialize`] from this
+ /// `Parcel`.
+ pub fn read<D: Deserialize>(&self) -> Result<D> {
+ D::deserialize(self)
+ }
+
+ /// Read a vector size from the `Parcel` and resize the given output vector
+ /// to be correctly sized for that amount of data.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> {
+ let len: i32 = self.read()?;
+
+ if len < 0 {
+ return Err(StatusCode::UNEXPECTED_NULL);
+ }
+
+ // usize in Rust may be 16-bit, so i32 may not fit
+ let len = len.try_into().unwrap();
+ out_vec.resize_with(len, Default::default);
+
+ Ok(())
+ }
+
+ /// Read a vector size from the `Parcel` and either create a correctly sized
+ /// vector for that amount of data or set the output parameter to None if
+ /// the vector should be null.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_nullable_out_vec<D: Default + Deserialize>(
+ &self,
+ out_vec: &mut Option<Vec<D>>,
+ ) -> Result<()> {
+ let len: i32 = self.read()?;
+
+ if len < 0 {
+ *out_vec = None;
+ } else {
+ // usize in Rust may be 16-bit, so i32 may not fit
+ let len = len.try_into().unwrap();
+ let mut vec = Vec::with_capacity(len);
+ vec.resize_with(len, Default::default);
+ *out_vec = Some(vec);
+ }
+
+ Ok(())
+ }
+}
+
+// Internal APIs
+impl Parcel {
+ pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
+ // null or a valid pointer to an `AIBinder`, both of which are
+ // valid, safe inputs to `AParcel_writeStrongBinder`.
+ //
+ // This call does not take ownership of the binder. However, it does
+ // require a mutable pointer, which we cannot extract from an
+ // immutable reference, so we clone the binder, incrementing the
+ // refcount before the call. The refcount will be immediately
+ // decremented when this temporary is dropped.
+ status_result(sys::AParcel_writeStrongBinder(
+ self.as_native_mut(),
+ binder.cloned().as_native_mut(),
+ ))
+ }
+ }
+
+ pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
+ let mut binder = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid, mutable out pointer to the `binder`
+ // parameter. After this call, `binder` will be either null or a
+ // valid pointer to an `AIBinder` owned by the caller.
+ sys::AParcel_readStrongBinder(self.as_native(), &mut binder)
+ };
+
+ status_result(status)?;
+
+ Ok(unsafe {
+ // Safety: `binder` is either null or a valid, owned pointer at this
+ // point, so can be safely passed to `SpIBinder::from_raw`.
+ SpIBinder::from_raw(binder)
+ })
+ }
+}
+
+impl Drop for Parcel {
+ fn drop(&mut self) {
+ // Run the C++ Parcel complete object destructor
+ if let Self::Owned(ptr) = *self {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If we own the parcel, we can safely delete it
+ // here.
+ sys::AParcel_delete(ptr)
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+impl Parcel {
+ /// Create a new parcel tied to a bogus binder. TESTING ONLY!
+ ///
+ /// This can only be used for testing! All real parcel operations must be
+ /// done in the callback to [`IBinder::transact`] or in
+ /// [`Remotable::on_transact`] using the parcels provided to these methods.
+ pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> {
+ let mut input = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `binder` always contains a
+ // valid pointer to an `AIBinder`. We pass a valid, mutable out
+ // pointer to receive a newly constructed parcel. When successful
+ // this function assigns a new pointer to an `AParcel` to `input`
+ // and transfers ownership of this pointer to the caller. Thus,
+ // after this call, `input` will either be null or point to a valid,
+ // owned `AParcel`.
+ sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input)
+ };
+ status_result(status)?;
+ unsafe {
+ // Safety: `input` is either null or a valid, owned pointer to an
+ // `AParcel`, so is valid to safe to
+ // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel
+ // pointer.
+ Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+ }
+}
+
+#[test]
+fn test_read_write() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+ use std::ffi::CString;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<i8>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<u16>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<u32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<i64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<u64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<f32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<f64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<Option<CString>>(), Ok(None));
+ assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL));
+
+ assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE));
+
+ parcel.write(&1i32).unwrap();
+
+ unsafe {
+ parcel.set_data_position(start).unwrap();
+ }
+
+ let i: i32 = parcel.read().unwrap();
+ assert_eq!(i, 1i32);
+}
+
+#[test]
+#[allow(clippy::float_cmp)]
+fn test_read_data() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let str_start = parcel.get_data_position();
+
+ parcel.write(&b"Hello, Binder!\0"[..]).unwrap();
+ // Skip over string length
+ unsafe {
+ assert!(parcel.set_data_position(str_start).is_ok());
+ }
+ assert_eq!(parcel.read::<i32>().unwrap(), 15);
+ let start = parcel.get_data_position();
+
+ assert_eq!(parcel.read::<bool>().unwrap(), true);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<i8>().unwrap(), 72i8);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u16>().unwrap(), 25928);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<i32>().unwrap(), 1819043144);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 1819043144);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<i64>().unwrap(), 4764857262830019912);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u64>().unwrap(), 4764857262830019912);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(
+ parcel.read::<f32>().unwrap(),
+ 1143139100000000000000000000.0
+ );
+ assert_eq!(parcel.read::<f32>().unwrap(), 40.043392);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<f64>().unwrap(), 34732488246.197815);
+
+ // Skip back to before the string length
+ unsafe {
+ assert!(parcel.set_data_position(str_start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<Vec<u8>>().unwrap(), b"Hello, Binder!\0");
+}
+
+#[test]
+fn test_utf8_utf16_conversions() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(parcel.write("Hello, Binder!").is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert_eq!(
+ parcel.read::<Option<String>>().unwrap().unwrap(),
+ "Hello, Binder!"
+ );
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok());
+ assert!(parcel
+ .write(
+ &[
+ String::from("str4"),
+ String::from("str5"),
+ String::from("str6"),
+ ][..]
+ )
+ .is_ok());
+
+ let s1 = "Hello, Binder!";
+ let s2 = "This is a utf8 string.";
+ let s3 = "Some more text here.";
+
+ assert!(parcel.write(&[s1, s2, s3][..]).is_ok());
+ unsafe {
+ 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(), [s1, s2, s3]);
+}
+
+#[test]
+fn test_sized_write() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ let arr = [1i32, 2i32, 3i32];
+
+ 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;
+
+ assert_eq!(parcel.get_data_position(), start + expected_len);
+
+ unsafe {
+ parcel.set_data_position(start).unwrap();
+ }
+
+ assert_eq!(
+ expected_len,
+ parcel.read().unwrap(),
+ );
+
+ assert_eq!(
+ parcel.read::<Vec<i32>>().unwrap(),
+ &arr,
+ );
+}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
new file mode 100644
index 0000000..8a89ab0
--- /dev/null
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use super::{
+ Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ SerializeOption,
+};
+use crate::binder::AsNative;
+use crate::error::{status_result, Result, StatusCode};
+use crate::sys;
+
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd};
+
+/// Rust version of the Java class android.os.ParcelFileDescriptor
+pub struct ParcelFileDescriptor(File);
+
+impl ParcelFileDescriptor {
+ /// Create a new `ParcelFileDescriptor`
+ pub fn new(file: File) -> Self {
+ Self(file)
+ }
+}
+
+impl AsRef<File> for ParcelFileDescriptor {
+ fn as_ref(&self) -> &File {
+ &self.0
+ }
+}
+
+impl From<ParcelFileDescriptor> for File {
+ fn from(file: ParcelFileDescriptor) -> File {
+ file.0
+ }
+}
+
+impl Serialize for ParcelFileDescriptor {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ let fd = self.0.as_raw_fd();
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
+ // valid file, so we can borrow a valid file
+ // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
+ // ownership of the fd, so we need not duplicate it first.
+ sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd)
+ };
+ status_result(status)
+ }
+}
+
+impl SerializeArray for ParcelFileDescriptor {}
+
+impl SerializeOption for ParcelFileDescriptor {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ if let Some(f) = this {
+ f.serialize(parcel)
+ } else {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
+ // value `-1` as the file descriptor to signify serializing a
+ // null file descriptor.
+ sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32)
+ };
+ status_result(status)
+ }
+ }
+}
+
+impl SerializeArray for Option<ParcelFileDescriptor> {}
+
+impl DeserializeOption for ParcelFileDescriptor {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let mut fd = -1i32;
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid mutable pointer to an i32, which
+ // `AParcel_readParcelFileDescriptor` assigns the valid file
+ // descriptor into, or `-1` if deserializing a null file
+ // descriptor. The read function passes ownership of the file
+ // descriptor to its caller if it was non-null, so we must take
+ // ownership of the file and ensure that it is eventually closed.
+ status_result(sys::AParcel_readParcelFileDescriptor(
+ parcel.as_native(),
+ &mut fd,
+ ))?;
+ }
+ if fd < 0 {
+ Ok(None)
+ } else {
+ let file = unsafe {
+ // Safety: At this point, we know that the file descriptor was
+ // not -1, so must be a valid, owned file descriptor which we
+ // can safely turn into a `File`.
+ File::from_raw_fd(fd)
+ };
+ Ok(Some(ParcelFileDescriptor::new(file)))
+ }
+ }
+}
+
+impl DeserializeArray for Option<ParcelFileDescriptor> {}
+
+impl Deserialize for ParcelFileDescriptor {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Deserialize::deserialize(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeArray for ParcelFileDescriptor {}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
new file mode 100644
index 0000000..78b3d2c
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::{AsNative, FromIBinder};
+use crate::error::{status_result, Result, Status, StatusCode};
+use crate::parcel::Parcel;
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryInto;
+use std::ffi::{c_void, CStr, CString};
+use std::ptr;
+
+/// A struct whose instances can be written to a [`Parcel`].
+// Might be able to hook this up as a serde backend in the future?
+pub trait Serialize {
+ /// Serialize this instance into the given [`Parcel`].
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()>;
+}
+
+/// A struct whose instances can be restored from a [`Parcel`].
+// Might be able to hook this up as a serde backend in the future?
+pub trait Deserialize: Sized {
+ /// Deserialize an instance from the given [`Parcel`].
+ fn deserialize(parcel: &Parcel) -> Result<Self>;
+}
+
+/// Helper trait for types that can be serialized as arrays.
+/// Defaults to calling Serialize::serialize() manually for every element,
+/// but can be overridden for custom implementations like `writeByteArray`.
+// Until specialization is stabilized in Rust, we need this to be a separate
+// trait because it's the only way to have a default implementation for a method.
+// We want the default implementation for most types, but an override for
+// a few special ones like `readByteArray` for `u8`.
+pub trait SerializeArray: Serialize + Sized {
+ /// Serialize an array of this type into the given [`Parcel`].
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ parcel.write_slice_size(Some(slice))?;
+
+ for item in slice {
+ parcel.write(item)?;
+ }
+
+ Ok(())
+ }
+}
+
+/// Helper trait for types that can be deserialized as arrays.
+/// Defaults to calling Deserialize::deserialize() manually for every element,
+/// but can be overridden for custom implementations like `readByteArray`.
+pub trait DeserializeArray: Deserialize {
+ /// Deserialize an array of type from the given [`Parcel`].
+ fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ let len: i32 = parcel.read()?;
+ if len < 0 {
+ return Ok(None);
+ }
+
+ // TODO: Assumes that usize is at least 32 bits
+ let mut vec = Vec::with_capacity(len as usize);
+
+ for _ in 0..len {
+ vec.push(parcel.read()?);
+ }
+
+ Ok(Some(vec))
+ }
+}
+
+/// Helper trait for types that can be nullable when serialized.
+// We really need this trait instead of implementing `Serialize for Option<T>`
+// because of the Rust orphan rule which prevents us from doing
+// `impl Serialize for Option<&dyn IFoo>` for AIDL interfaces.
+// Instead we emit `impl SerializeOption for dyn IFoo` which is allowed.
+// We also use it to provide a default implementation for AIDL-generated
+// parcelables.
+pub trait SerializeOption: Serialize {
+ /// Serialize an Option of this type into the given [`Parcel`].
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ if let Some(inner) = this {
+ parcel.write(&1i32)?;
+ parcel.write(inner)
+ } else {
+ parcel.write(&0i32)
+ }
+ }
+}
+
+/// Helper trait for types that can be nullable when deserialized.
+pub trait DeserializeOption: Deserialize {
+ /// Deserialize an Option of this type from the given [`Parcel`].
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let null: i32 = parcel.read()?;
+ if null == 0 {
+ Ok(None)
+ } else {
+ parcel.read().map(Some)
+ }
+ }
+}
+
+/// Callback to allocate a vector for parcel array read functions.
+///
+/// # Safety
+///
+/// The opaque data pointer passed to the array read function must be a mutable
+/// pointer to an `Option<Vec<T>>`. `buffer` will be assigned a mutable pointer
+/// to the allocated vector data if this function returns true.
+unsafe extern "C" fn allocate_vec<T: Clone + Default>(
+ data: *mut c_void,
+ len: i32,
+ buffer: *mut *mut T,
+) -> bool {
+ let vec = &mut *(data as *mut Option<Vec<T>>);
+ if len < 0 {
+ *vec = None;
+ return true;
+ }
+ let mut new_vec: Vec<T> = Vec::with_capacity(len as usize);
+ new_vec.resize_with(len as usize, Default::default);
+ *buffer = new_vec.as_mut_ptr();
+ *vec = Some(new_vec);
+ true
+}
+
+macro_rules! parcelable_primitives {
+ {
+ $(
+ impl $trait:ident for $ty:ty = $fn:path;
+ )*
+ } => {
+ $(impl_parcelable!{$trait, $ty, $fn})*
+ };
+}
+
+macro_rules! impl_parcelable {
+ {Serialize, $ty:ty, $write_fn:path} => {
+ impl Serialize for $ty {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`, and any `$ty` literal value is safe to pass to
+ // `$write_fn`.
+ status_result($write_fn(parcel.as_native_mut(), *self))
+ }
+ }
+ }
+ };
+
+ {Deserialize, $ty:ty, $read_fn:path} => {
+ impl Deserialize for $ty {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let mut val = Self::default();
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid, mutable pointer to `val`, a
+ // literal of type `$ty`, and `$read_fn` will write the
+ // value read into `val` if successful
+ status_result($read_fn(parcel.as_native(), &mut val))?
+ };
+ Ok(val)
+ }
+ }
+ };
+
+ {SerializeArray, $ty:ty, $write_array_fn:path} => {
+ impl SerializeArray for $ty {
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
+ // will be a valid pointer to an array of elements of type
+ // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
+ // dangling, but this is safe since the pointer is not
+ // dereferenced if the length parameter is 0.
+ $write_array_fn(
+ parcel.as_native_mut(),
+ slice.as_ptr(),
+ slice
+ .len()
+ .try_into()
+ .or(Err(StatusCode::BAD_VALUE))?,
+ )
+ };
+ status_result(status)
+ }
+ }
+ };
+
+ {DeserializeArray, $ty:ty, $read_array_fn:path} => {
+ impl DeserializeArray for $ty {
+ fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ let mut vec: Option<Vec<Self>> = None;
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
+ // be of type `*mut Option<Vec<T>>`, so `&mut vec` is
+ // correct for it.
+ $read_array_fn(
+ parcel.as_native(),
+ &mut vec as *mut _ as *mut c_void,
+ Some(allocate_vec),
+ )
+ };
+ status_result(status)?;
+ Ok(vec)
+ }
+ }
+ };
+}
+
+parcelable_primitives! {
+ impl Serialize for bool = sys::AParcel_writeBool;
+ impl Deserialize for bool = sys::AParcel_readBool;
+
+ // This is only safe because `Option<Vec<u8>>` is interchangeable with
+ // `Option<Vec<i8>>` (what the allocator function actually allocates.
+ impl DeserializeArray for u8 = sys::AParcel_readByteArray;
+
+ impl Serialize for i8 = sys::AParcel_writeByte;
+ impl Deserialize for i8 = sys::AParcel_readByte;
+ impl SerializeArray for i8 = sys::AParcel_writeByteArray;
+ impl DeserializeArray for i8 = sys::AParcel_readByteArray;
+
+ impl Serialize for u16 = sys::AParcel_writeChar;
+ impl Deserialize for u16 = sys::AParcel_readChar;
+ impl SerializeArray for u16 = sys::AParcel_writeCharArray;
+ impl DeserializeArray for u16 = sys::AParcel_readCharArray;
+
+ // This is only safe because `Option<Vec<i16>>` is interchangeable with
+ // `Option<Vec<u16>>` (what the allocator function actually allocates.
+ impl DeserializeArray for i16 = sys::AParcel_readCharArray;
+
+ impl Serialize for u32 = sys::AParcel_writeUint32;
+ impl Deserialize for u32 = sys::AParcel_readUint32;
+ impl SerializeArray for u32 = sys::AParcel_writeUint32Array;
+ impl DeserializeArray for u32 = sys::AParcel_readUint32Array;
+
+ impl Serialize for i32 = sys::AParcel_writeInt32;
+ impl Deserialize for i32 = sys::AParcel_readInt32;
+ impl SerializeArray for i32 = sys::AParcel_writeInt32Array;
+ impl DeserializeArray for i32 = sys::AParcel_readInt32Array;
+
+ impl Serialize for u64 = sys::AParcel_writeUint64;
+ impl Deserialize for u64 = sys::AParcel_readUint64;
+ impl SerializeArray for u64 = sys::AParcel_writeUint64Array;
+ impl DeserializeArray for u64 = sys::AParcel_readUint64Array;
+
+ impl Serialize for i64 = sys::AParcel_writeInt64;
+ impl Deserialize for i64 = sys::AParcel_readInt64;
+ impl SerializeArray for i64 = sys::AParcel_writeInt64Array;
+ impl DeserializeArray for i64 = sys::AParcel_readInt64Array;
+
+ impl Serialize for f32 = sys::AParcel_writeFloat;
+ impl Deserialize for f32 = sys::AParcel_readFloat;
+ impl SerializeArray for f32 = sys::AParcel_writeFloatArray;
+ impl DeserializeArray for f32 = sys::AParcel_readFloatArray;
+
+ impl Serialize for f64 = sys::AParcel_writeDouble;
+ impl Deserialize for f64 = sys::AParcel_readDouble;
+ impl SerializeArray for f64 = sys::AParcel_writeDoubleArray;
+ impl DeserializeArray for f64 = sys::AParcel_readDoubleArray;
+}
+
+impl SerializeArray for bool {}
+impl DeserializeArray for bool {}
+
+impl Serialize for u8 {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ (*self as i8).serialize(parcel)
+ }
+}
+
+impl Deserialize for u8 {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ i8::deserialize(parcel).map(|v| v as u8)
+ }
+}
+
+impl SerializeArray for u8 {
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+ // valid pointer to an array of elements of type `$ty`. If the slice
+ // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+ // since the pointer is not dereferenced if the length parameter is
+ // 0.
+ sys::AParcel_writeByteArray(
+ parcel.as_native_mut(),
+ slice.as_ptr() as *const i8,
+ slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+ )
+ };
+ status_result(status)
+ }
+}
+
+impl Serialize for i16 {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ (*self as u16).serialize(parcel)
+ }
+}
+
+impl Deserialize for i16 {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ u16::deserialize(parcel).map(|v| v as i16)
+ }
+}
+
+impl SerializeArray for i16 {
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+ // valid pointer to an array of elements of type `$ty`. If the slice
+ // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+ // since the pointer is not dereferenced if the length parameter is
+ // 0.
+ sys::AParcel_writeCharArray(
+ parcel.as_native_mut(),
+ slice.as_ptr() as *const u16,
+ slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+ )
+ };
+ status_result(status)
+ }
+}
+
+impl SerializeOption for CStr {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ match this {
+ None => unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the string pointer is null,
+ // `AParcel_writeString` requires that the length is -1 to
+ // indicate that we want to serialize a null string.
+ status_result(sys::AParcel_writeString(
+ parcel.as_native_mut(),
+ ptr::null(),
+ -1,
+ ))
+ },
+ Some(s) => unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AParcel_writeString` assumes that we pass a
+ // null-terminated C string pointer with no nulls in the middle
+ // of the string. Rust guarantees exactly that for a valid CStr
+ // instance.
+ status_result(sys::AParcel_writeString(
+ parcel.as_native_mut(),
+ s.as_ptr(),
+ s.to_bytes()
+ .len()
+ .try_into()
+ .or(Err(StatusCode::BAD_VALUE))?,
+ ))
+ },
+ }
+ }
+}
+
+impl SerializeArray for Option<&CStr> {}
+
+impl Serialize for CStr {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self).serialize(parcel)
+ }
+}
+
+impl Serialize for CString {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self.as_c_str()).serialize(parcel)
+ }
+}
+
+impl SerializeArray for CString {}
+
+impl SerializeOption for CString {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(CString::as_c_str), parcel)
+ }
+}
+
+impl SerializeArray for Option<CString> {}
+
+impl Serialize for String {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self.as_str()).serialize(parcel)
+ }
+}
+
+impl SerializeArray for String {}
+
+impl SerializeOption for String {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(String::as_str), parcel)
+ }
+}
+
+impl SerializeArray for Option<String> {}
+
+impl Deserialize for Option<CString> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let mut vec: Option<Vec<u8>> = None;
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
+ // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
+ // for `allocate_vec`, so `vec` is safe to pass as the opaque data
+ // pointer on platforms where char is signed.
+ sys::AParcel_readString(
+ parcel.as_native(),
+ &mut vec as *mut _ as *mut c_void,
+ Some(allocate_vec),
+ )
+ };
+
+ status_result(status)?;
+ vec.map(|mut s| {
+ // The vector includes a null-terminator and CString::new requires
+ // no nulls, including terminating.
+ s.pop();
+ CString::new(s).or(Err(StatusCode::BAD_VALUE))
+ })
+ .transpose()
+ }
+}
+
+impl DeserializeArray for Option<CString> {}
+
+impl DeserializeOption for String {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let c_str = <Option<CString>>::deserialize(parcel)?;
+ c_str
+ .map(|s| s.into_string().or(Err(StatusCode::BAD_VALUE)))
+ .transpose()
+ }
+}
+
+impl DeserializeArray for Option<String> {}
+
+impl Deserialize for String {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Deserialize::deserialize(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeArray for String {}
+
+impl SerializeOption for str {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ match this {
+ None => parcel.write(&-1i32),
+ Some(s) => {
+ let c_str = CString::new(s).or(Err(StatusCode::BAD_VALUE))?;
+ parcel.write(&c_str)
+ }
+ }
+ }
+}
+
+impl Serialize for str {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self).serialize(parcel)
+ }
+}
+
+impl SerializeArray for &str {}
+
+impl SerializeArray for Option<&str> {}
+
+impl<T: SerializeArray> Serialize for [T] {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ SerializeArray::serialize_array(self, parcel)
+ }
+}
+
+impl<T: SerializeArray> Serialize for Vec<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ SerializeArray::serialize_array(&self[..], parcel)
+ }
+}
+
+impl<T: SerializeArray> SerializeOption for [T] {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ if let Some(v) = this {
+ SerializeArray::serialize_array(v, parcel)
+ } else {
+ parcel.write(&-1i32)
+ }
+ }
+}
+
+impl<T: SerializeArray> SerializeOption for Vec<T> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(Vec::as_slice), parcel)
+ }
+}
+
+impl<T: DeserializeArray> Deserialize for Vec<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ DeserializeArray::deserialize_array(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl<T: DeserializeArray> DeserializeOption for Vec<T> {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ DeserializeArray::deserialize_array(parcel)
+ }
+}
+
+impl Serialize for Status {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`
+ // and `Status` always contains a valid pointer to an `AStatus`, so
+ // both parameters are valid and safe. This call does not take
+ // ownership of either of its parameters.
+ status_result(sys::AParcel_writeStatusHeader(
+ parcel.as_native_mut(),
+ self.as_native(),
+ ))
+ }
+ }
+}
+
+impl Deserialize for Status {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let mut status_ptr = ptr::null_mut();
+ let ret_status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a mutable out pointer which will be
+ // assigned a valid `AStatus` pointer if the function returns
+ // status OK. This function passes ownership of the status
+ // pointer to the caller, if it was assigned.
+ sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr)
+ };
+ status_result(ret_status)?;
+ Ok(unsafe {
+ // Safety: At this point, the return status of the read call was ok,
+ // so we know that `status_ptr` is a valid, owned pointer to an
+ // `AStatus`, from which we can safely construct a `Status` object.
+ Status::from_ptr(status_ptr)
+ })
+ }
+}
+
+impl<T: Serialize + ?Sized> Serialize for Box<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Serialize::serialize(&**self, parcel)
+ }
+}
+
+impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(|b| &**b), parcel)
+ }
+}
+
+impl<T: FromIBinder + ?Sized> Deserialize for Box<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let ibinder: SpIBinder = parcel.read()?;
+ FromIBinder::try_from(ibinder)
+ }
+}
+
+impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let ibinder: Option<SpIBinder> = parcel.read()?;
+ ibinder.map(FromIBinder::try_from).transpose()
+ }
+}
+
+// We need these to support Option<&T> for all T
+impl<T: Serialize + ?Sized> Serialize for &T {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Serialize::serialize(*self, parcel)
+ }
+}
+
+impl<T: SerializeOption + ?Sized> SerializeOption for &T {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.copied(), parcel)
+ }
+}
+
+impl<T: SerializeOption> Serialize for Option<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(self.as_ref(), parcel)
+ }
+}
+
+impl<T: DeserializeOption> Deserialize for Option<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ DeserializeOption::deserialize_option(parcel)
+ }
+}
+
+#[test]
+fn test_custom_parcelable() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+ let mut service = Binder::new(()).as_binder();
+
+ struct Custom(u32, bool, String, Vec<String>);
+
+ impl Serialize for Custom {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ self.0.serialize(parcel)?;
+ self.1.serialize(parcel)?;
+ self.2.serialize(parcel)?;
+ self.3.serialize(parcel)
+ }
+ }
+
+ impl Deserialize for Custom {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Ok(Custom(
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read::<Option<Vec<String>>>()?.unwrap(),
+ ))
+ }
+ }
+
+ let string8 = "Custom Parcelable".to_string();
+
+ let s1 = "str1".to_string();
+ let s2 = "str2".to_string();
+ let s3 = "str3".to_string();
+
+ let strs = vec![s1, s2, s3];
+
+ let custom = Custom(123_456_789, true, string8, strs);
+
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(custom.serialize(&mut parcel).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let custom2 = Custom::deserialize(&parcel).unwrap();
+
+ assert_eq!(custom2.0, 123_456_789);
+ assert!(custom2.1);
+ assert_eq!(custom2.2, custom.2);
+ assert_eq!(custom2.3, custom.3);
+}
+
+#[test]
+#[allow(clippy::excessive_precision)]
+fn test_slice_parcelables() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+ let mut service = Binder::new(()).as_binder();
+
+ let bools = [true, false, false, true];
+
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(bools.serialize(&mut parcel).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<bool>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [true, false, false, true]);
+
+ let u8s = [101u8, 255, 42, 117];
+
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(parcel.write(&u8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(&parcel).unwrap();
+ assert_eq!(vec, [101, 255, 42, 117]);
+
+ let i8s = [-128i8, 127, 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert!(parcel.write(&i8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(&parcel).unwrap();
+ assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
+
+ let u16s = [u16::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u16s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u16>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+
+ let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i16s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i16>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+
+ let u32s = [u32::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u32s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u32>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+
+ let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i32s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i32>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+
+ let u64s = [u64::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u64s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u64>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+
+ let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i64s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i64>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+
+ let f32s = [
+ std::f32::NAN,
+ std::f32::INFINITY,
+ 1.23456789,
+ std::f32::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f32s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f32>::deserialize(&parcel).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f32s[1..]);
+
+ let f64s = [
+ std::f64::NAN,
+ std::f64::INFINITY,
+ 1.234567890123456789,
+ std::f64::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f64s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f64>::deserialize(&parcel).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f64s[1..]);
+
+ let s1 = "Hello, Binder!";
+ let s2 = "This is a utf8 string.";
+ let s3 = "Some more text here.";
+
+ let strs = [s1, s2, s3];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(strs.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<String>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, strs);
+}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
new file mode 100644
index 0000000..13e5619
--- /dev/null
+++ b/libs/binder/rust/src/proxy.rs
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Rust API for interacting with a remote binder service.
+
+use crate::binder::{
+ AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags,
+};
+use crate::error::{status_result, Result, StatusCode};
+use crate::parcel::{
+ Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ SerializeOption,
+};
+use crate::sys;
+
+use std::convert::TryInto;
+use std::ffi::{c_void, CString};
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+
+/// A strong reference to a Binder remote object.
+///
+/// This struct encapsulates the generic C++ `sp<IBinder>` class. This wrapper
+/// is untyped; typed interface access is implemented by the AIDL compiler.
+pub struct SpIBinder(*mut sys::AIBinder);
+
+/// # Safety
+///
+/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe
+unsafe impl Send for SpIBinder {}
+
+impl SpIBinder {
+ /// Create an `SpIBinder` wrapper object from a raw `AIBinder` pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe iff `ptr` is a null pointer or a valid pointer
+ /// to an `AIBinder`.
+ ///
+ /// In the non-null case, this method conceptually takes ownership of a strong
+ /// reference to the object, so `AIBinder_incStrong` must have been called
+ /// on the pointer before passing it to this constructor. This is generally
+ /// done by Binder NDK methods that return an `AIBinder`, but care should be
+ /// taken to ensure this invariant.
+ ///
+ /// All `SpIBinder` objects that are constructed will hold a valid pointer
+ /// to an `AIBinder`, which will remain valid for the entire lifetime of the
+ /// `SpIBinder` (we keep a strong reference, and only decrement on drop).
+ pub(crate) unsafe fn from_raw(ptr: *mut sys::AIBinder) -> Option<Self> {
+ ptr.as_mut().map(|p| Self(p))
+ }
+
+ /// Return true if this binder object is hosted in a different process than
+ /// the current one.
+ pub fn is_remote(&self) -> bool {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer.
+ sys::AIBinder_isRemote(self.as_native())
+ }
+ }
+
+ /// Try to convert this Binder object into a trait object for the given
+ /// Binder interface.
+ ///
+ /// If this object does not implement the expected interface, the error
+ /// `StatusCode::BAD_TYPE` is returned.
+ pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> {
+ FromIBinder::try_from(self)
+ }
+
+ /// Return the interface class of this binder object, if associated with
+ /// one.
+ pub(crate) fn get_class(&mut self) -> Option<InterfaceClass> {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer. `AIBinder_getClass` returns either a null
+ // pointer or a valid pointer to an `AIBinder_Class`. After mapping
+ // null to None, we can safely construct an `InterfaceClass` if the
+ // pointer was non-null.
+ let class = sys::AIBinder_getClass(self.as_native_mut());
+ class.as_ref().map(|p| InterfaceClass::from_ptr(p))
+ }
+ }
+}
+
+/// An object that can be associate with an [`InterfaceClass`].
+pub trait AssociateClass {
+ /// Check if this object is a valid object for the given interface class
+ /// `I`.
+ ///
+ /// Returns `Some(self)` if this is a valid instance of the interface, and
+ /// `None` otherwise.
+ ///
+ /// Classes constructed by `InterfaceClass` are unique per type, so
+ /// repeatedly calling this method for the same `InterfaceClass` is allowed.
+ fn associate_class(&mut self, class: InterfaceClass) -> bool;
+}
+
+impl AssociateClass for SpIBinder {
+ fn associate_class(&mut self, class: InterfaceClass) -> bool {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer. An `InterfaceClass` can always be converted
+ // into a valid `AIBinder_Class` pointer, so these parameters are
+ // always safe.
+ sys::AIBinder_associateClass(self.as_native_mut(), class.into())
+ }
+ }
+}
+
+impl PartialEq for SpIBinder {
+ fn eq(&self, other: &Self) -> bool {
+ ptr::eq(self.0, other.0)
+ }
+}
+
+impl Eq for SpIBinder {}
+
+impl Clone for SpIBinder {
+ fn clone(&self) -> Self {
+ unsafe {
+ // Safety: Cloning a strong reference must increment the reference
+ // count. We are guaranteed by the `SpIBinder` constructor
+ // invariants that `self.0` is always a valid `AIBinder` pointer.
+ sys::AIBinder_incStrong(self.0);
+ }
+ Self(self.0)
+ }
+}
+
+impl Drop for SpIBinder {
+ // We hold a strong reference to the IBinder in SpIBinder and need to give up
+ // this reference on drop.
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
+ // know this pointer is safe to pass to `AIBinder_decStrong` here.
+ sys::AIBinder_decStrong(self.as_native_mut());
+ }
+ }
+}
+
+impl<T: AsNative<sys::AIBinder>> IBinder for T {
+ /// Perform a binder transaction
+ fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+ &self,
+ code: TransactionCode,
+ flags: TransactionFlags,
+ input_callback: F,
+ ) -> Result<Parcel> {
+ let mut input = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. It is safe to cast from an
+ // immutable pointer to a mutable pointer here, because
+ // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
+ // methods but the parameter is unfortunately not marked as const.
+ //
+ // After the call, input will be either a valid, owned `AParcel`
+ // pointer, or null.
+ sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
+ };
+ status_result(status)?;
+ let mut input = unsafe {
+ // Safety: At this point, `input` is either a valid, owned `AParcel`
+ // pointer, or null. `Parcel::owned` safely handles both cases,
+ // taking ownership of the parcel.
+ Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)?
+ };
+ input_callback(&mut input)?;
+ let mut reply = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. Although `IBinder::transact` is
+ // not a const method, it is still safe to cast our immutable
+ // pointer to mutable for the call. First, `IBinder::transact` is
+ // thread-safe, so concurrency is not an issue. The only way that
+ // `transact` can affect any visible, mutable state in the current
+ // process is by calling `onTransact` for a local service. However,
+ // in order for transactions to be thread-safe, this method must
+ // dynamically lock its data before modifying it. We enforce this
+ // property in Rust by requiring `Sync` for remotable objects and
+ // only providing `on_transact` with an immutable reference to
+ // `self`.
+ //
+ // This call takes ownership of the `input` parcel pointer, and
+ // passes ownership of the `reply` out parameter to its caller. It
+ // does not affect ownership of the `binder` parameter.
+ sys::AIBinder_transact(
+ self.as_native() as *mut sys::AIBinder,
+ code,
+ &mut input.into_raw(),
+ &mut reply,
+ flags,
+ )
+ };
+ status_result(status)?;
+
+ unsafe {
+ // Safety: `reply` is either a valid `AParcel` pointer or null
+ // after the call to `AIBinder_transact` above, so we can
+ // construct a `Parcel` out of it. `AIBinder_transact` passes
+ // ownership of the `reply` parcel to Rust, so we need to
+ // construct an owned variant. `Parcel::owned` takes ownership
+ // of the parcel pointer.
+ Parcel::owned(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+ }
+
+ fn is_binder_alive(&self) -> bool {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ sys::AIBinder_isAlive(self.as_native())
+ }
+ }
+
+ fn ping_binder(&mut self) -> Result<()> {
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ sys::AIBinder_ping(self.as_native_mut())
+ };
+ status_result(status)
+ }
+
+ fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
+ let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect();
+ let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
+ // file descriptor parameter is always be a valid open file. The
+ // `args` pointer parameter is a valid pointer to an array of C
+ // strings that will outlive the call since `args` lives for the
+ // whole function scope.
+ //
+ // This call does not affect ownership of its binder pointer
+ // parameter and does not take ownership of the file or args array
+ // parameters.
+ sys::AIBinder_dump(
+ self.as_native_mut(),
+ fp.as_raw_fd(),
+ arg_ptrs.as_mut_ptr(),
+ arg_ptrs.len().try_into().unwrap(),
+ )
+ };
+ status_result(status)
+ }
+
+ fn get_extension(&mut self) -> Result<Option<SpIBinder>> {
+ let mut out = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. After this call, the `out`
+ // parameter will be either null, or a valid pointer to an
+ // `AIBinder`.
+ //
+ // This call passes ownership of the out pointer to its caller
+ // (assuming it is set to a non-null value).
+ sys::AIBinder_getExtension(self.as_native_mut(), &mut out)
+ };
+ let ibinder = unsafe {
+ // Safety: The call above guarantees that `out` is either null or a
+ // valid, owned pointer to an `AIBinder`, both of which are safe to
+ // pass to `SpIBinder::from_raw`.
+ SpIBinder::from_raw(out)
+ };
+
+ status_result(status)?;
+ Ok(ibinder)
+ }
+
+ fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+ status_result(unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `recipient` can always be
+ // converted into a valid pointer to an
+ // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+ // cookie, although we depend on this value being set by
+ // `get_cookie` when the death recipient callback is called.
+ sys::AIBinder_linkToDeath(
+ self.as_native_mut(),
+ recipient.as_native_mut(),
+ recipient.get_cookie(),
+ )
+ })
+ }
+
+ fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+ status_result(unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `recipient` can always be
+ // converted into a valid pointer to an
+ // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+ // cookie, although we depend on this value being set by
+ // `get_cookie` when the death recipient callback is called.
+ sys::AIBinder_unlinkToDeath(
+ self.as_native_mut(),
+ recipient.as_native_mut(),
+ recipient.get_cookie(),
+ )
+ })
+ }
+}
+
+impl Serialize for SpIBinder {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ parcel.write_binder(Some(self))
+ }
+}
+
+impl SerializeOption for SpIBinder {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ parcel.write_binder(this)
+ }
+}
+
+impl SerializeArray for SpIBinder {}
+impl SerializeArray for Option<&SpIBinder> {}
+
+impl Deserialize for SpIBinder {
+ fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
+ parcel.read_binder().transpose().unwrap()
+ }
+}
+
+impl DeserializeOption for SpIBinder {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> {
+ parcel.read_binder()
+ }
+}
+
+impl DeserializeArray for SpIBinder {}
+impl DeserializeArray for Option<SpIBinder> {}
+
+/// A weak reference to a Binder remote object.
+///
+/// This struct encapsulates the C++ `wp<IBinder>` class. However, this wrapper
+/// is untyped, so properly typed versions implementing a particular binder
+/// interface should be crated with [`declare_binder_interface!`].
+pub struct WpIBinder(*mut sys::AIBinder_Weak);
+
+impl WpIBinder {
+ /// Create a new weak reference from an object that can be converted into a
+ /// raw `AIBinder` pointer.
+ pub fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
+ let ptr = unsafe {
+ // Safety: `SpIBinder` guarantees that `binder` always contains a
+ // valid pointer to an `AIBinder`.
+ sys::AIBinder_Weak_new(binder.as_native_mut())
+ };
+ assert!(!ptr.is_null());
+ Self(ptr)
+ }
+
+ /// Promote this weak reference to a strong reference to the binder object.
+ pub fn promote(&self) -> Option<SpIBinder> {
+ unsafe {
+ // Safety: `WpIBinder` always contains a valid weak reference, so we
+ // can pass this pointer to `AIBinder_Weak_promote`. Returns either
+ // null or an AIBinder owned by the caller, both of which are valid
+ // to pass to `SpIBinder::from_raw`.
+ let ptr = sys::AIBinder_Weak_promote(self.0);
+ SpIBinder::from_raw(ptr)
+ }
+ }
+}
+
+/// Rust wrapper around DeathRecipient objects.
+#[repr(C)]
+pub struct DeathRecipient {
+ recipient: *mut sys::AIBinder_DeathRecipient,
+ callback: Box<dyn Fn() + Send + 'static>,
+}
+
+impl DeathRecipient {
+ /// Create a new death recipient that will call the given callback when its
+ /// associated object dies.
+ pub fn new<F>(callback: F) -> DeathRecipient
+ where
+ F: Fn() + Send + 'static,
+ {
+ let callback = Box::new(callback);
+ let recipient = unsafe {
+ // Safety: The function pointer is a valid death recipient callback.
+ //
+ // This call returns an owned `AIBinder_DeathRecipient` pointer
+ // which must be destroyed via `AIBinder_DeathRecipient_delete` when
+ // no longer needed.
+ sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
+ };
+ DeathRecipient {
+ recipient,
+ callback,
+ }
+ }
+
+ /// Get the opaque cookie that identifies this death recipient.
+ ///
+ /// This cookie will be used to link and unlink this death recipient to a
+ /// binder object and will be passed to the `binder_died` callback as an
+ /// opaque userdata pointer.
+ fn get_cookie(&self) -> *mut c_void {
+ &*self.callback as *const _ as *mut c_void
+ }
+
+ /// Callback invoked from C++ when the binder object dies.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must have been created with the `get_cookie`
+ /// method of this object.
+ unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + 'static,
+ {
+ let callback = (cookie as *mut F).as_ref().unwrap();
+ callback();
+ }
+}
+
+/// # Safety
+///
+/// A `DeathRecipient` is always constructed with a valid raw pointer to an
+/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this
+/// pointer.
+unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient {
+ fn as_native(&self) -> *const sys::AIBinder_DeathRecipient {
+ self.recipient
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder_DeathRecipient {
+ self.recipient
+ }
+}
+
+impl Drop for DeathRecipient {
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: `self.recipient` is always a valid, owned
+ // `AIBinder_DeathRecipient` pointer returned by
+ // `AIBinder_DeathRecipient_new` when `self` was created. This
+ // delete method can only be called once when `self` is dropped.
+ sys::AIBinder_DeathRecipient_delete(self.recipient);
+ }
+ }
+}
+
+/// Generic interface to remote binder objects.
+///
+/// Corresponds to the C++ `BpInterface` class.
+pub trait Proxy: Sized + Interface {
+ /// The Binder interface descriptor string.
+ ///
+ /// This string is a unique identifier for a Binder interface, and should be
+ /// the same between all implementations of that interface.
+ fn get_descriptor() -> &'static str;
+
+ /// Create a new interface from the given proxy, if it matches the expected
+ /// type of this interface.
+ fn from_binder(binder: SpIBinder) -> Result<Self>;
+}
+
+/// # Safety
+///
+/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow
+/// invocation of `IBinder` methods directly from `Interface` objects. It shares
+/// the same safety as the implementation for `SpIBinder`.
+unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T {
+ fn as_native(&self) -> *const sys::AIBinder {
+ self.as_binder().as_native()
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+ self.as_binder().as_native_mut()
+ }
+}
+
+/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
+/// exist.
+pub fn get_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ unsafe {
+ // Safety: `AServiceManager_getService` returns either a null pointer or
+ // a valid pointer to an owned `AIBinder`. Either of these values is
+ // safe to pass to `SpIBinder::from_raw`.
+ SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr()))
+ }
+}
+
+/// 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<Box<T>> {
+ let service = get_service(name);
+ match service {
+ Some(service) => FromIBinder::try_from(service),
+ None => Err(StatusCode::NAME_NOT_FOUND),
+ }
+}
+
+/// # Safety
+///
+/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an
+/// `AIBinder`, so we can trivially extract this pointer here.
+unsafe impl AsNative<sys::AIBinder> for SpIBinder {
+ fn as_native(&self) -> *const sys::AIBinder {
+ self.0
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+ self.0
+ }
+}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
new file mode 100644
index 0000000..992f074
--- /dev/null
+++ b/libs/binder/rust/src/state.rs
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::sys;
+
+use libc::{pid_t, uid_t};
+
+/// Static utility functions to manage Binder process state.
+pub struct ProcessState;
+
+impl ProcessState {
+ /// Start the Binder IPC thread pool
+ pub fn start_thread_pool() {
+ unsafe {
+ // Safety: Safe FFI
+ sys::ABinderProcess_startThreadPool();
+ }
+ }
+
+ /// Set 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.
+ pub fn set_thread_pool_max_thread_count(num_threads: u32) {
+ unsafe {
+ // Safety: Safe FFI
+ sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads);
+ }
+ }
+
+ /// Block on the Binder IPC thread pool
+ pub fn join_thread_pool() {
+ unsafe {
+ // Safety: Safe FFI
+ sys::ABinderProcess_joinThreadPool();
+ }
+ }
+}
+
+/// Static utility functions to manage Binder thread state.
+pub struct ThreadState;
+
+impl ThreadState {
+ /// This returns the calling UID assuming that this thread is called from a
+ /// thread that is processing a binder transaction (for instance, in the
+ /// implementation of
+ /// [`Remotable::on_transact`](crate::Remotable::on_transact)).
+ ///
+ /// This can be used with higher-level system services to determine the
+ /// caller's identity and check permissions.
+ ///
+ /// Available since API level 29.
+ ///
+ /// \return calling uid or the current process's UID if this thread isn't
+ /// processing a transaction.
+ pub fn get_calling_uid() -> uid_t {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_getCallingUid()
+ }
+ }
+
+ /// This returns the calling PID assuming that this thread is called from a
+ /// thread that is processing a binder transaction (for instance, in the
+ /// implementation of
+ /// [`Remotable::on_transact`](crate::Remotable::on_transact)).
+ ///
+ /// This can be used with higher-level system services to determine the
+ /// caller's identity and check permissions. However, when doing this, one
+ /// should be aware of possible TOCTOU problems when the calling process
+ /// dies and is replaced with another process with elevated permissions and
+ /// the same PID.
+ ///
+ /// Available since API level 29.
+ ///
+ /// \return calling pid or the current process's PID if this thread isn't
+ /// processing a transaction.
+ ///
+ /// If the transaction being processed is a oneway transaction, then this
+ /// method will return 0.
+ pub fn get_calling_pid() -> pid_t {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_getCallingPid()
+ }
+ }
+}
diff --git a/libs/binder/rust/sys/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.h
new file mode 100644
index 0000000..c7a06d9
--- /dev/null
+++ b/libs/binder/rust/sys/BinderBindings.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_parcel.h>
+#include <android/binder_process.h>
+#include <android/binder_shell.h>
+#include <android/binder_status.h>
+
+namespace android {
+
+namespace c_interface {
+
+// Expose error codes from anonymous enum in binder_status.h
+enum StatusCode {
+ OK = STATUS_OK,
+ UNKNOWN_ERROR = STATUS_UNKNOWN_ERROR,
+ NO_MEMORY = STATUS_NO_MEMORY,
+ INVALID_OPERATION = STATUS_INVALID_OPERATION,
+ BAD_VALUE = STATUS_BAD_VALUE,
+ BAD_TYPE = STATUS_BAD_TYPE,
+ NAME_NOT_FOUND = STATUS_NAME_NOT_FOUND,
+ PERMISSION_DENIED = STATUS_PERMISSION_DENIED,
+ NO_INIT = STATUS_NO_INIT,
+ ALREADY_EXISTS = STATUS_ALREADY_EXISTS,
+ DEAD_OBJECT = STATUS_DEAD_OBJECT,
+ FAILED_TRANSACTION = STATUS_FAILED_TRANSACTION,
+ BAD_INDEX = STATUS_BAD_INDEX,
+ NOT_ENOUGH_DATA = STATUS_NOT_ENOUGH_DATA,
+ WOULD_BLOCK = STATUS_WOULD_BLOCK,
+ TIMED_OUT = STATUS_TIMED_OUT,
+ UNKNOWN_TRANSACTION = STATUS_UNKNOWN_TRANSACTION,
+ FDS_NOT_ALLOWED = STATUS_FDS_NOT_ALLOWED,
+ UNEXPECTED_NULL = STATUS_UNEXPECTED_NULL,
+};
+
+// Expose exception codes from anonymous enum in binder_status.h
+enum ExceptionCode {
+ NONE = EX_NONE,
+ SECURITY = EX_SECURITY,
+ BAD_PARCELABLE = EX_BAD_PARCELABLE,
+ ILLEGAL_ARGUMENT = EX_ILLEGAL_ARGUMENT,
+ NULL_POINTER = EX_NULL_POINTER,
+ ILLEGAL_STATE = EX_ILLEGAL_STATE,
+ NETWORK_MAIN_THREAD = EX_NETWORK_MAIN_THREAD,
+ UNSUPPORTED_OPERATION = EX_UNSUPPORTED_OPERATION,
+ SERVICE_SPECIFIC = EX_SERVICE_SPECIFIC,
+ PARCELABLE = EX_PARCELABLE,
+
+ /**
+ * This is special, and indicates to native binder proxies that the
+ * transaction has failed at a low level.
+ */
+ TRANSACTION_FAILED = EX_TRANSACTION_FAILED,
+};
+
+namespace consts {
+
+enum {
+ FIRST_CALL_TRANSACTION = FIRST_CALL_TRANSACTION,
+ LAST_CALL_TRANSACTION = LAST_CALL_TRANSACTION,
+};
+
+enum {
+ FLAG_ONEWAY = FLAG_ONEWAY,
+};
+
+} // namespace consts
+
+} // namespace c_interface
+
+} // namespace android
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
new file mode 100644
index 0000000..9095af2
--- /dev/null
+++ b/libs/binder/rust/sys/lib.rs
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Generated Rust bindings to libbinder_ndk
+
+#![allow(
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused,
+ improper_ctypes,
+ missing_docs
+)]
+use std::error::Error;
+use std::fmt;
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+impl Error for android_c_interface_StatusCode {}
+
+impl fmt::Display for android_c_interface_StatusCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "StatusCode::{:?}", self)
+ }
+}
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
new file mode 100644
index 0000000..622604f
--- /dev/null
+++ b/libs/binder/rust/tests/Android.bp
@@ -0,0 +1,28 @@
+rust_test {
+ name: "rustBinderTest",
+ srcs: ["integration.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+ // For the binaries to be pushed properly as specified in AndroidTest.xml,
+ // this cannot be the same as the module name.
+ stem: "rustBinderTestClientBinary",
+ test_suites: ["general-tests"],
+}
+
+rust_test {
+ name: "rustBinderTestService",
+ srcs: ["integration.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ "liblibc",
+ ],
+ // For the binaries to be pushed properly as specified in AndroidTest.xml,
+ // this cannot be the same as the module name.
+ stem: "rustBinderTestServiceBinary",
+ test_harness: false,
+ // TODO(b/164473602): Remove this setting and add the module to `data`
+ // attribute of rustBinderTest.
+ auto_gen_config: false,
+ test_suites: ["general-tests"],
+}
diff --git a/libs/binder/rust/tests/AndroidTest.xml b/libs/binder/rust/tests/AndroidTest.xml
new file mode 100644
index 0000000..d8d735d
--- /dev/null
+++ b/libs/binder/rust/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Binder Rust integration tests.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="rustBinderTestClientBinary->/data/local/tmp/rustBinderTest" />
+ <option name="push" value="rustBinderTestServiceBinary->/data/local/tmp/rustBinderTestService" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+ <option name="test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="rustBinderTest" />
+ </test>
+</configuration>
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
new file mode 100644
index 0000000..fe59416
--- /dev/null
+++ b/libs/binder/rust/tests/integration.rs
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Rust Binder crate integration tests
+
+use binder::declare_binder_interface;
+use binder::parcel::Parcel;
+use binder::{Binder, IBinder, Interface, SpIBinder, TransactionCode};
+
+/// Name of service runner.
+///
+/// Must match the binary name in Android.bp
+const RUST_SERVICE_BINARY: &str = "rustBinderTestService";
+
+/// Binary to run a test service.
+///
+/// This needs to be in a separate process from the tests, so we spawn this
+/// binary as a child, providing the service name as an argument.
+fn main() -> Result<(), &'static str> {
+ // Ensure that we can handle all transactions on the main thread.
+ binder::ProcessState::set_thread_pool_max_thread_count(0);
+ binder::ProcessState::start_thread_pool();
+
+ let mut args = std::env::args().skip(1);
+ if args.len() < 1 || args.len() > 2 {
+ print_usage();
+ return Err("");
+ }
+ let service_name = args.next().ok_or_else(|| {
+ print_usage();
+ "Missing SERVICE_NAME argument"
+ })?;
+ let extension_name = args.next();
+
+ {
+ let mut service = Binder::new(BnTest(Box::new(TestService {
+ s: service_name.clone(),
+ })));
+ if let Some(extension_name) = extension_name {
+ let extension = BnTest::new_binder(TestService { s: extension_name });
+ 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");
+ }
+
+ binder::ProcessState::join_thread_pool();
+ Err("Unexpected exit after join_thread_pool")
+}
+
+fn print_usage() {
+ 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",
+ ));
+}
+
+#[derive(Clone)]
+struct TestService {
+ s: String,
+}
+
+impl Interface for TestService {}
+
+impl ITest for TestService {
+ fn test(&self) -> binder::Result<String> {
+ Ok(self.s.clone())
+ }
+}
+
+/// Trivial testing binder interface
+pub trait ITest: Interface {
+ /// Returns a test string
+ fn test(&self) -> binder::Result<String>;
+}
+
+declare_binder_interface! {
+ ITest["android.os.ITest"] {
+ native: BnTest(on_transact),
+ proxy: BpTest {
+ x: i32 = 100
+ },
+ }
+}
+
+fn on_transact(
+ service: &dyn ITest,
+ _code: TransactionCode,
+ _data: &Parcel,
+ reply: &mut Parcel,
+) -> binder::Result<()> {
+ reply.write(&service.test()?)?;
+ Ok(())
+}
+
+impl ITest for BpTest {
+ fn test(&self) -> binder::Result<String> {
+ let reply = self
+ .binder
+ .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+ reply.read()
+ }
+}
+
+impl ITest for Binder<BnTest> {
+ fn test(&self) -> binder::Result<String> {
+ self.0.test()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::File;
+ use std::process::{Child, Command};
+ use std::sync::atomic::{AtomicBool, Ordering};
+ use std::sync::Arc;
+ use std::thread;
+ use std::time::Duration;
+
+ use binder::{DeathRecipient, FromIBinder, IBinder, SpIBinder, StatusCode};
+
+ use super::{ITest, RUST_SERVICE_BINARY};
+
+ pub struct ScopedServiceProcess(Child);
+
+ impl ScopedServiceProcess {
+ pub fn new(identifier: &str) -> Self {
+ Self::new_internal(identifier, None)
+ }
+
+ pub fn new_with_extension(identifier: &str, extension: &str) -> Self {
+ Self::new_internal(identifier, Some(extension))
+ }
+
+ fn new_internal(identifier: &str, extension: Option<&str>) -> Self {
+ let mut binary_path =
+ std::env::current_exe().expect("Could not retrieve current executable path");
+ binary_path.pop();
+ binary_path.push(RUST_SERVICE_BINARY);
+ let mut command = Command::new(&binary_path);
+ command.arg(identifier);
+ if let Some(ext) = extension {
+ command.arg(ext);
+ }
+ let child = command.spawn().expect("Could not start service");
+ Self(child)
+ }
+ }
+
+ 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");
+ }
+ }
+
+ #[test]
+ fn check_services() {
+ let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
+ assert!(sm.is_binder_alive());
+ assert!(sm.ping_binder().is_ok());
+
+ assert!(binder::get_service("this_service_does_not_exist").is_none());
+ assert_eq!(
+ binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+
+ // 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)
+ );
+ }
+
+ #[test]
+ fn trivial_client() {
+ let service_name = "trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Box<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ assert_eq!(test_client.test().unwrap(), "trivial_client_test");
+ }
+
+ fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
+ let binder_died = Arc::new(AtomicBool::new(false));
+
+ let mut death_recipient = {
+ let flag = binder_died.clone();
+ DeathRecipient::new(move || {
+ flag.store(true, Ordering::Relaxed);
+ })
+ };
+
+ binder
+ .link_to_death(&mut death_recipient)
+ .expect("link_to_death failed");
+
+ (binder_died, death_recipient)
+ }
+
+ /// Killing a remote service should unregister the service and trigger
+ /// death notifications.
+ #[test]
+ fn test_death_notifications() {
+ binder::ProcessState::start_thread_pool();
+
+ let service_name = "test_death_notifications";
+ let service_process = ScopedServiceProcess::new(service_name);
+ let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+ let (binder_died, _recipient) = register_death_notification(&mut remote);
+
+ drop(service_process);
+ 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!(
+ binder_died.load(Ordering::Relaxed),
+ "Did not receive death notification"
+ );
+ }
+
+ /// Test unregistering death notifications.
+ #[test]
+ fn test_unregister_death_notifications() {
+ binder::ProcessState::start_thread_pool();
+
+ let service_name = "test_unregister_death_notifications";
+ let service_process = ScopedServiceProcess::new(service_name);
+ let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+ let (binder_died, mut recipient) = register_death_notification(&mut remote);
+
+ 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");
+
+ // Pause to ensure any death notifications get delivered
+ thread::sleep(Duration::from_secs(1));
+
+ assert!(
+ !binder_died.load(Ordering::Relaxed),
+ "Received unexpected death notification after unlinking",
+ );
+ }
+
+ /// Dropping a remote handle should unregister any death notifications.
+ #[test]
+ fn test_death_notification_registration_lifetime() {
+ binder::ProcessState::start_thread_pool();
+
+ let service_name = "test_death_notification_registration_lifetime";
+ let service_process = ScopedServiceProcess::new(service_name);
+ let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+ let (binder_died, _recipient) = register_death_notification(&mut remote);
+
+ // This should automatically unregister our death notification.
+ drop(remote);
+
+ drop(service_process);
+
+ // Pause to ensure any death notifications get delivered
+ thread::sleep(Duration::from_secs(1));
+
+ // We dropped the remote handle, so we should not receive the death
+ // notification when the remote process dies here.
+ assert!(
+ !binder_died.load(Ordering::Relaxed),
+ "Received unexpected death notification after dropping remote handle"
+ );
+ }
+
+ /// Test IBinder interface methods not exercised elsewhere.
+ #[test]
+ fn test_misc_ibinder() {
+ let service_name = "rust_test_ibinder";
+
+ {
+ let _process = ScopedServiceProcess::new(service_name);
+
+ let mut remote = binder::get_service(service_name);
+ assert!(remote.is_binder_alive());
+ remote.ping_binder().expect("Could not ping remote service");
+
+ // We're not testing the output of dump here, as that's really a
+ // property of the C++ implementation. There is the risk that the
+ // method just does nothing, but we don't want to depend on any
+ // particular output from the underlying library.
+ let null_out = File::open("/dev/null").expect("Could not open /dev/null");
+ remote
+ .dump(&null_out, &[])
+ .expect("Could not dump remote service");
+ }
+
+ // get/set_extensions is tested in test_extensions()
+
+ // transact is tested everywhere else, and we can't make raw
+ // transactions outside the [FIRST_CALL_TRANSACTION,
+ // LAST_CALL_TRANSACTION] range from the NDK anyway.
+
+ // link_to_death is tested in test_*_death_notification* tests.
+ }
+
+ #[test]
+ fn test_extensions() {
+ let service_name = "rust_test_extensions";
+ let extension_name = "rust_test_extensions_ext";
+
+ {
+ let _process = ScopedServiceProcess::new(service_name);
+
+ 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");
+ assert!(extension.is_none());
+ }
+
+ {
+ let _process = ScopedServiceProcess::new_with_extension(service_name, extension_name);
+
+ 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 extension = maybe_extension.expect("Remote binder did not have an extension");
+
+ let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
+ .expect("Extension could not be converted to the expected interface");
+
+ assert_eq!(extension.test().unwrap(), extension_name);
+ }
+ }
+}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 2680e84..a03835b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -155,6 +155,7 @@
"binderStabilityTest.cpp",
],
+ // critical that libbinder/libbinder_ndk are shared for VTS
shared_libs: [
"libbinder_ndk",
"libbinder",
@@ -166,7 +167,7 @@
"binderStabilityTestIface-ndk_platform",
],
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "vts"],
require_root: true,
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 40de2db..917751e 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -50,6 +50,9 @@
static char *binderserversuffix;
static char binderserverarg[] = "--binderserver";
+static constexpr int kSchedPolicy = SCHED_RR;
+static constexpr int kSchedPriority = 7;
+
static String16 binderLibTestServiceName = String16("test.binderLib");
enum BinderLibTestTranscationCode {
@@ -75,6 +78,7 @@
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
+ BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_BUF,
};
@@ -1015,6 +1019,22 @@
EXPECT_EQ(NO_ERROR, ret2);
}
+TEST_F(BinderLibTest, SchedPolicySet) {
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ Parcel data, reply;
+ status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ int policy = reply.readInt32();
+ int priority = reply.readInt32();
+
+ EXPECT_EQ(kSchedPolicy, policy & (~SCHED_RESET_ON_FORK));
+ EXPECT_EQ(kSchedPriority, priority);
+}
+
+
TEST_F(BinderLibTest, VectorSent) {
Parcel data, reply;
sp<IBinder> server = addServer();
@@ -1332,6 +1352,16 @@
reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_SCHEDULING_POLICY: {
+ int policy = 0;
+ sched_param param;
+ if (0 != pthread_getschedparam(pthread_self(), &policy, ¶m)) {
+ return UNKNOWN_ERROR;
+ }
+ reply->writeInt32(policy);
+ reply->writeInt32(param.sched_priority);
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
@@ -1368,6 +1398,8 @@
{
sp<BinderLibTestService> testService = new BinderLibTestService(index);
+ testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority);
+
/*
* Normally would also contain functionality as well, but we are only
* testing the extension mechanism.
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 50f6289..5e785b6 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -251,7 +251,7 @@
for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
key.bucket = i;
if (findMapEntry(gTisMapFd, &key, vals.data())) {
- if (errno != ENOENT) return {};
+ if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {};
continue;
}
@@ -362,7 +362,7 @@
time_key_t key = {.uid = uid};
for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
- if (errno != ENOENT) return {};
+ if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {};
continue;
}
auto offset = key.bucket * CPUS_PER_ENTRY;
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index 489f3c3..1f71e23 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -696,7 +696,7 @@
// relationship, thus |getConsumerName| from the producer side does not
// make any sense.
ALOGE("BufferHubProducer::getConsumerName not supported.");
- return String8("BufferHubQueue::DummyConsumer");
+ return String8("BufferHubQueue::StubConsumer");
}
status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 59f1bcd..30d19e3 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -280,7 +280,7 @@
mCurrentFenceTime = FenceTime::NO_FENCE;
if (mAttached) {
- // This binds a dummy buffer (mReleasedTexImage).
+ // This binds a buffer placeholder (mReleasedTexImage).
status_t result = bindTextureImageLocked();
if (result != NO_ERROR) {
return result;
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 5c81b9d..0683087 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -119,7 +119,7 @@
return BBinder::onTransact(code, data, reply, flags);
}
-DummyProducerListener::~DummyProducerListener() = default;
+StubProducerListener::~StubProducerListener() = default;
bool BnProducerListener::needsReleaseNotify() {
return true;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e43446a..0281279 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -176,7 +176,12 @@
sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
}
- colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
+ const void* color_transform_data = input.readInplace(16 * sizeof(float));
+ if (color_transform_data) {
+ colorTransform = mat4(static_cast<const float*>(color_transform_data));
+ } else {
+ return BAD_VALUE;
+ }
cornerRadius = input.readFloat();
backgroundBlurRadius = input.readUint32();
cachedBuffer.token = input.readStrongBinder();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index cf269b3..c3323fe 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1513,7 +1513,7 @@
}
int Surface::connect(int api) {
- static sp<IProducerListener> listener = new DummyProducerListener();
+ static sp<IProducerListener> listener = new StubProducerListener();
return connect(api, listener);
}
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index ddd868d..2f538ff 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -499,7 +499,7 @@
// protects static initialization
static Mutex sStaticInitLock;
- // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ // mReleasedTexImageBuffer is a buffer placeholder used when in single buffer
// mode and releaseTexImage() has been called
static sp<GraphicBuffer> sReleasedTexImageBuffer;
sp<EglImage> mReleasedTexImage;
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index d7f3492..45e0a13 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -458,7 +458,7 @@
// the producer wants to be notified when the consumer releases a buffer
// back to the BufferQueue. It is also used to detect the death of the
// producer. If only the latter functionality is desired, there is a
- // DummyProducerListener class in IProducerListener.h that can be used.
+ // StubProducerListener class in IProducerListener.h that can be used.
//
// The api should be one of the NATIVE_WINDOW_API_* values in <window.h>
//
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 0b1f4b5..f7ffbb9 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -80,10 +80,9 @@
class BnProducerListener : public IProducerListener {
};
#endif
-class DummyProducerListener : public BnProducerListener
-{
+class StubProducerListener : public BnProducerListener {
public:
- virtual ~DummyProducerListener();
+ virtual ~StubProducerListener();
virtual void onBufferReleased() {}
virtual bool needsReleaseNotify() { return false; }
};
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 99ab085..004d875 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -51,12 +51,14 @@
typedef ::android::IProducerListener BProducerListener;
#ifndef LOG
-struct LOG_dummy {
+struct LOG_stub {
template <typename T>
- LOG_dummy& operator<< (const T&) { return *this; }
+ LOG_stub& operator<<(const T&) {
+ return *this;
+ }
};
-#define LOG(x) LOG_dummy()
+#define LOG(x) LOG_stub()
#endif
// Instantiate only if HGraphicBufferProducer is base of BASE.
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index d929a59..da5bbdd 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -128,7 +128,7 @@
ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
ASSERT_EQ(NO_ERROR,
- igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
+ igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
&qbOutput));
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
producer = igbProducer;
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index b87cbbd..fc6551c 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -51,7 +51,7 @@
mBFL = new BufferFreedListener(this);
mBIC->setBufferFreedListener(mBFL);
- sp<IProducerListener> producerListener = new DummyProducerListener();
+ sp<IProducerListener> producerListener = new StubProducerListener();
IGraphicBufferProducer::QueueBufferOutput bufferOutput;
ASSERT_EQ(NO_ERROR,
mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -131,7 +131,7 @@
// Test that detaching buffer from consumer side triggers onBufferFreed.
TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) {
int slot;
- // Producer: generate a dummy buffer.
+ // Producer: generate a placeholder buffer.
DequeueBuffer(&slot);
QueueBuffer(slot);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 6d7b6bb..d1208ee 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "BufferQueue_test"
//#define LOG_NDEBUG 0
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
@@ -134,8 +134,8 @@
mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);
EXPECT_TRUE(mConsumer != nullptr);
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
ASSERT_EQ(OK,
mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
@@ -171,23 +171,22 @@
TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
int bufferCount = 50;
mConsumer->setMaxBufferCount(bufferCount);
IGraphicBufferProducer::QueueBufferOutput output;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output);
ASSERT_EQ(output.maxBufferCount, bufferCount);
}
TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbo);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->setMaxDequeuedBufferCount(3);
int slot;
@@ -219,15 +218,14 @@
TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(10));
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbo);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->setMaxDequeuedBufferCount(3);
int minBufferCount;
@@ -263,12 +261,11 @@
TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbo);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->setMaxDequeuedBufferCount(2);
int minBufferCount;
@@ -310,8 +307,8 @@
TEST_F(BufferQueueTest, SetMaxBufferCountWithLegalValues_Succeeds) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
// Test shared buffer mode
EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
@@ -319,8 +316,8 @@
TEST_F(BufferQueueTest, SetMaxBufferCountWithIllegalValues_ReturnsError) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(0));
EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(
@@ -332,11 +329,11 @@
TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(
@@ -386,11 +383,11 @@
TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@@ -445,11 +442,11 @@
TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@@ -488,11 +485,11 @@
TEST_F(BufferQueueTest, TestDisallowingAllocation) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
static const uint32_t WIDTH = 320;
static const uint32_t HEIGHT = 240;
@@ -526,11 +523,11 @@
TEST_F(BufferQueueTest, TestGenerationNumbers) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mProducer->setGenerationNumber(1));
@@ -568,11 +565,11 @@
TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mProducer->setSharedBufferMode(true));
@@ -618,11 +615,11 @@
TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mProducer->setSharedBufferMode(true));
ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
@@ -687,11 +684,11 @@
TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
// Dequeue a buffer
int sharedSlot;
@@ -738,11 +735,11 @@
TEST_F(BufferQueueTest, TestTimeouts) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
// Fill up the queue. Since the controlledByApp flags are set to true, this
// queue should be in non-blocking mode, and we should be recycling the same
@@ -800,11 +797,11 @@
TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
@@ -822,11 +819,11 @@
TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
// Dequeue and queue the first buffer, storing the handle
int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -876,11 +873,11 @@
TEST_F(BufferQueueTest, TestOccupancyHistory) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
@@ -1030,8 +1027,8 @@
TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
sp<BufferDiscardedListener> pl(new BufferDiscardedListener);
ASSERT_EQ(OK, mProducer->connect(pl,
@@ -1115,11 +1112,11 @@
TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -1156,12 +1153,11 @@
TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- sp<IProducerListener> dummyListener(new DummyProducerListener);
- ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
- true, &output));
+ sp<IProducerListener> fakeListener(new StubProducerListener);
+ ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
@@ -1215,15 +1211,13 @@
TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- sp<IProducerListener> dummyListener(new DummyProducerListener);
+ sp<IProducerListener> fakeListener(new StubProducerListener);
ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
- ASSERT_EQ(OK, mProducer->connect(
- dummyListener, NATIVE_WINDOW_API_CPU, true, &output));
- ASSERT_EQ(BAD_VALUE, mProducer->connect(
- dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output));
+ ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(BAD_VALUE, mProducer->connect(fakeListener, NATIVE_WINDOW_API_MEDIA, true, &output));
ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 103f775..15bd32d 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "IGraphicBufferProducer_test"
//#define LOG_NDEBUG 0
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -89,7 +89,7 @@
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
- mDC = new DummyConsumer;
+ mMC = new MockConsumer;
switch (GetParam()) {
case USE_BUFFER_QUEUE_PRODUCER: {
@@ -114,7 +114,7 @@
}
// Must connect consumer before producer connects will succeed.
- ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false));
+ ASSERT_OK(mConsumer->consumerConnect(mMC, /*controlledByApp*/ false));
}
virtual void TearDown() {
@@ -249,7 +249,7 @@
}
private: // hide from test body
- sp<DummyConsumer> mDC;
+ sp<MockConsumer> mMC;
protected: // accessible from test body
sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index acd4297..58d7cc6 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -129,7 +129,7 @@
int32_t mExpectedSlot = 0;
};
-class DummyListener : public BnConsumerListener {
+class FakeListener : public BnConsumerListener {
public:
void onFrameAvailable(const BufferItem&) override {}
void onBuffersReleased() override {}
@@ -140,7 +140,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<IConsumerListener> listener = new DummyListener;
+ sp<IConsumerListener> listener = new FakeListener;
consumer->consumerConnect(listener, false);
sp<MaliciousBQP> malicious = new MaliciousBQP(producer);
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/MockConsumer.h
similarity index 94%
rename from libs/gui/tests/DummyConsumer.h
rename to libs/gui/tests/MockConsumer.h
index 502bdf9..4a6c51c 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/MockConsumer.h
@@ -18,7 +18,7 @@
namespace android {
-struct DummyConsumer : public BnConsumerListener {
+struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index ad6e051..b65cdda 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -48,7 +48,7 @@
}
};
-struct DummyListener : public BnConsumerListener {
+struct FakeListener : public BnConsumerListener {
virtual void onFrameAvailable(const BufferItem& /* item */) {}
virtual void onBuffersReleased() {}
virtual void onSidebandStreamChanged() {}
@@ -64,7 +64,7 @@
sp<IGraphicBufferProducer> outputProducer;
sp<IGraphicBufferConsumer> outputConsumer;
BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
- ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+ ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false));
sp<StreamSplitter> splitter;
status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
@@ -75,8 +75,9 @@
ASSERT_EQ(OK, outputProducer->allowAllocation(false));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &qbOutput));
+ ASSERT_EQ(OK,
+ inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
int slot;
sp<Fence> fence;
@@ -132,8 +133,7 @@
for (int output = 0; output < NUM_OUTPUTS; ++output) {
BufferQueue::createBufferQueue(&outputProducers[output],
&outputConsumers[output]);
- ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(
- new DummyListener, false));
+ ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(new FakeListener, false));
}
sp<StreamSplitter> splitter;
@@ -147,8 +147,9 @@
}
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &qbOutput));
+ ASSERT_EQ(OK,
+ inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
int slot;
sp<Fence> fence;
@@ -203,7 +204,7 @@
sp<IGraphicBufferProducer> outputProducer;
sp<IGraphicBufferConsumer> outputConsumer;
BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
- ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+ ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false));
sp<StreamSplitter> splitter;
status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
@@ -211,8 +212,9 @@
ASSERT_EQ(OK, splitter->addOutput(outputProducer));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &qbOutput));
+ ASSERT_EQ(OK,
+ inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
int slot;
sp<Fence> fence;
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index c85e844..c7458a3 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -54,7 +54,7 @@
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
- // This initializes EGL and create a dummy GL context with a
+ // This initializes EGL and create a GL context placeholder with a
// pbuffer render target.
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 9906166..592913c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -56,12 +56,11 @@
static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
-class DummySurfaceListener : public SurfaceListener {
+class FakeSurfaceListener : public SurfaceListener {
public:
- DummySurfaceListener(bool enableReleasedCb = false) :
- mEnableReleaseCb(enableReleasedCb),
- mBuffersReleased(0) {}
- virtual ~DummySurfaceListener() = default;
+ FakeSurfaceListener(bool enableReleasedCb = false)
+ : mEnableReleaseCb(enableReleasedCb), mBuffersReleased(0) {}
+ virtual ~FakeSurfaceListener() = default;
virtual void onBufferReleased() {
mBuffersReleased++;
@@ -124,15 +123,15 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- sp<DummySurfaceListener> listener;
+ sp<FakeSurfaceListener> listener;
if (hasSurfaceListener) {
- listener = new DummySurfaceListener(enableReleasedCb);
+ listener = new FakeSurfaceListener(enableReleasedCb);
}
ASSERT_EQ(OK, surface->connect(
NATIVE_WINDOW_API_CPU,
@@ -381,8 +380,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -397,8 +396,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -428,8 +427,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -452,8 +451,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -497,8 +496,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -523,13 +522,13 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- sp<DummyProducerListener> listener = new DummyProducerListener();
+ sp<StubProducerListener> listener = new StubProducerListener();
ASSERT_EQ(OK, surface->connect(
NATIVE_WINDOW_API_CPU,
/*listener*/listener,
@@ -1910,8 +1909,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setDefaultBufferSize(10, 10);
sp<Surface> surface = new Surface(producer);
@@ -1980,8 +1979,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index c00fbba..b44456b 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -1,2 +1,4 @@
+alecmouri@google.com
+jreck@google.com
lpy@google.com
stoza@google.com
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 92e7e71..2139acb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -245,22 +245,22 @@
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
- EGLSurface dummy = EGL_NO_SURFACE;
+ EGLSurface stub = EGL_NO_SURFACE;
if (!extensions.hasSurfacelessContext()) {
- dummy = createDummyEglPbufferSurface(display, config, args.pixelFormat,
- Protection::UNPROTECTED);
- LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+ stub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::UNPROTECTED);
+ LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer");
}
- EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
- LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
+ EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current");
extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
- EGLSurface protectedDummy = EGL_NO_SURFACE;
+ EGLSurface protectedStub = EGL_NO_SURFACE;
if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
- protectedDummy = createDummyEglPbufferSurface(display, config, args.pixelFormat,
- Protection::PROTECTED);
- ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer");
+ protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer");
}
// now figure out what version of GL did we actually get
@@ -278,8 +278,8 @@
break;
case GLES_VERSION_2_0:
case GLES_VERSION_3_0:
- engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, dummy,
- protectedContext, protectedDummy);
+ engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub,
+ protectedContext, protectedStub);
break;
}
@@ -334,15 +334,15 @@
}
GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
- EGLConfig config, EGLContext ctxt, EGLSurface dummy,
- EGLContext protectedContext, EGLSurface protectedDummy)
+ EGLConfig config, EGLContext ctxt, EGLSurface stub,
+ EGLContext protectedContext, EGLSurface protectedStub)
: renderengine::impl::RenderEngine(args),
mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
- mDummySurface(dummy),
+ mStubSurface(stub),
mProtectedEGLContext(protectedContext),
- mProtectedDummySurface(protectedDummy),
+ mProtectedStubSurface(protectedStub),
mVpWidth(0),
mVpHeight(0),
mFramebufferImageCacheSize(args.imageCacheSize),
@@ -355,12 +355,12 @@
// Initialize protected EGL Context.
if (mProtectedEGLContext != EGL_NO_CONTEXT) {
- EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface,
+ EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface,
mProtectedEGLContext);
ALOGE_IF(!success, "can't make protected context current");
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
- success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext);
+ success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext);
LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
}
@@ -896,7 +896,7 @@
return false;
}
- // Bind the texture to dummy data so that backing image data can be freed.
+ // Bind the texture to placeholder so that backing image data can be freed.
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
// Release the cached fence here, so that we don't churn reallocations when
@@ -934,7 +934,7 @@
if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
return false;
}
- const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface;
+ const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
if (success) {
@@ -1228,7 +1228,8 @@
if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+ GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
}
@@ -1562,11 +1563,11 @@
return context;
}
-EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection) {
- EGLConfig dummyConfig = config;
- if (dummyConfig == EGL_NO_CONFIG) {
- dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection) {
+ EGLConfig stubConfig = config;
+ if (stubConfig == EGL_NO_CONFIG) {
+ stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
}
std::vector<EGLint> attributes;
attributes.reserve(7);
@@ -1580,7 +1581,7 @@
}
attributes.push_back(EGL_NONE);
- return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+ return eglCreatePbufferSurface(display, stubConfig, attributes.data());
}
bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 42b8537..61986ff 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -53,8 +53,8 @@
static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
- EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
- EGLSurface protectedDummy);
+ EGLContext ctxt, EGLSurface stub, EGLContext protectedContext,
+ EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
void primeCache() const override;
@@ -115,8 +115,8 @@
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext, bool useContextPriority,
Protection protection);
- static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection);
+ static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection);
std::unique_ptr<Framebuffer> createFramebuffer();
std::unique_ptr<Image> createImage();
void checkErrors() const;
@@ -175,9 +175,9 @@
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
EGLContext mEGLContext;
- EGLSurface mDummySurface;
+ EGLSurface mStubSurface;
EGLContext mProtectedEGLContext;
- EGLSurface mProtectedDummySurface;
+ EGLSurface mProtectedStubSurface;
GLint mMaxViewportDims[2];
GLint mMaxTextureSize;
GLuint mVpWidth;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 16a8a0d..0b5b1e4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -916,7 +916,7 @@
void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
fillRedBufferWithoutPremultiplyAlpha();
- expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
}
void RenderEngineTest::clearLeftRegion() {
@@ -926,7 +926,7 @@
settings.clip = Rect(4, 4);
settings.clearRegion = Region(Rect(2, 4));
std::vector<const renderengine::LayerSettings*> layers;
- // dummy layer, without bounds should not render anything
+ // fake layer, without bounds should not render anything
renderengine::LayerSettings layer;
layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 203a739..b1317b1 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,6 +1,7 @@
+alecmouri@google.com
chrisforbes@google.com
+jreck@google.com
lpy@google.com
mathias@google.com
romainguy@google.com
stoza@google.com
-vhau@google.com
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 013505a..57be686 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -260,6 +260,9 @@
uint64_t mId;
+ // Unused, but removing this may break GSI.
+ int32_t mBufferId = -1;
+
// Stores the generation number of this buffer. If this number does not
// match the BufferQueue's internal generation number (set through
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 8cc7081..fab1097 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,7 +108,7 @@
void ConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
// Can connect the first time.
- ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+ ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi,
kTestControlledByApp, &output));
}
@@ -140,7 +140,7 @@
return QueueBufferInputBuilder().build();
}
- const sp<IProducerListener> kDummyListener{new DummyProducerListener};
+ const sp<IProducerListener> kStubListener{new StubProducerListener};
sp<BufferHubProducer> mProducer;
sp<Surface> mSurface;
@@ -150,11 +150,11 @@
IGraphicBufferProducer::QueueBufferOutput output;
// NULL output returns BAD_VALUE
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
kTestControlledByApp, nullptr));
// Invalid API returns bad value
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid,
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid,
kTestControlledByApp, &output));
}
@@ -163,7 +163,7 @@
// Can't connect when there is already a producer connected.
IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
kTestControlledByApp, &output));
}
@@ -554,18 +554,18 @@
ProducerQueueParcelable producer_parcelable;
EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE);
- // Create a valid dummy producer parcelable.
- auto dummy_channel_parcelable =
+ // 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(dummy_channel_parcelable->IsValid());
- ProducerQueueParcelable dummy_producer_parcelable(
- std::move(dummy_channel_parcelable));
- EXPECT_TRUE(dummy_producer_parcelable.IsValid());
+ 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(&dummy_producer_parcelable), BAD_VALUE);
+ 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());
@@ -583,7 +583,7 @@
// But connect to API will fail.
IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp,
+ EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp,
&output),
BAD_VALUE);
@@ -592,8 +592,8 @@
sp<BufferHubProducer> new_producer =
BufferHubProducer::Create(std::move(producer_parcelable));
ASSERT_TRUE(new_producer != nullptr);
- EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
- kTestControlledByApp, &output),
+ EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp,
+ &output),
OK);
}
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index 7b3717e..07e2121 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -308,14 +308,6 @@
class DvrDisplayManagerTest : public Test {
protected:
void SetUp() override {
- // dvr display manager test doesn't apply to standalone vr devices because
- // tests cannot create display manager client on these devices.
- if (property_get_bool("ro.boot.vr", false)) {
- GTEST_SKIP()
- << "All tests in DvrDisplayManagerTest test case are skipped "
- "because the device boot to VR.";
- }
-
int ret;
DvrDisplayManager* display_manager;
DvrSurfaceState* surface_state;
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
index b7d94b3..7b477c4 100644
--- a/libs/vr/libpdx/encoder_performance_test.cpp
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -158,12 +158,12 @@
size_t iterations,
ResetFunc* write_reset,
void* reset_data, size_t data_size) {
- std::vector<uint8_t> dummy_data(data_size);
+ std::vector<uint8_t> fake_data(data_size);
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < iterations; i++) {
write_reset(reset_data);
- memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
- dummy_data.data(), dummy_data.size());
+ memcpy(writer->GetNextWriteBufferSection(fake_data.size()),
+ fake_data.data(), fake_data.size());
}
auto stop = std::chrono::high_resolution_clock::now();
return stop - start;
@@ -177,17 +177,17 @@
MessageReader* reader, MessageWriter* writer, size_t iterations,
ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
size_t data_size) {
- std::vector<uint8_t> dummy_data(data_size);
+ std::vector<uint8_t> fake_data(data_size);
write_reset(reset_data);
- memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
- dummy_data.data(), dummy_data.size());
+ memcpy(writer->GetNextWriteBufferSection(fake_data.size()), fake_data.data(),
+ fake_data.size());
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < iterations; i++) {
read_reset(reset_data);
auto section = reader->GetNextReadBufferSection();
- memcpy(dummy_data.data(), section.first, dummy_data.size());
+ memcpy(fake_data.data(), section.first, fake_data.size());
reader->ConsumeReadBufferSectionData(
- AdvancePointer(section.first, dummy_data.size()));
+ AdvancePointer(section.first, fake_data.size()));
}
auto stop = std::chrono::high_resolution_clock::now();
return stop - start;
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
index aeae9d3..99325b5 100644
--- a/libs/vr/libpdx/private/pdx/rpc/macros.h
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -28,7 +28,7 @@
// Clears any remaining contents wrapped in parentheses.
#define _PDX_CLEAR(...)
-// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+// Introduces a first stub argument and _PDX_CLEAR as second argument.
#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
// Returns the first argument of a list.
@@ -45,7 +45,7 @@
// Returns next_func if the next element is not (), or _PDX_CLEAR
// otherwise.
//
-// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+// _PDX_CLEAR_IF_LAST inserts an extra first stub argument if peek is ().
#define _PDX_NEXT_FUNC(next_element, next_func) \
_PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
diff --git a/libs/vr/libpdx/service_dispatcher.cpp b/libs/vr/libpdx/service_dispatcher.cpp
index b112fa3..ba0d69c 100644
--- a/libs/vr/libpdx/service_dispatcher.cpp
+++ b/libs/vr/libpdx/service_dispatcher.cpp
@@ -92,9 +92,9 @@
if (thread_count_ > 0)
return -EBUSY;
- epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+ epoll_event ee; // See BUGS in man 2 epoll_ctl.
if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(),
- &dummy) < 0) {
+ &ee) < 0) {
ALOGE("Failed to remove service from dispatcher because: %s\n",
strerror(errno));
return -errno;
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 9bc70ea..810eb19 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -334,8 +334,8 @@
int channel_fd = iter->second.data_fd.Get();
Status<void> status;
- epoll_event dummy; // See BUGS in man 2 epoll_ctl.
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) {
+ epoll_event ee; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &ee) < 0) {
status.SetError(errno);
ALOGE(
"Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
index 1cf5f17..0d5eb80 100644
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
@@ -68,8 +68,8 @@
ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
std::lock_guard<std::mutex> lock(lock_);
- epoll_event dummy; // See BUGS in man 2 epoll_ctl.
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) {
+ epoll_event ee; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
const int error = errno;
ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
return pdx::ErrorStatus(error);
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index b771538..70f303b 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -47,7 +47,6 @@
namespace {
const char kDvrPerformanceProperty[] = "sys.dvr.performance";
-const char kDvrStandaloneProperty[] = "ro.boot.vr";
const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
@@ -159,8 +158,6 @@
return false;
}
- is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false);
-
request_display_callback_ = request_display_callback;
primary_display_ = GetDisplayParams(composer, primary_display_id, true);
@@ -206,8 +203,6 @@
return;
boot_finished_ = true;
post_thread_wait_.notify_one();
- if (is_standalone_device_)
- request_display_callback_(true);
}
// Update the post thread quiescent state based on idle and suspended inputs.
@@ -267,17 +262,11 @@
layers_.clear();
// Phones create a new composer client on resume and destroy it on pause.
- // Standalones only create the composer client once and then use SetPowerMode
- // to control the screen on pause/resume.
- if (!is_standalone_device_) {
- if (composer_callback_ != nullptr) {
- composer_callback_->SetVsyncService(nullptr);
- composer_callback_ = nullptr;
- }
- composer_.reset(nullptr);
- } else {
- EnableDisplay(*target_display_, false);
+ if (composer_callback_ != nullptr) {
+ composer_callback_->SetVsyncService(nullptr);
+ composer_callback_ = nullptr;
}
+ composer_.reset(nullptr);
// Trigger target-specific performance mode change.
property_set(kDvrPerformanceProperty, "idle");
@@ -588,7 +577,7 @@
surfaces_changed_ = true;
}
- if (request_display_callback_ && !is_standalone_device_)
+ if (request_display_callback_)
request_display_callback_(!display_idle);
// Set idle state based on whether there are any surfaces to handle.
@@ -773,28 +762,6 @@
VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
- if (is_standalone_device_) {
- // First, wait until boot finishes.
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- if (PostThreadCondWait(lock, -1, [this] { return boot_finished_; })) {
- return;
- }
-
- // Then, wait until we're either leaving the quiescent state, or the boot
- // finished display off timeout expires.
- if (PostThreadCondWait(lock, kBootFinishedDisplayOffTimeoutSec,
- [this] { return !post_thread_quiescent_; })) {
- return;
- }
-
- LOG_ALWAYS_FATAL_IF(post_thread_state_ & PostThreadState::Suspended,
- "Vr flinger should own the display by now.");
- post_thread_resumed_ = true;
- post_thread_ready_.notify_all();
- if (!composer_)
- CreateComposer();
- }
-
while (1) {
ATRACE_NAME("HardwareComposer::PostThread");
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index efd6d1d..ac44f74 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -43,9 +43,6 @@
// completed.
constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
-// How long to wait for a device that boots to VR to have vr flinger ready.
-constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30);
-
// A Binder connection to surface flinger.
class SurfaceFlingerConnection {
public:
@@ -153,11 +150,6 @@
return;
}
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
auto surface_flinger_connection = SurfaceFlingerConnection::Create();
ASSERT_NE(surface_flinger_connection, nullptr);
@@ -230,31 +222,5 @@
SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
}
-// This test runs only on devices that boot to vr. Such a device should boot to
-// a state where vr flinger is running, and the test verifies this after a
-// delay.
-TEST(BootVrFlingerTest, BootsToVrFlinger) {
- // Exit if we are not running on a device that boots to vr.
- if (!property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
- auto surface_flinger_connection = SurfaceFlingerConnection::Create();
- ASSERT_NE(surface_flinger_connection, nullptr);
-
- // Verify that vr flinger is enabled.
- ASSERT_TRUE(surface_flinger_connection->IsAlive());
- auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
- ASSERT_TRUE(vr_flinger_active.has_value());
-
- bool active_value = vr_flinger_active.value();
- if (!active_value) {
- // Try again, but delay up to 30 seconds.
- ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true,
- kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout),
- SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
- }
-}
-
} // namespace dvr
} // namespace android
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 74fb019..a27c09f 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -78,12 +78,12 @@
return;
}
- std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
- CacheEntry dummyEntry(dummyKey, nullptr);
+ std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
+ CacheEntry cacheEntry(cacheKey, nullptr);
while (true) {
- auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
- if (index == mCacheEntries.end() || dummyEntry < *index) {
+ auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry);
+ if (index == mCacheEntries.end() || cacheEntry < *index) {
// Create a new cache entry.
std::shared_ptr<Blob> keyBlob(new Blob(key, keySize, true));
std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
@@ -138,10 +138,10 @@
keySize, mMaxKeySize);
return 0;
}
- std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
- CacheEntry dummyEntry(dummyKey, nullptr);
- auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
- if (index == mCacheEntries.end() || dummyEntry < *index) {
+ std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
+ CacheEntry cacheEntry(cacheKey, nullptr);
+ auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry);
+ if (index == mCacheEntries.end() || cacheEntry < *index) {
ALOGV("get: no cache entry found for key of size %zu", keySize);
return 0;
}
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 3284778..751f3be 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -70,7 +70,7 @@
return;
}
- // Sanity check the size before trying to mmap it.
+ // Check the size before trying to mmap it.
size_t fileSize = statBuf.st_size;
if (fileSize > mMaxTotalSize * 2) {
ALOGE("cache file is too large: %#" PRIx64,
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 510226d..bbd786d 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -138,7 +138,7 @@
};
EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -148,7 +148,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -258,7 +258,7 @@
EXPECT_EQ(components[2], 8);
EXPECT_EQ(components[3], 8);
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -268,7 +268,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
EGLint winAttrs[] = {
@@ -306,7 +306,7 @@
get8BitConfig(config);
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -316,7 +316,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
EGLint winAttrs[] = {
@@ -398,7 +398,7 @@
EXPECT_EQ(components[2], 10);
EXPECT_EQ(components[3], 2);
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -408,7 +408,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
EGLint winAttrs[] = {
@@ -570,7 +570,7 @@
ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -580,7 +580,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -622,7 +622,7 @@
ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -632,7 +632,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
std::vector<EGLint> winAttrs;
@@ -705,7 +705,7 @@
EXPECT_GE(components[2], 16);
EXPECT_GE(components[3], 16);
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -714,7 +714,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -734,7 +734,7 @@
ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_KHR_no_config_context"));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -809,7 +809,7 @@
EXPECT_EQ(components[2], 10);
EXPECT_EQ(components[3], 2);
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -819,7 +819,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -835,7 +835,7 @@
ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -845,7 +845,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -882,7 +882,7 @@
ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(1, numConfigs);
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -892,7 +892,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -913,7 +913,7 @@
ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -923,7 +923,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
@@ -953,7 +953,7 @@
ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
- struct DummyConsumer : public BnConsumerListener {
+ struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
@@ -963,7 +963,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->consumerConnect(new DummyConsumer, false);
+ consumer->consumerConnect(new MockConsumer, false);
sp<Surface> mSTC = new Surface(producer);
sp<ANativeWindow> mANW = mSTC;
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 9efd38f..7fd9c3a 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -17,7 +17,7 @@
mkdir out
-# Create dummy Java files for Android APIs that are used by the code we generate.
+# Create stub Java files for Android APIs that are used by the code we generate.
# This allows us to test the generated code without building the rest of Android.
mkdir -p out/javax/microedition/khronos/opengles
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 304f1d0..81b0a46 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -63,11 +63,23 @@
}
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
- developerDriverPath = driverPath;
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+
+ // only system_server is allowed to set updatable driver path
+ if (uid != AID_SYSTEM) {
+ ALOGE("Permission Denial: can't set updatable driver path from pid=%d, uid=%d\n", pid, uid);
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mLock);
+ mDeveloperDriverPath = driverPath;
}
std::string GpuService::getUpdatableDriverPath() {
- return developerDriverPath;
+ std::lock_guard<std::mutex> lock(mLock);
+ return mDeveloperDriverPath;
}
status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index ba44fe0..d1c3aab 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -75,7 +75,8 @@
* Attributes
*/
std::unique_ptr<GpuStats> mGpuStats;
- std::string developerDriverPath;
+ std::mutex mLock;
+ std::string mDeveloperDriverPath;
};
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b3b9fe5..b37ca33 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -121,13 +121,6 @@
//
// You can either "make dist tests" before flashing, or set this
// option to false temporarily.
-
-
- // FIXME: ASAN build is broken for a while, but was not discovered
- // since new PM silently suppressed ASAN. Temporarily turn off ASAN
- // to unblock the compiler upgrade process.
- // address: true,
- // http://b/139747256
- address: false,
+ address: true,
},
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a9763b..b4ed92f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -116,7 +116,7 @@
FloatRect geomLayerBounds;
// length of the shadow in screen space
- float shadowRadius;
+ float shadowRadius{0.f};
/*
* Geometry state
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 75394fa..d2b38d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -110,7 +110,7 @@
void dump(std::string& result) const;
// Timestamp for when the layer is queued for client composition
- nsecs_t clientCompositionTimestamp;
+ nsecs_t clientCompositionTimestamp{0};
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e8f54f5..34dc536 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -952,7 +952,7 @@
const bool useIdentityTransform = false;
bool firstLayer = true;
// Used when a layer clears part of the buffer.
- Region dummyRegion;
+ Region stubRegion;
for (auto* layer : getOutputLayersOrderedByZ()) {
const auto& layerState = layer->getState();
@@ -991,7 +991,7 @@
layer->needsFiltering() || outputState.needsFiltering,
outputState.isSecure,
supportsProtectedContent,
- clientComposition ? clearRegion : dummyRegion,
+ clientComposition ? clearRegion : stubRegion,
outputState.viewport,
outputDataspace,
realContentIsVisible,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 59ed72e..7a06400 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3583,15 +3583,15 @@
mLayers[1].mLayerFEState.isOpaque = true;
mLayers[2].mLayerFEState.isOpaque = true;
Region accumClearRegion(Rect(10, 11, 12, 13));
- Region dummyRegion;
+ Region stubRegion;
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
- false, /* needs filtering */
- false, /* secure */
- false, /* supports protected content */
- dummyRegion, /* clear region */
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ stubRegion, /* clear region */
kDisplayViewport,
kDisplayDataspace,
false /* realContentIsVisible */,
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index ff91bf7..46112f5 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -340,7 +340,7 @@
return nextEventTime;
}
- // Sanity check that the duration is close enough in length to a period without
+ // Check that the duration is close enough in length to a period without
// falling into double-rate vsyncs.
bool isCloseToPeriod(nsecs_t duration) {
// Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 832f08e..6fb5654 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -227,7 +227,7 @@
// mZeroErrSamplesCount keeps track of how many times in a row there were
// zero timestamps available in the mPresentFences array.
- // Used to sanity check that we are able to calculate the model error.
+ // Used to check that we are able to calculate the model error.
size_t mZeroErrSamplesCount;
// Whether we have updated the vsync event model since the last resync.
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 293738c..4868c12 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -88,7 +88,7 @@
sp<Surface> mSurface;
};
-/* Used to generate valid fences. It is not possible to create a dummy sync
+/* Used to generate valid fences. It is not possible to create a placeholder sync
* fence for testing. Egl can generate buffers along with a valid fence.
* The buffer cannot be guaranteed to be the same format across all devices so
* a CPU filled buffer is used instead. The Egl fence is used along with the
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 97cba63..1f8f7ed 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -118,10 +118,9 @@
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
const auto producer = layer->getIGraphicBufferProducer();
- const sp<IProducerListener> dummyListener(new DummyProducerListener);
+ const sp<IProducerListener> stubListener(new StubProducerListener);
IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
- ASSERT_EQ(OK,
- producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
+ ASSERT_EQ(OK, producer->connect(stubListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
std::map<int, sp<GraphicBuffer>> slotMap;
auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 63a34af..0a24650 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -221,7 +221,7 @@
}
static std::string genLayerName(int32_t layerId) {
- return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerId);
+ return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
}
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
@@ -424,7 +424,7 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
.count());
- // Push a dummy present fence to trigger flushing the RenderEngine timings.
+ // Push a fake present fence to trigger flushing the RenderEngine timings.
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
@@ -505,7 +505,7 @@
ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
- // Push a dummy present fence to trigger flushing the RenderEngine timings.
+ // Push a fake present fence to trigger flushing the RenderEngine timings.
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 74ef0e7..01cbf39 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -964,9 +964,7 @@
// conditionally add VK_GOOGLE_display_timing if present timestamps are
// supported by the driver:
- const std::string timestamp_property("service.sf.present_timestamp");
- android::base::WaitForPropertyCreation(timestamp_property);
- if (android::base::GetBoolProperty(timestamp_property, true)) {
+ if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
loader_extensions.push_back({
VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});