Merge ab/6749736 in stage.
Bug: 167233921
Merged-In: Iac158e7721f651cc0d041fd310d01808d549a4db
Change-Id: Ib8828047260ea745fa9513812b3dbc6b61806d86
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 08f44c8..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, {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8879aed..edad3fd 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1307,12 +1307,12 @@
static void DumpHals() {
if (!ds.IsZipping()) {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
+ CommandOptions::WithTimeout(60).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;
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 3467898..67a77f6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -55,6 +55,7 @@
MOCK_METHOD1(listServices, Vector<String16>(int));
MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
MOCK_METHOD1(isDeclared, bool(const String16&));
+ MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
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/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 0782b43..6001a58 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -941,6 +941,33 @@
auto scope_guard = android::base::make_scope_guard(deleter);
+ if (storageFlags & FLAG_STORAGE_DE) {
+ auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
+ auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
+ auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
+ snapshotId, package_name);
+
+ int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
+ if (rc != 0) {
+ return error(rc, "Failed to create folder " + to);
+ }
+
+ rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
+ if (rc != 0) {
+ return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
+ }
+
+ // Check if we have data to copy.
+ if (access(from.c_str(), F_OK) == 0) {
+ rc = copy_directory_recursive(from.c_str(), to.c_str());
+ }
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ clear_de_on_exit = true;
+ return res;
+ }
+ }
+
// The app may not have any data at all, in which case it's OK to skip here.
auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
if (access(from_ce.c_str(), F_OK) != 0) {
@@ -966,30 +993,6 @@
LOG(WARNING) << "Failed to clear code_cache of app " << packageName;
}
- if (storageFlags & FLAG_STORAGE_DE) {
- auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
- auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
- snapshotId, package_name);
-
- int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
- if (rc != 0) {
- return error(rc, "Failed to create folder " + to);
- }
-
- rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
- if (rc != 0) {
- return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
- }
-
- rc = copy_directory_recursive(from.c_str(), to.c_str());
- if (rc != 0) {
- res = error(rc, "Failed copying " + from + " to " + to);
- clear_de_on_exit = true;
- return res;
- }
- }
-
if (storageFlags & FLAG_STORAGE_CE) {
auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
@@ -2195,6 +2198,9 @@
auto obbPath = StringPrintf("%s/Android/obb",
create_data_media_path(uuid_, userId).c_str());
calculate_tree_size(obbPath, &obbSize);
+ if (!(flags & FLAG_USE_QUOTA)) {
+ totalSize -= obbSize;
+ }
ATRACE_END();
}
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 9c75781..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>
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/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 2c3efe5..99cb93a 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -141,7 +141,7 @@
}
}
- PipeRelay relay(out);
+ PipeRelay relay(out, err, interfaceName, instanceName);
if (relay.initCheck() != OK) {
std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 820679f..4e97636 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -23,7 +23,6 @@
#include <atomic>
-#include <android-base/logging.h>
#include <utils/Thread.h>
namespace android {
@@ -31,8 +30,15 @@
static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
+static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
+ auto dot = interfaceName.rfind(".");
+ if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
+ return "RelayThread_" + interfaceName + "_" + instanceName;
+}
+
struct PipeRelay::RelayThread : public Thread {
- explicit RelayThread(int fd, std::ostream &os);
+ explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &fqName);
bool threadLoop() override;
void setFinished();
@@ -40,6 +46,7 @@
private:
int mFd;
std::ostream &mOutStream;
+ NullableOStream<std::ostream> mErrStream;
// If we were to use requestExit() and exitPending() instead, threadLoop()
// may not run at all by the time ~PipeRelay is called (i.e. debug() has
@@ -47,13 +54,17 @@
// read() are executed until data are drained.
std::atomic_bool mFinished;
+ std::string mFqName;
+
DISALLOW_COPY_AND_ASSIGN(RelayThread);
};
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
- : mFd(fd), mOutStream(os), mFinished(false) {}
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
+ const NullableOStream<std::ostream> &err,
+ const std::string &fqName)
+ : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
bool PipeRelay::RelayThread::threadLoop() {
char buffer[1024];
@@ -66,13 +77,14 @@
int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
if (res < 0) {
- PLOG(INFO) << "select() failed";
+ mErrStream << "debug " << mFqName << ": select() failed";
return false;
}
if (res == 0 || !FD_ISSET(mFd, &set)) {
if (mFinished) {
- LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated.";
+ mErrStream << "debug " << mFqName
+ << ": timeout reading from pipe, output may be truncated.";
return false;
}
// timeout, but debug() has not returned, so wait for HAL to finish.
@@ -83,7 +95,7 @@
ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
if (n < 0) {
- PLOG(ERROR) << "read() failed";
+ mErrStream << "debug " << mFqName << ": read() failed";
}
if (n <= 0) {
@@ -101,8 +113,9 @@
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::PipeRelay(std::ostream &os)
- : mInitCheck(NO_INIT) {
+PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &interfaceName, const std::string &instanceName)
+ : mInitCheck(NO_INIT) {
int res = pipe(mFds);
if (res < 0) {
@@ -110,8 +123,8 @@
return;
}
- mThread = new RelayThread(mFds[0], os);
- mInitCheck = mThread->run("RelayThread");
+ mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
+ mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
}
void PipeRelay::CloseFd(int *fd) {
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index 835016041..bd994b4 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -21,6 +21,8 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include "NullableOStream.h"
+
namespace android {
namespace lshal {
@@ -28,7 +30,10 @@
* written to the "write"-end of the pair to the specified output stream "os".
*/
struct PipeRelay {
- explicit PipeRelay(std::ostream &os);
+ explicit PipeRelay(std::ostream& os,
+ const NullableOStream<std::ostream>& err,
+ const std::string& interfaceName,
+ const std::string& instanceName);
~PipeRelay();
status_t initCheck() const;
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 e80c321..7aac7da 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -37,6 +37,27 @@
namespace android {
#ifndef VENDORSERVICEMANAGER
+struct ManifestWithDescription {
+ std::shared_ptr<const vintf::HalManifest> manifest;
+ const char* description;
+};
+// func true -> stop search and forEachManifest will return true
+static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
+ for (const ManifestWithDescription& mwd : {
+ ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
+ ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
+ }) {
+ if (mwd.manifest == nullptr) {
+ LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
+ // note, we explicitly do not retry here, so that we can detect VINTF
+ // or other bugs (b/151696835)
+ continue;
+ }
+ if (func(mwd)) return true;
+ }
+ return false;
+}
+
static bool isVintfDeclared(const std::string& name) {
size_t firstSlash = name.find('/');
size_t lastDot = name.rfind('.', firstSlash);
@@ -49,31 +70,41 @@
const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
const std::string instance = name.substr(firstSlash+1);
- struct ManifestWithDescription {
- std::shared_ptr<const vintf::HalManifest> manifest;
- const char* description;
- };
- for (const ManifestWithDescription& mwd : {
- ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
- ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
- }) {
- if (mwd.manifest == nullptr) {
- LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
- // note, we explicitly do not retry here, so that we can detect VINTF
- // or other bugs (b/151696835)
- continue;
- }
+ bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
return true;
}
+ return false; // continue
+ });
+
+ if (!found) {
+ // Although it is tested, explicitly rebuilding qualified name, in case it
+ // becomes something unexpected.
+ LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
+ << " in the VINTF manifest.";
}
- // Although it is tested, explicitly rebuilding qualified name, in case it
- // becomes something unexpected.
- LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
- << " in the VINTF manifest.";
- return false;
+ return found;
+}
+
+static std::vector<std::string> getVintfInstances(const std::string& interface) {
+ size_t lastDot = interface.rfind('.');
+ if (lastDot == std::string::npos) {
+ LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface;
+ return {};
+ }
+ const std::string package = interface.substr(0, lastDot);
+ const std::string iface = interface.substr(lastDot+1);
+
+ std::vector<std::string> ret;
+ (void)forEachManifest([&](const ManifestWithDescription& mwd) {
+ auto instances = mwd.manifest->getAidlInstances(package, iface);
+ ret.insert(ret.end(), instances.begin(), instances.end());
+ return false; // continue
+ });
+
+ return ret;
}
static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
@@ -331,6 +362,29 @@
return Status::ok();
}
+binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ std::vector<std::string> allInstances;
+#ifndef VENDORSERVICEMANAGER
+ allInstances = getVintfInstances(interface);
+#endif
+
+ outReturn->clear();
+
+ for (const std::string& instance : allInstances) {
+ if (mAccess->canFind(ctx, interface + "/" + instance)) {
+ outReturn->push_back(instance);
+ }
+ }
+
+ if (outReturn->size() == 0 && allInstances.size() != 0) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ return Status::ok();
+}
+
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index a2fc5a8..9f43eb4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,6 +44,7 @@
const sp<IServiceCallback>& callback) override;
binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) override;
binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 2618906..b1bc6dc 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -130,7 +130,7 @@
}
IPCThreadState::self()->setTheContextObject(manager);
- ps->becomeContextManager(nullptr, nullptr);
+ ps->becomeContextManager();
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
index cc4c842..32bcc83 100644
--- a/cmds/surfacereplayer/OWNERS
+++ b/cmds/surfacereplayer/OWNERS
@@ -1,2 +1 @@
-mathias@google.com
-racarr@google.com
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 6efa4f7..5f74682 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -65,6 +65,10 @@
* another process. File descriptors may also be sent to other processes over a Unix domain
* socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
*
+ * If you intend to share this file descriptor with a child process after
+ * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD
+ * to clear the FD_CLOEXEC flag for this to work on all versions of Android.
+ *
* Available since API level 26.
*
* \param name an optional name.
diff --git a/include/binder/Enum.h b/include/binder/Enum.h
new file mode 100644
index 0000000..4c25654
--- /dev/null
+++ b/include/binder/Enum.h
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+#error Do not rely on global include files. All Android cc_* programs are given access to \
+ include_dirs for frameworks/native/include via global configuration, but this is legacy \
+ configuration. Instead, you should have a direct dependency on libbinder OR one of your \
+ dependencies should re-export libbinder headers with export_shared_lib_headers.
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 92da10c..0a00241 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -20,8 +20,8 @@
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
+#include <input/PropertyMap.h>
#include <utils/Errors.h>
-#include <utils/PropertyMap.h>
namespace android {
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
new file mode 100644
index 0000000..3d04331
--- /dev/null
+++ b/include/input/PropertyMap.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+ /* Creates an empty property map. */
+ PropertyMap();
+ ~PropertyMap();
+
+ /* Clears the property map. */
+ void clear();
+
+ /* Adds a property.
+ * Replaces the property with the same key if it is already present.
+ */
+ void addProperty(const String8& key, const String8& value);
+
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const String8& key) const;
+
+ /* Gets the value of a property and parses it.
+ * Returns true and sets outValue if the key was found and its value was parsed successfully.
+ * Otherwise returns false and does not modify outValue. (Also logs a warning.)
+ */
+ bool tryGetProperty(const String8& key, String8& outValue) const;
+ bool tryGetProperty(const String8& key, bool& outValue) const;
+ bool tryGetProperty(const String8& key, int32_t& outValue) const;
+ bool tryGetProperty(const String8& key, float& outValue) const;
+
+ /* Adds all values from the specified property map. */
+ void addAll(const PropertyMap* map);
+
+ /* Gets the underlying property map. */
+ inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
+
+ /* Loads a property map from a file. */
+ static status_t load(const String8& filename, PropertyMap** outMap);
+
+private:
+ class Parser {
+ PropertyMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(PropertyMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseType();
+ status_t parseKey();
+ status_t parseKeyProperty();
+ status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseCharacterLiteral(char16_t* outCharacter);
+ };
+
+ KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
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 861b589..d363ee9 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",
],
@@ -99,6 +99,7 @@
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
"Parcel.cpp",
+ "ParcelableHolder.cpp",
"ParcelFileDescriptor.cpp",
"PersistableBundle.cpp",
"ProcessState.cpp",
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 157538e..05fcc2b 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -448,6 +448,14 @@
return mLastTransactionBinderFlags;
}
+void IPCThreadState::setCallRestriction(ProcessState::CallRestriction restriction) {
+ mCallRestriction = restriction;
+}
+
+ProcessState::CallRestriction IPCThreadState::getCallRestriction() const {
+ return mCallRestriction;
+}
+
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
@@ -679,7 +687,7 @@
CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
ANDROID_LOG_ERROR);
} else /* FATAL_IF_NOT_ONEWAY */ {
- LOG_ALWAYS_FATAL("Process may not make oneway calls (code: %u).", code);
+ LOG_ALWAYS_FATAL("Process may not make non-oneway calls (code: %u).", code);
}
}
@@ -860,6 +868,10 @@
err = FAILED_TRANSACTION;
goto finish;
+ case BR_FROZEN_REPLY:
+ err = FAILED_TRANSACTION;
+ goto finish;
+
case BR_ACQUIRE_RESULT:
{
ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
@@ -1316,6 +1328,42 @@
}
}
+status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received)
+{
+ int ret = 0;
+ binder_frozen_status_info info;
+ info.pid = pid;
+
+#if defined(__ANDROID__)
+ if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
+ ret = -errno;
+#endif
+ *sync_received = info.sync_recv;
+ *async_received = info.async_recv;
+
+ return ret;
+}
+
+status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
+ struct binder_freeze_info info;
+ int ret = 0;
+
+ info.pid = pid;
+ info.enable = enable;
+ info.timeout_ms = timeout_ms;
+
+
+#if defined(__ANDROID__)
+ if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0)
+ ret = -errno;
+#endif
+
+ //
+ // ret==-EAGAIN indicates that transactions have not drained.
+ // Call again to poll for completion.
+ //
+ return ret;
+}
void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
size_t /*dataSize*/,
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9aa82d9..6d728dc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -74,6 +74,7 @@
Vector<String16> listServices(int dumpsysPriority) override;
sp<IBinder> waitForService(const String16& name16) override;
bool isDeclared(const String16& name) override;
+ Vector<String16> getDeclaredInstances(const String16& interface) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -373,4 +374,18 @@
return declared;
}
+Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
+ std::vector<std::string> out;
+ if (!mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out).isOk()) {
+ return {};
+ }
+
+ Vector<String16> res;
+ res.setCapacity(out.size());
+ for (const std::string& instance : out) {
+ res.push(String16(instance.c_str()));
+ }
+ return res;
+}
+
} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 632458e..a9c19b3 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -41,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>
@@ -528,14 +527,19 @@
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
+ return writeInterfaceToken(interface.string(), interface.size());
+}
+
+status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ?
threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
+
// currently the interface identification token is just its name as a string
- return writeString16(interface);
+ return writeString16(str, len);
}
bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
@@ -1526,7 +1530,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) {
@@ -1559,7 +1563,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/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
new file mode 100644
index 0000000..e9df279
--- /dev/null
+++ b/libs/binder/ParcelableHolder.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/ParcelableHolder.h>
+
+#define RETURN_ON_FAILURE(expr) \
+ do { \
+ android::status_t _status = (expr); \
+ if (_status != android::OK) return _status; \
+ } while (false)
+
+namespace android {
+namespace os {
+status_t ParcelableHolder::writeToParcel(Parcel* p) const {
+ std::lock_guard<std::mutex> l(mMutex);
+ RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
+ if (this->mParcelPtr) {
+ RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
+ RETURN_ON_FAILURE(p->appendFrom(this->mParcelPtr.get(), 0, this->mParcelPtr->dataSize()));
+ return OK;
+ }
+ if (this->mParcelable) {
+ size_t sizePos = p->dataPosition();
+ RETURN_ON_FAILURE(p->writeInt32(0));
+ size_t dataStartPos = p->dataPosition();
+ RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName));
+ this->mParcelable->writeToParcel(p);
+ size_t dataSize = p->dataPosition() - dataStartPos;
+
+ p->setDataPosition(sizePos);
+ RETURN_ON_FAILURE(p->writeInt32(dataSize));
+ p->setDataPosition(p->dataPosition() + dataSize);
+ return OK;
+ }
+
+ RETURN_ON_FAILURE(p->writeInt32(0));
+ return OK;
+}
+
+status_t ParcelableHolder::readFromParcel(const Parcel* p) {
+ std::lock_guard<std::mutex> l(mMutex);
+ this->mStability = static_cast<Stability>(p->readInt32());
+ this->mParcelable = nullptr;
+ this->mParcelableName = std::nullopt;
+ int32_t rawDataSize;
+
+ status_t status = p->readInt32(&rawDataSize);
+ if (status != android::OK || rawDataSize < 0) {
+ this->mParcelPtr = nullptr;
+ return status != android::OK ? status : BAD_VALUE;
+ }
+ if (rawDataSize == 0) {
+ if (this->mParcelPtr) {
+ this->mParcelPtr = nullptr;
+ }
+ return OK;
+ }
+
+ size_t dataSize = rawDataSize;
+
+ size_t dataStartPos = p->dataPosition();
+
+ if (dataStartPos > SIZE_MAX - dataSize) {
+ this->mParcelPtr = nullptr;
+ return BAD_VALUE;
+ }
+
+ if (!this->mParcelPtr) {
+ this->mParcelPtr = std::make_unique<Parcel>();
+ }
+ this->mParcelPtr->freeData();
+
+ status = this->mParcelPtr->appendFrom(p, dataStartPos, dataSize);
+ if (status != android::OK) {
+ this->mParcelPtr = nullptr;
+ return status;
+ }
+ p->setDataPosition(dataStartPos + dataSize);
+ return OK;
+}
+} // namespace os
+} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index acc1e67..83ca687 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -144,11 +144,9 @@
}
}
-bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+bool ProcessState::becomeContextManager()
{
AutoMutex _l(mLock);
- mBinderContextCheckFunc = checkFunc;
- mBinderContextUserData = userData;
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
@@ -160,13 +158,11 @@
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) {
- mBinderContextCheckFunc = nullptr;
- mBinderContextUserData = nullptr;
ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
}
@@ -286,9 +282,17 @@
// a driver API to get a handle to the context manager with
// proper reference counting.
+ IPCThreadState* ipc = IPCThreadState::self();
+
+ CallRestriction originalCallRestriction = ipc->getCallRestriction();
+ ipc->setCallRestriction(CallRestriction::NONE);
+
Parcel data;
- status_t status = IPCThreadState::self()->transact(
+ status_t status = ipc->transact(
0, IBinder::PING_TRANSACTION, data, nullptr, 0);
+
+ ipc->setCallRestriction(originalCallRestriction);
+
if (status == DEAD_OBJECT)
return nullptr;
}
@@ -397,14 +401,12 @@
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
- , mBinderContextCheckFunc(nullptr)
- , mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, 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/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ff15460..2b1e492 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -99,6 +99,14 @@
boolean isDeclared(@utf8InCpp String name);
/**
+ * Returns all declared instances for a particular interface.
+ *
+ * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+ * passed here, then ["foo"] would be returned.
+ */
+ @utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
+
+ /**
* Request a callback when the number of clients of the service changes.
* Used by LazyServiceRegistrar to dynamically stop services that have no clients.
*/
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
index 52c730c..c2d4a3f 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/fuzzer/binder.cpp
@@ -19,6 +19,8 @@
#include "util.h"
#include <android/os/IServiceManager.h>
+#include <binder/ParcelableHolder.h>
+#include <binder/PersistableBundle.h>
using ::android::status_t;
@@ -251,5 +253,20 @@
PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+
+ // additional parcelable objects defined in libbinder
+ [] (const ::android::Parcel& p, uint8_t data) {
+ using ::android::os::ParcelableHolder;
+ using ::android::Parcelable;
+ FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status";
+ Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL;
+ if ( (data & 1) == 1 ) {
+ stability = Parcelable::Stability::STABILITY_VINTF;
+ }
+ ParcelableHolder t = ParcelableHolder(stability);
+ status_t status = p.readParcelable(&t);
+ FUZZ_LOG() << "ParcelableHolder status: " << status;
+ },
+ PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
};
// clang-format on
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 2bd39a7..49ef253 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -32,9 +32,28 @@
class IPCThreadState
{
public:
+ using CallRestriction = ProcessState::CallRestriction;
+
static IPCThreadState* self();
static IPCThreadState* selfOrNull(); // self(), but won't instantiate
-
+
+ // Freeze or unfreeze the binder interface to a specific process. When freezing, this method
+ // will block up to timeout_ms to process pending transactions directed to pid. Unfreeze
+ // is immediate. Transactions to processes frozen via this method won't be delivered and the
+ // driver will return BR_FROZEN_REPLY to the client sending them. After unfreeze,
+ // transactions will be delivered normally.
+ //
+ // pid: id for the process for which the binder interface is to be frozen
+ // enable: freeze (true) or unfreeze (false)
+ // timeout_ms: maximum time this function is allowed to block the caller waiting for pending
+ // binder transactions to be processed.
+ //
+ // returns: 0 in case of success, a value < 0 in case of error
+ static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
+
+ // Provide information about the state of a frozen process
+ static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received,
+ bool *async_received);
sp<ProcessState> process();
status_t clearLastError();
@@ -82,6 +101,9 @@
void setLastTransactionBinderFlags(int32_t flags);
int32_t getLastTransactionBinderFlags() const;
+ void setCallRestriction(CallRestriction restriction);
+ CallRestriction getCallRestriction() const;
+
int64_t clearCallingIdentity();
// Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
@@ -140,7 +162,6 @@
// This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
// side.
static const int32_t kUnsetWorkSource = -1;
-
private:
IPCThreadState();
~IPCThreadState();
@@ -187,8 +208,7 @@
bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
-
- ProcessState::CallRestriction mCallRestriction;
+ CallRestriction mCallRestriction;
};
} // namespace android
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 1d520c1..3c5ccc1 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -96,6 +96,11 @@
* service.
*/
virtual bool isDeclared(const String16& name) = 0;
+
+ /**
+ * Get all instances of a service as declared in the VINTF manifest
+ */
+ virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
};
sp<IServiceManager> defaultServiceManager();
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/Parcel.h b/libs/binder/include/binder/Parcel.h
index b6cfb8e..fbfd6c5 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -86,6 +86,7 @@
// Writes the RPC header.
status_t writeInterfaceToken(const String16& interface);
+ status_t writeInterfaceToken(const char16_t* str, size_t len);
// Parses the RPC header, returning true if the interface name
// in the header matches the expected interface from the caller.
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index 83c2f19..a6e610c 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -56,7 +56,7 @@
// 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 {
+ enum class Stability : int32_t {
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
new file mode 100644
index 0000000..b6814aa
--- /dev/null
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -0,0 +1,141 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <mutex>
+#include <optional>
+#include <tuple>
+
+namespace android {
+namespace os {
+/*
+ * C++ implementation of the Java class android.os.ParcelableHolder
+ */
+class ParcelableHolder : public android::Parcelable {
+public:
+ ParcelableHolder() = delete;
+ explicit ParcelableHolder(Stability stability) : mStability(stability){};
+ virtual ~ParcelableHolder() = default;
+ ParcelableHolder(const ParcelableHolder& other) {
+ mParcelable = other.mParcelable;
+ mParcelableName = other.mParcelableName;
+ if (other.mParcelPtr) {
+ mParcelPtr = std::make_unique<Parcel>();
+ mParcelPtr->appendFrom(other.mParcelPtr.get(), 0, other.mParcelPtr->dataSize());
+ }
+ mStability = other.mStability;
+ };
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ void reset() {
+ this->mParcelable = nullptr;
+ this->mParcelableName = std::nullopt;
+ this->mParcelPtr = nullptr;
+ }
+
+ template <typename T>
+ bool setParcelable(T&& p) {
+ using Tt = typename std::decay<T>::type;
+ return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p)));
+ }
+
+ template <typename T>
+ bool setParcelable(std::shared_ptr<T> p) {
+ std::lock_guard<std::mutex> l(mMutex);
+ static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
+ if (p && this->getStability() > p->getStability()) {
+ return false;
+ }
+ this->mParcelable = p;
+ this->mParcelableName = T::getParcelableDescriptor();
+ this->mParcelPtr = nullptr;
+ return true;
+ }
+
+ template <typename T>
+ std::shared_ptr<T> getParcelable() const {
+ static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
+ std::lock_guard<std::mutex> l(mMutex);
+ const std::string& parcelableDesc = T::getParcelableDescriptor();
+ if (!this->mParcelPtr) {
+ if (!this->mParcelable || !this->mParcelableName) {
+ ALOGD("empty ParcelableHolder");
+ return nullptr;
+ } else if (parcelableDesc != *mParcelableName) {
+ ALOGD("extension class name mismatch expected:%s actual:%s",
+ mParcelableName->c_str(), parcelableDesc.c_str());
+ return nullptr;
+ }
+ return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ }
+ this->mParcelPtr->setDataPosition(0);
+ status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
+ if (status != android::OK || parcelableDesc != this->mParcelableName) {
+ this->mParcelableName = std::nullopt;
+ return nullptr;
+ }
+ this->mParcelable = std::make_shared<T>();
+ status = mParcelable.get()->readFromParcel(this->mParcelPtr.get());
+ if (status != android::OK) {
+ this->mParcelableName = std::nullopt;
+ this->mParcelable = nullptr;
+ return nullptr;
+ }
+ this->mParcelPtr = nullptr;
+ return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ }
+
+ Stability getStability() const override { return mStability; };
+
+ inline bool operator!=(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) !=
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator<(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) <
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator<=(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) <=
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator==(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) ==
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator>(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) >
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator>=(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) >=
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+
+private:
+ mutable std::shared_ptr<Parcelable> mParcelable;
+ mutable std::optional<std::string> mParcelableName;
+ mutable std::unique_ptr<Parcel> mParcelPtr;
+ Stability mStability;
+ mutable std::mutex mMutex;
+};
+} // namespace os
+} // namespace android
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9f5346a..efb95f4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,14 +50,8 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
void startThreadPool();
-
- typedef bool (*context_check_func)(const String16& name,
- const sp<IBinder>& caller,
- void* userData);
- bool becomeContextManager(
- context_check_func checkFunc,
- void* userData);
+ bool becomeContextManager();
sp<IBinder> getStrongProxyForHandle(int32_t handle);
void expungeHandle(int32_t handle, IBinder* binder);
@@ -128,9 +122,6 @@
Vector<handle_entry>mHandleToObject;
- context_check_func mBinderContextCheckFunc;
- void* mBinderContextUserData;
-
String8 mRootDir;
bool mThreadPoolStarted;
volatile int32_t mThreadPoolSeq;
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index c22be9f..7be8f7b 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -36,6 +36,60 @@
#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#ifndef BR_FROZEN_REPLY
+// Temporary definition of BR_FROZEN_REPLY. For production
+// this will come from UAPI binder.h
+#define BR_FROZEN_REPLY _IO('r', 18)
+#endif //BR_FROZEN_REPLY
+
+#ifndef BINDER_FREEZE
+/*
+ * Temporary definitions for freeze support. For the final version
+ * these will be defined in the UAPI binder.h file from upstream kernel.
+ */
+#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
+
+struct binder_freeze_info {
+ //
+ // Group-leader PID of process to be frozen
+ //
+ uint32_t pid;
+ //
+ // Enable(1) / Disable(0) freeze for given PID
+ //
+ uint32_t enable;
+ //
+ // Timeout to wait for transactions to drain.
+ // 0: don't wait (ioctl will return EAGAIN if not drained)
+ // N: number of ms to wait
+ uint32_t timeout_ms;
+};
+#endif //BINDER_FREEZE
+
+#ifndef BINDER_GET_FROZEN_INFO
+
+#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
+
+struct binder_frozen_status_info {
+ //
+ // Group-leader PID of process to be queried
+ //
+ __u32 pid;
+ //
+ // Indicates whether the process has received any sync calls since last
+ // freeze (cleared at freeze/unfreeze)
+ //
+ __u32 sync_recv;
+ //
+ // Indicates whether the process has received any async calls since last
+ // freeze (cleared at freeze/unfreeze)
+ //
+ __u32 async_recv;
+};
+#endif //BINDER_GET_FROZEN_INFO
+
+
+
#ifdef __cplusplus
} // namespace android
#endif
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index d287290..2a2eed7 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
#include "ibinder_internal.h"
@@ -72,14 +73,13 @@
AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {}
AIBinder::~AIBinder() {}
-bool AIBinder::associateClass(const AIBinder_Class* clazz) {
- if (clazz == nullptr) return false;
+std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz,
+ const String16& newDescriptor, bool set) {
+ std::lock_guard<std::mutex> lock(mClazzMutex);
if (mClazz == clazz) return true;
- String8 newDescriptor(clazz->getInterfaceDescriptor());
-
if (mClazz != nullptr) {
- String8 currentDescriptor(mClazz->getInterfaceDescriptor());
+ const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
if (newDescriptor == currentDescriptor) {
LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
<< "' match during associateClass, but they are different class objects. "
@@ -88,33 +88,46 @@
LOG(ERROR) << __func__
<< ": Class cannot be associated on object which already has a class. "
"Trying to associate to '"
- << newDescriptor.c_str() << "' but already set to '"
- << currentDescriptor.c_str() << "'.";
+ << newDescriptor << "' but already set to '" << currentDescriptor << "'.";
}
// always a failure because we know mClazz != clazz
return false;
}
+ if (set) {
+ // if this is a local object, it's not one known to libbinder_ndk
+ mClazz = clazz;
+ return true;
+ }
+
+ return {};
+}
+
+bool AIBinder::associateClass(const AIBinder_Class* clazz) {
+ if (clazz == nullptr) return false;
+
+ const String16& newDescriptor = clazz->getInterfaceDescriptor();
+
+ auto result = associateClassInternal(clazz, newDescriptor, false);
+ if (result.has_value()) return *result;
+
CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor
- String8 descriptor(getBinder()->getInterfaceDescriptor());
+ const String16& descriptor = getBinder()->getInterfaceDescriptor();
if (descriptor != newDescriptor) {
if (getBinder()->isBinderAlive()) {
- LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
- << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+ LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
+ << "' but descriptor is actually '" << descriptor << "'.";
} else {
// b/155793159
- LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor.c_str()
+ LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
<< "' to dead binder.";
}
return false;
}
- // if this is a local object, it's not one known to libbinder_ndk
- mClazz = clazz;
-
- return true;
+ return associateClassInternal(clazz, newDescriptor, true).value();
}
ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5779427..f601127 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -22,6 +22,7 @@
#include <atomic>
#include <mutex>
+#include <optional>
#include <vector>
#include <binder/Binder.h>
@@ -52,10 +53,14 @@
}
private:
+ std::optional<bool> associateClassInternal(const AIBinder_Class* clazz,
+ const ::android::String16& newDescriptor, bool set);
+
// AIBinder instance is instance of this class for a local object. In order to transact on a
// remote object, this also must be set for simplicity (although right now, only the
// interfaceDescriptor from it is used).
const AIBinder_Class* mClazz;
+ std::mutex mClazzMutex;
};
// This is a local AIBinder object with a known class.
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index f59bb75..18877af 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -44,9 +44,14 @@
class SpAIBinder {
public:
/**
+ * Default constructor.
+ */
+ SpAIBinder() : mBinder(nullptr) {}
+
+ /**
* Takes ownership of one strong refcount of binder.
*/
- explicit SpAIBinder(AIBinder* binder = nullptr) : mBinder(binder) {}
+ explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {}
/**
* Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not
@@ -199,6 +204,9 @@
public:
/**
* Takes ownership of a.
+ *
+ * WARNING: this constructor is only expected to be used when reading a
+ * status value. Use `ScopedAStatus::ok()` instead.
*/
explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
~ScopedAStatus() {}
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..a4f4441 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 || defined(__ANDROID_APEX__)
+ 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_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
new file mode 100644
index 0000000..6701518
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcelable_utils.h
+ * @brief Helper for parcelable.
+ */
+
+#pragma once
+
+namespace ndk {
+// Also see Parcelable.h in libbinder.
+typedef int32_t parcelable_stability_t;
+enum {
+ STABILITY_LOCAL,
+ STABILITY_VINTF, // corresponds to @VintfStability
+};
+} // namespace ndk
+
+/** @} */
diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h
new file mode 100644
index 0000000..a99d555
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_context.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+/**
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
+ *
+ * \param binder local server binder to request security contexts on
+ */
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+ __INTRODUCED_IN(31);
+
+/**
+ * Returns the selinux context of the callee.
+ *
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
+ *
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
+ */
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
+
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index d4feaba..2af65cf 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -16,40 +16,14 @@
#pragma once
+// binder_context.h used to be part of this header and is included for backwards
+// compatibility.
+#include <android/binder_context.h>
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
#include <android/binder_ibinder.h>
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
#include <binder/IBinder.h>
-#endif
-
-__BEGIN_DECLS
-
-/**
- * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
- * must be called on a local binder server before it is sent out to any othe
- * process. If this is a remote binder, it will abort. If the kernel doesn't
- * support this feature, you'll always get null from AIBinder_getCallingSid.
- *
- * \param binder local server binder to request security contexts on
- */
-void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) __INTRODUCED_IN(31);
-
-/**
- * Returns the selinux context of the callee.
- *
- * In order for this to work, the following conditions must be met:
- * - The kernel must be new enough to support this feature.
- * - The server must have called AIBinder_setRequestingSid.
- * - The callee must be a remote process.
- *
- * \return security context or null if unavailable. The lifetime of this context
- * is the lifetime of the transaction.
- */
-__attribute__((warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-
-__END_DECLS
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
/**
* Get libbinder version of binder from AIBinder.
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 52bcd20..2784aa8 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -29,9 +29,9 @@
* \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.
+ * \return EX_NONE on success.
*/
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance);
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance);
/**
* Gets a binder object with this specific instance name. Will return nullptr immediately if the
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c33c44f..722ae23 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -272,7 +272,7 @@
}
binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) {
- return PruneStatusT(status->get()->writeToParcel(parcel->get()));
+ return PruneStatusT(status->get().writeToParcel(parcel->get()));
}
binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) {
::android::binder::Status bstatus;
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 6b2184e..c782d47 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -29,14 +29,14 @@
using ::android::status_t;
using ::android::String16;
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) {
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) {
if (binder == nullptr || instance == nullptr) {
- return STATUS_UNEXPECTED_NULL;
+ return EX_ILLEGAL_ARGUMENT;
}
sp<IServiceManager> sm = defaultServiceManager();
- status_t status = sm->addService(String16(instance), binder->getBinder());
- return PruneStatusT(status);
+ status_t exception = sm->addService(String16(instance), binder->getBinder());
+ return PruneException(exception);
}
AIBinder* AServiceManager_checkService(const char* instance) {
if (instance == nullptr) {
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 87e1341..a8ae441 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -23,7 +23,8 @@
using ::android::binder::Status;
AStatus* AStatus_newOk() {
- return new AStatus();
+ static AStatus status = AStatus();
+ return &status;
}
AStatus* AStatus_fromExceptionCode(binder_exception_t exception) {
@@ -47,27 +48,27 @@
}
bool AStatus_isOk(const AStatus* status) {
- return status->get()->isOk();
+ return status->get().isOk();
}
binder_exception_t AStatus_getExceptionCode(const AStatus* status) {
- return PruneException(status->get()->exceptionCode());
+ return PruneException(status->get().exceptionCode());
}
int32_t AStatus_getServiceSpecificError(const AStatus* status) {
- return status->get()->serviceSpecificErrorCode();
+ return status->get().serviceSpecificErrorCode();
}
binder_status_t AStatus_getStatus(const AStatus* status) {
- return PruneStatusT(status->get()->transactionError());
+ return PruneStatusT(status->get().transactionError());
}
const char* AStatus_getMessage(const AStatus* status) {
- return status->get()->exceptionMessage().c_str();
+ return status->get().exceptionMessage().c_str();
}
const char* AStatus_getDescription(const AStatus* status) {
- android::String8 description = status->get()->toString8();
+ android::String8 description = status->get().toString8();
char* cStr = new char[description.size() + 1];
memcpy(cStr, description.c_str(), description.size() + 1);
return cStr;
@@ -78,7 +79,9 @@
}
void AStatus_delete(AStatus* status) {
- delete status;
+ if (status != AStatus_newOk()) {
+ delete status;
+ }
}
binder_status_t PruneStatusT(status_t status) {
@@ -123,8 +126,8 @@
return STATUS_UNKNOWN_ERROR;
default:
- LOG(WARNING) << __func__
- << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status;
+ LOG(WARNING) << __func__ << ": Unknown status_t (" << status
+ << ") pruned into STATUS_UNKNOWN_ERROR";
return STATUS_UNKNOWN_ERROR;
}
}
@@ -155,8 +158,8 @@
return EX_TRANSACTION_FAILED;
default:
- LOG(WARNING) << __func__
- << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception;
+ LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception
+ << ") pruned into EX_TRANSACTION_FAILED";
return EX_TRANSACTION_FAILED;
}
}
diff --git a/libs/binder/ndk/status_internal.h b/libs/binder/ndk/status_internal.h
index f6227f7..cb96e48 100644
--- a/libs/binder/ndk/status_internal.h
+++ b/libs/binder/ndk/status_internal.h
@@ -25,8 +25,7 @@
AStatus() {} // ok
explicit AStatus(::android::binder::Status&& status) : mStatus(std::move(status)) {}
- ::android::binder::Status* get() { return &mStatus; }
- const ::android::binder::Status* get() const { return &mStatus; }
+ const ::android::binder::Status& get() const { return mStatus; }
private:
::android::binder::Status mStatus;
diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp
index 7c271f6..46e6270 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -66,9 +66,6 @@
],
test_suites: ["general-tests", "vts"],
require_root: true,
-
- // force since binderVendorDoubleLoadTest has its own
- auto_gen_config: true,
}
cc_test {
@@ -90,6 +87,7 @@
"libutils",
],
test_suites: ["general-tests", "vts"],
+ require_root: true,
}
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 b2c412d..44d8ebf 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -18,6 +18,7 @@
#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
+#include <android/binder_context.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
@@ -84,10 +85,11 @@
AIBinder_setRequestingSid(binder.get(), true);
- binder_status_t status = AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
+ binder_exception_t exception =
+ AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+ if (exception != EX_NONE) {
+ LOG(FATAL) << "Could not register: " << exception << " " << kBinderNdkUnitTestService;
}
ABinderProcess_joinThreadPool();
@@ -111,10 +113,10 @@
void manualService(const char* instance) {
// Strong reference to MyFoo kept by service manager.
- binder_status_t status = (new MyFoo)->addService(instance);
+ binder_exception_t exception = (new MyFoo)->addService(instance);
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << instance;
+ if (exception != EX_NONE) {
+ LOG(FATAL) << "Could not register: " << exception << " " << instance;
}
}
int manualPollingService(const char* instance) {
@@ -302,11 +304,20 @@
}
};
+TEST(NdkBinder, AddNullService) {
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
+}
+
+TEST(NdkBinder, AddInvalidServiceName) {
+ sp<IFoo> foo = new MyTestFoo;
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, foo->addService("!@#$%^&"));
+}
+
TEST(NdkBinder, GetServiceInProcess) {
static const char* kInstanceName = "test-get-service-in-process";
sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
+ EXPECT_EQ(EX_NONE, foo->addService(kInstanceName));
sp<IFoo> getFoo = IFoo::getService(kInstanceName);
EXPECT_EQ(foo.get(), getFoo.get());
@@ -353,8 +364,8 @@
static const char* kInstanceName1 = "test-multi-1";
static const char* kInstanceName2 = "test-multi-2";
sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
+ EXPECT_EQ(EX_NONE, foo->addService(kInstanceName1));
+ EXPECT_EQ(EX_NONE, foo->addService(kInstanceName2));
EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
new file mode 100644
index 0000000..dc8270e
--- /dev/null
+++ b/libs/binder/rust/Android.bp
@@ -0,0 +1,83 @@
+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.hpp",
+ source_stem: "bindings",
+ 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..6d0a369
--- /dev/null
+++ b/libs/binder/rust/src/binder.rs
@@ -0,0 +1,658 @@
+/*
+ * 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<()>;
+
+ /// Indicate that the service intends to receive caller security contexts.
+ fn set_requesting_sid(&mut self, enable: bool);
+
+ /// 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<()> {
+ match $on_transact(&*self.0, code, data, reply) {
+ // The C++ backend converts UNEXPECTED_NULL into an exception
+ Err($crate::StatusCode::UNEXPECTED_NULL) => {
+ let status = $crate::Status::new_exception(
+ $crate::ExceptionCode::NULL_POINTER,
+ None,
+ );
+ reply.write(&status)
+ },
+ result => result
+ }
+ }
+
+ 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))
+ }
+ }
+
+ impl std::fmt::Debug for dyn $interface {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.pad(stringify!($interface))
+ }
+ }
+
+ // Convert a &dyn $interface to Box<dyn $interface>
+ impl std::borrow::ToOwned for dyn $interface {
+ type Owned = Box<dyn $interface>;
+ fn to_owned(&self) -> Self::Owned {
+ self.as_binder().into_interface()
+ .expect(concat!("Error cloning interface ", stringify!($interface)))
+ }
+ }
+ };
+}
+
+/// Declare an AIDL enumeration.
+///
+/// This is mainly used internally by the AIDL compiler.
+#[macro_export]
+macro_rules! declare_binder_enum {
+ {
+ $enum:ident : $backing:ty {
+ $( $name:ident = $value:expr, )*
+ }
+ } => {
+ #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+ pub struct $enum(pub $backing);
+ impl $enum {
+ $( pub const $name: Self = Self($value); )*
+ }
+
+ impl $crate::parcel::Serialize for $enum {
+ fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ parcel.write(&self.0)
+ }
+ }
+
+ impl $crate::parcel::SerializeArray for $enum {
+ fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
+ <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+ }
+ }
+
+ impl $crate::parcel::Deserialize for $enum {
+ fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> {
+ parcel.read().map(Self)
+ }
+ }
+
+ impl $crate::parcel::DeserializeArray for $enum {
+ fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> {
+ let v: Option<Vec<$backing>> =
+ <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+ Ok(v.map(|v| v.into_iter().map(Self).collect()))
+ }
+ }
+ };
+}
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..2c1e5a4
--- /dev/null
+++ b/libs/binder/rust/src/parcel.rs
@@ -0,0 +1,554 @@
+/*
+ * 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;
+
+ 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<String>>(), 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("Embedded null \0 inside a string").is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert_eq!(
+ parcel.read::<Option<String>>().unwrap().unwrap(),
+ "Embedded null \0 inside a string",
+ );
+ 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..20e9178
--- /dev/null
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -0,0 +1,127 @@
+/*
+ * 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
+#[derive(Debug)]
+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..138b360
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -0,0 +1,880 @@
+/*
+ * 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;
+use std::os::raw::c_char;
+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 str {
+ 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 utf-8
+ // string pointer of `length` bytes, which is what str in Rust
+ // is. The docstring for `AParcel_writeString` says that the
+ // string input should be null-terminated, but it doesn't
+ // actually rely on that fact in the code. If this ever becomes
+ // necessary, we will need to null-terminate the str buffer
+ // before sending it.
+ status_result(sys::AParcel_writeString(
+ parcel.as_native_mut(),
+ s.as_ptr() as *const c_char,
+ s.as_bytes()
+ .len()
+ .try_into()
+ .or(Err(StatusCode::BAD_VALUE))?,
+ ))
+ },
+ }
+ }
+}
+
+impl SerializeArray for Option<&str> {}
+
+impl Serialize for str {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self).serialize(parcel)
+ }
+}
+
+impl SerializeArray for &str {}
+
+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<String> {
+ 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 we don't want the
+ // string to be null-terminated for Rust.
+ s.pop();
+ String::from_utf8(s).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<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 s4 = "Embedded nulls \0 \0";
+
+ let strs = [s1, s2, s3, s4];
+
+ 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..5002fc6
--- /dev/null
+++ b/libs/binder/rust/src/proxy.rs
@@ -0,0 +1,548 @@
+/*
+ * 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::fmt;
+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);
+
+impl fmt::Debug for SpIBinder {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("SpIBinder")
+ }
+}
+
+/// # 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 set_requesting_sid(&mut self, enable: bool) {
+ unsafe {
+ sys::AIBinder_setRequestingSid(self.as_native_mut(), enable)
+ };
+ }
+
+ 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_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+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..0e05f10
--- /dev/null
+++ b/libs/binder/rust/src/state.rs
@@ -0,0 +1,133 @@
+/*
+ * 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()
+ }
+ }
+
+ /// This function makes the client's security context available to the
+ /// service calling this function. This can be used for access control.
+ /// It does not suffer from the TOCTOU issues of get_calling_pid.
+ ///
+ /// Implementations of `check_permission` should use the given CStr
+ /// argument as context for selinux permission checks. If `None` is
+ /// given, the implementation should fall back to using the PID
+ /// instead.
+ ///
+ /// Note: `None` may be passed to the callback if the caller did not
+ /// `set_requesting_sid` on the serviced binder, or if the underlying
+ /// kernel is too old to support this feature.
+ pub fn with_calling_sid<T, F>(check_permission: F) -> T
+ where
+ for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T {
+ // Safety: AIBinder_getCallingSid returns a c-string pointer
+ // that is valid for a transaction. Also, the string returned
+ // is thread local. By restricting the lifetime of the CStr
+ // reference to the scope of the callback, we prevent it being
+ // used beyond the guaranteed lifetime.
+ check_permission(unsafe {
+ let sid = sys::AIBinder_getCallingSid();
+ // AIBinder_getCallingSid() returns a '\0' terminated string
+ // or NULL.
+ if sid.is_null() {
+ None
+ } else {
+ Some(std::ffi::CStr::from_ptr(sid))
+ }
+ })
+ }
+}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
new file mode 100644
index 0000000..303f4a5
--- /dev/null
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -0,0 +1,87 @@
+/*
+ * 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_context.h>
+#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..3db40ba
--- /dev/null
+++ b/libs/binder/rust/tests/Android.bp
@@ -0,0 +1,32 @@
+rust_test {
+ name: "rustBinderTest",
+ srcs: ["integration.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ "libselinux_bindgen",
+ ],
+ shared_libs: [
+ "libselinux",
+ ],
+ // 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/ndk/tests/AndroidTest.xml b/libs/binder/rust/tests/AndroidTest.xml
similarity index 61%
rename from libs/binder/ndk/tests/AndroidTest.xml
rename to libs/binder/rust/tests/AndroidTest.xml
index 89646f7..d8d735d 100644
--- a/libs/binder/ndk/tests/AndroidTest.xml
+++ b/libs/binder/rust/tests/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- 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.
@@ -13,20 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Runs binderVendorDoubleLoadTest.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
-
+<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="binderVendorDoubleLoadTest->/data/nativetest/vendor/binderVendorDoubleLoadTest" />
+ <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.GTest" >
- <option name="native-test-device-path" value="/data/nativetest/vendor" />
- <option name="module-name" value="binderVendorDoubleLoadTest" />
+ <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..953d328
--- /dev/null
+++ b/libs/binder/rust/tests/integration.rs
@@ -0,0 +1,438 @@
+/*
+ * 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, StatusCode, ThreadState, TransactionCode};
+use std::convert::{TryFrom, TryInto};
+
+/// 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(),
+ })));
+ service.set_requesting_sid(true);
+ 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,
+}
+
+#[repr(u32)]
+enum TestTransactionCode {
+ Test = SpIBinder::FIRST_CALL_TRANSACTION,
+ GetSelinuxContext,
+}
+
+impl TryFrom<u32> for TestTransactionCode {
+ type Error = StatusCode;
+
+ fn try_from(c: u32) -> Result<Self, Self::Error> {
+ match c {
+ _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test),
+ _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
+ Ok(TestTransactionCode::GetSelinuxContext)
+ }
+ _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+ }
+ }
+}
+
+impl Interface for TestService {}
+
+impl ITest for TestService {
+ fn test(&self) -> binder::Result<String> {
+ Ok(self.s.clone())
+ }
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ let sid =
+ ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
+ sid.ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+}
+
+/// Trivial testing binder interface
+pub trait ITest: Interface {
+ /// Returns a test string
+ fn test(&self) -> binder::Result<String>;
+
+ /// Returns the caller's SELinux context
+ fn get_selinux_context(&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<()> {
+ match code.try_into()? {
+ TestTransactionCode::Test => reply.write(&service.test()?),
+ TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
+ }
+}
+
+impl ITest for BpTest {
+ fn test(&self) -> binder::Result<String> {
+ let reply =
+ self.binder
+ .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
+ reply.read()
+ }
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ let reply = self.binder.transact(
+ TestTransactionCode::GetSelinuxContext as TransactionCode,
+ 0,
+ |_| Ok(()),
+ )?;
+ reply.read()
+ }
+}
+
+impl ITest for Binder<BnTest> {
+ fn test(&self) -> binder::Result<String> {
+ self.0.test()
+ }
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ self.0.get_selinux_context()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use selinux_bindgen as selinux_sys;
+ use std::ffi::CStr;
+ use std::fs::File;
+ use std::process::{Child, Command};
+ use std::ptr;
+ 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");
+ }
+
+ #[test]
+ fn get_selinux_context() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Box<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ let expected_context = unsafe {
+ let mut out_ptr = ptr::null_mut();
+ assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+ assert!(!out_ptr.is_null());
+ CStr::from_ptr(out_ptr)
+ };
+ assert_eq!(
+ test_client.get_selinux_context().unwrap(),
+ expected_context.to_str().expect("context was invalid UTF-8"),
+ );
+ }
+
+ 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/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 917751e..98f0868 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <fstream>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
@@ -79,6 +80,8 @@
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
+ BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
+ BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_BUF,
};
@@ -399,6 +402,49 @@
EXPECT_EQ(NO_ERROR, ret);
}
+TEST_F(BinderLibTest, Freeze) {
+ status_t ret;
+ Parcel data, reply, replypid;
+ std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
+
+ //Pass test on devices where the freezer is not supported
+ if (freezer_file.fail()) {
+ GTEST_SKIP();
+ return;
+ }
+
+ std::string freezer_enabled;
+ std::getline(freezer_file, freezer_enabled);
+
+ //Pass test on devices where the freezer is disabled
+ if (freezer_enabled != "1") {
+ GTEST_SKIP();
+ return;
+ }
+
+ ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+ int32_t pid = replypid.readInt32();
+ EXPECT_EQ(NO_ERROR, ret);
+ for (int i = 0; i < 10; i++) {
+ EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
+ }
+ EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
+ EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000));
+ EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
+
+ bool sync_received, async_received;
+
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received,
+ &async_received));
+
+ EXPECT_EQ(sync_received, 1);
+ EXPECT_EQ(async_received, 0);
+
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
+ EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
+}
+
TEST_F(BinderLibTest, SetError) {
int32_t testValue[] = { 0, -123, 123 };
for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
@@ -1178,6 +1224,12 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return ret;
}
+ case BINDER_LIB_TEST_GETPID:
+ reply->writeInt32(getpid());
+ return NO_ERROR;
+ case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT:
+ usleep(5000);
+ return NO_ERROR;
case BINDER_LIB_TEST_NOP_TRANSACTION:
return NO_ERROR;
case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
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/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6964324..4ecbe53 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -61,4 +61,16 @@
return mNameToService.find(name) != mNameToService.end();
}
+Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+ Vector<String16> out;
+ const String16 prefix = name + String16("/");
+ for (const auto& [registeredName, service] : mNameToService) {
+ (void) service;
+ if (registeredName.startsWith(prefix)) {
+ out.add(String16(registeredName.string() + prefix.size()));
+ }
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index 62311d4..4ef47fb 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -30,42 +30,23 @@
public:
ServiceManager();
- /**
- * Equivalent of checkService.
- */
sp<IBinder> getService( const String16& name) const override;
- /**
- * Retrieve an existing service, non-blocking.
- */
sp<IBinder> checkService( const String16& name) const override;
- /**
- * Register a service.
- */
status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated = false,
int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
- /**
- * Return list of all existing services.
- */
Vector<String16> listServices(int dumpsysFlags = 0) override;
IBinder* onAsBinder() override;
- /**
- * Effectively no-oped in this implementation - equivalent to checkService.
- */
sp<IBinder> waitForService(const String16& name) override;
- /**
- * Check if a service is declared (e.g. VINTF manifest).
- *
- * If this returns true, waitForService should always be able to return the
- * service.
- */
- bool isDeclared(const String16& name) override;
+ bool isDeclared(const String16& name) override;
+
+ Vector<String16> getDeclaredInstances(const String16& iface) override;
private:
std::map<String16, sp<IBinder>> mNameToService;
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 53c68b7..e2f072a 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -706,35 +706,35 @@
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBytes), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.sampleIncrementInBits), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.strideInBytes), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.widthInSamples), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.heightInSamples), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.totalSizeInBytes), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.horizontalSubsampling), output);
if (err) {
return err;
}
- return encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+ return encodeInteger<int64_t>(static_cast<int64_t>(input.verticalSubsampling), output);
}
status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 7037680..6a5d434 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -28,6 +28,7 @@
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "PropertyMap.cpp",
"TouchVideoFrame.cpp",
"VirtualKeyMap.cpp",
],
@@ -74,4 +75,23 @@
},
}
+cc_defaults {
+ name: "libinput_fuzz_defaults",
+ host_supported: true,
+ shared_libs: [
+ "libutils",
+ "libbase",
+ "liblog",
+ ],
+}
+
+cc_fuzz {
+ name: "libinput_fuzz_propertymap",
+ defaults: ["libinput_fuzz_defaults"],
+ srcs: [
+ "PropertyMap.cpp",
+ "PropertyMap_fuzz.cpp",
+ ],
+}
+
subdirs = ["tests"]
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
new file mode 100644
index 0000000..4833eb9
--- /dev/null
+++ b/libs/input/PropertyMap.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "PropertyMap"
+
+#include <input/PropertyMap.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {}
+
+PropertyMap::~PropertyMap() {}
+
+void PropertyMap::clear() {
+ mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+ return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+ int32_t intValue;
+ if (!tryGetProperty(key, intValue)) {
+ return false;
+ }
+
+ outValue = intValue;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), &end, 10);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(),
+ stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), &end);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(),
+ stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+void PropertyMap::addAll(const PropertyMap* map) {
+ for (size_t i = 0; i < map->mProperties.size(); i++) {
+ mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+ }
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+ *outMap = nullptr;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening property file %s.", status, filename.string());
+ } else {
+ PropertyMap* map = new PropertyMap();
+ if (!map) {
+ ALOGE("Error allocating property map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
+ }
+ return status;
+}
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer)
+ : mMap(map), mTokenizer(tokenizer) {}
+
+PropertyMap::Parser::~Parser() {}
+
+status_t PropertyMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ if (keyToken.isEmpty()) {
+ ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (mTokenizer->nextChar() != '=') {
+ ALOGE("%s: Expected '=' between property key and value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+ if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+ ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+
+ if (mMap->hasProperty(keyToken)) {
+ ALOGE("%s: Duplicate property value for key '%s'.",
+ mTokenizer->getLocation().string(), keyToken.string());
+ return BAD_VALUE;
+ }
+
+ mMap->addProperty(keyToken, valueToken);
+ }
+
+ mTokenizer->nextLine();
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
new file mode 100755
index 0000000..23ead0e
--- /dev/null
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "input/PropertyMap.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_FILE_SIZE = 256;
+static constexpr int MAX_STR_LEN = 2048;
+static constexpr int MAX_OPERATIONS = 1000;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+ operations = {
+ [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ propertyMap.getProperties();
+ },
+ [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ propertyMap.clear();
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ propertyMap.hasProperty(key);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ android::String8 out;
+ propertyMap.tryGetProperty(key, out);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ TemporaryFile tf;
+ // Generate file contents
+ std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
+ // If we have string contents, dump them into the file.
+ // Otherwise, just leave it as an empty file.
+ if (contents.length() > 0) {
+ const char* bytes = contents.c_str();
+ android::base::WriteStringToFd(bytes, tf.fd);
+ }
+ android::PropertyMap* mapPtr = &propertyMap;
+ android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ android::String8 val = android::String8(valStr.c_str());
+ propertyMap.addProperty(key, val);
+ },
+};
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::PropertyMap proprtyMap = android::PropertyMap();
+
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ operations[op](&dataProvider, proprtyMap);
+ }
+ return 0;
+}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index f6a95ce..b431cbb 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -443,12 +443,18 @@
}
void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AChoreographer_postFrameCallback(choreographer, callback, data);
+#pragma clang diagnostic pop
}
void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+#pragma clang diagnostic pop
}
void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 7c84a19..2139acb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -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);
}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 50e5550..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() {
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/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 6eed24a..4da6db3 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -42,11 +42,11 @@
defaults: ["libgpuservice_defaults"],
cflags: [
"-fvisibility=hidden",
- "-fwhole-program-vtables", // requires ThinLTO
],
lto: {
thin: true,
},
+ whole_program_vtables: true, // Requires ThinLTO
}
filegroup {
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 683c05d..c9001b0 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -29,8 +29,8 @@
#include <hardware/input.h>
#include <input/InputDevice.h>
+#include <input/PropertyMap.h>
#include <utils/Log.h>
-#include <utils/PropertyMap.h>
#include <utils/String8.h>
#define INDENT2 " "
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index c17f3a1..f5451d7 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -24,6 +24,7 @@
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
+#include <input/PropertyMap.h>
#include <input/VirtualKeyMap.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
@@ -31,7 +32,6 @@
#include <utils/List.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
-#include <utils/PropertyMap.h>
#include <linux/input.h>
#include <sys/epoll.h>
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 71313fc..7c17102 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -19,8 +19,8 @@
#include <input/DisplayViewport.h>
#include <input/InputDevice.h>
+#include <input/PropertyMap.h>
#include <stdint.h>
-#include <utils/PropertyMap.h>
#include <optional>
#include <unordered_map>
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 8a282e2..e355594 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -153,12 +153,18 @@
SensorDeviceUtils::defaultResolutionForType(sensor.type);
}
- double promotedResolution = sensor.resolution;
- double promotedMaxRange = sensor.maxRange;
- if (fmod(promotedMaxRange, promotedResolution) != 0) {
- ALOGW("%s's max range %f is not a multiple of the resolution %f",
- sensor.name, sensor.maxRange, sensor.resolution);
- SensorDeviceUtils::quantizeValue(&sensor.maxRange, promotedResolution);
+ // Some sensors don't have a default resolution and will be left at 0.
+ // Don't crash in this case since CTS will verify that devices don't go to
+ // production with a resolution of 0.
+ if (sensor.resolution != 0) {
+ double promotedResolution = sensor.resolution;
+ double promotedMaxRange = sensor.maxRange;
+ if (fmod(promotedMaxRange, promotedResolution) != 0) {
+ ALOGW("%s's max range %f is not a multiple of the resolution %f",
+ sensor.name, sensor.maxRange, sensor.resolution);
+ SensorDeviceUtils::quantizeValue(
+ &sensor.maxRange, promotedResolution);
+ }
}
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index a790d0b..bdd04db 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -108,11 +108,11 @@
defaults: ["libsurfaceflinger_defaults"],
cflags: [
"-fvisibility=hidden",
- "-fwhole-program-vtables", // requires ThinLTO
],
lto: {
thin: true,
},
+ whole_program_vtables: true, // Requires ThinLTO
// TODO(b/131771163): Fix broken fuzzer support with LTO.
sanitize: {
fuzzer: false,
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/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index d889d74..d5bf569 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -30,6 +30,7 @@
namespace {
using ::testing::_;
+using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Ref;
using ::testing::Return;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 7b1f0fb..7666f7f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -72,7 +72,7 @@
prop {
api_name: "max_graphics_width"
type: Integer
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.max_graphics_width"
}
@@ -82,7 +82,7 @@
prop {
api_name: "max_graphics_height"
type: Integer
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.max_graphics_height"
}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index b66e56e..ba60a7d 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -36,6 +36,11 @@
prop_name: "ro.surface_flinger.display_primary_white"
}
prop {
+ api_name: "display_update_imminent_timeout_ms"
+ type: Integer
+ prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
+ }
+ prop {
api_name: "enable_protected_contents"
prop_name: "ro.surface_flinger.protected_contents"
}
@@ -57,6 +62,16 @@
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
}
prop {
+ api_name: "max_graphics_height"
+ type: Integer
+ prop_name: "ro.surface_flinger.max_graphics_height"
+ }
+ prop {
+ api_name: "max_graphics_width"
+ type: Integer
+ prop_name: "ro.surface_flinger.max_graphics_width"
+ }
+ prop {
api_name: "max_virtual_display_dimension"
type: Long
prop_name: "ro.surface_flinger.max_virtual_display_dimension"
@@ -73,6 +88,11 @@
enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
}
prop {
+ api_name: "refresh_rate_switching"
+ prop_name: "ro.surface_flinger.refresh_rate_switching"
+ deprecated: true
+ }
+ prop {
api_name: "running_without_sync_framework"
prop_name: "ro.surface_flinger.running_without_sync_framework"
}
@@ -100,16 +120,29 @@
prop_name: "ro.surface_flinger.support_kernel_idle_timer"
}
prop {
+ api_name: "supports_background_blur"
+ prop_name: "ro.surface_flinger.supports_background_blur"
+ }
+ prop {
api_name: "use_color_management"
prop_name: "ro.surface_flinger.use_color_management"
}
prop {
+ api_name: "use_content_detection_for_refresh_rate"
+ prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+ }
+ prop {
api_name: "use_context_priority"
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
+ api_name: "use_frame_rate_api"
+ prop_name: "ro.surface_flinger.use_frame_rate_api"
+ }
+ prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
+ deprecated: true
}
prop {
api_name: "use_vr_flinger"
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 38bc8a1..cbf264d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -365,6 +365,10 @@
auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+ auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ return mFlinger->onTransact(code, data, reply, flags);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
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});
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d3ed88d..6b51817 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -258,7 +258,11 @@
bool shared;
struct Image {
- Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
+ Image()
+ : image(VK_NULL_HANDLE),
+ dequeue_fence(-1),
+ release_fence(-1),
+ dequeued(false) {}
VkImage image;
android::sp<ANativeWindowBuffer> buffer;
// The fence is only valid when the buffer is dequeued, and should be
@@ -266,6 +270,10 @@
// closed: either by closing it explicitly when queueing the buffer,
// or by passing ownership e.g. to ANativeWindow::cancelBuffer().
int dequeue_fence;
+ // This fence is a dup of the sync fd returned from the driver via
+ // vkQueueSignalReleaseImageANDROID upon vkQueuePresentKHR. We must
+ // ensure it is closed upon re-presenting or releasing the image.
+ int release_fence;
bool dequeued;
} images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
@@ -280,10 +288,19 @@
return reinterpret_cast<Swapchain*>(handle);
}
+static bool IsFencePending(int fd) {
+ if (fd < 0)
+ return false;
+
+ errno = 0;
+ return sync_wait(fd, 0 /* timeout */) == -1 && errno == ETIME;
+}
+
void ReleaseSwapchainImage(VkDevice device,
ANativeWindow* window,
int release_fence,
- Swapchain::Image& image) {
+ Swapchain::Image& image,
+ bool defer_if_pending) {
ATRACE_CALL();
ALOG_ASSERT(release_fence == -1 || image.dequeued,
@@ -319,10 +336,18 @@
close(release_fence);
}
}
-
+ release_fence = -1;
image.dequeued = false;
}
+ if (defer_if_pending && IsFencePending(image.release_fence))
+ return;
+
+ if (image.release_fence >= 0) {
+ close(image.release_fence);
+ image.release_fence = -1;
+ }
+
if (image.image) {
ATRACE_BEGIN("DestroyImage");
GetData(device).driver.DestroyImage(device, image.image, nullptr);
@@ -338,7 +363,8 @@
return;
for (uint32_t i = 0; i < swapchain->num_images; i++) {
if (!swapchain->images[i].dequeued)
- ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+ ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i],
+ true);
}
swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
swapchain->timing.clear();
@@ -998,7 +1024,7 @@
}
for (uint32_t i = 0; i < swapchain->num_images; i++) {
- ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+ ReleaseSwapchainImage(device, window, -1, swapchain->images[i], false);
}
if (active) {
@@ -1630,6 +1656,9 @@
ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
swapchain_result = result;
}
+ if (img.release_fence >= 0)
+ close(img.release_fence);
+ img.release_fence = fence < 0 ? -1 : dup(fence);
if (swapchain.surface.swapchain_handle ==
present_info->pSwapchains[sc]) {
@@ -1763,7 +1792,7 @@
WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
}
} else {
- ReleaseSwapchainImage(device, nullptr, fence, img);
+ ReleaseSwapchainImage(device, nullptr, fence, img, true);
swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
}