Merge "Initial Binder Rust crate"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ec97f1b..e2884e5 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -76,6 +76,7 @@
#include <hardware_legacy/power.h>
#include <hidl/ServiceManagement.h>
#include <log/log.h>
+#include <log/log_read.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -634,14 +635,24 @@
static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000;
-/* timeout in ms to read a list of buffers */
+// Returns the actual readable size of the given buffer or -1 on error.
+static long logcat_buffer_readable_size(const std::string& buffer) {
+ std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+ android_logger_list_alloc(0, 0, 0), &android_logger_list_free};
+ auto logger = android_logger_open(logger_list.get(), android_name_to_log_id(buffer.c_str()));
+
+ return android_logger_get_log_readable_size(logger);
+}
+
+// Returns timeout in ms to read a list of buffers.
static unsigned long logcat_timeout(const std::vector<std::string>& buffers) {
unsigned long timeout_ms = 0;
for (const auto& buffer : buffers) {
- log_id_t id = android_name_to_log_id(buffer.c_str());
- unsigned long property_size = __android_logger_get_buffer_size(id);
- /* Engineering margin is ten-fold our guess */
- timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf;
+ long readable_size = logcat_buffer_readable_size(buffer);
+ if (readable_size > 0) {
+ // Engineering margin is ten-fold our guess.
+ timeout_ms += 10 * (readable_size + worst_write_perf) / worst_write_perf;
+ }
}
return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
}
@@ -1226,12 +1237,12 @@
static void DumpHals() {
if (!ds.IsZipping()) {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
return;
}
DurationReporter duration_reporter("DUMP HALS");
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
using android::hidl::manager::V1_0::IServiceManager;
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 75dec37..f7f8a93 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -16,7 +16,9 @@
"InstalldNativeService.cpp",
"QuotaUtils.cpp",
"dexopt.cpp",
+ "execv_helper.cpp",
"globals.cpp",
+ "run_dex2oat.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
@@ -102,6 +104,30 @@
}
//
+// Unit tests
+//
+
+cc_test_host {
+ name: "run_dex2oat_test",
+ test_suites: ["general-tests"],
+ clang: true,
+ srcs: [
+ "run_dex2oat_test.cpp",
+ "run_dex2oat.cpp",
+ "execv_helper.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ //"libinstalld",
+ ],
+ test_config: "run_dex2oat_test.xml",
+}
+
+//
// Executable
//
@@ -203,9 +229,11 @@
srcs: [
"dexopt.cpp",
+ "execv_helper.cpp",
"globals.cpp",
"otapreopt.cpp",
"otapreopt_utils.cpp",
+ "run_dex2oat.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
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..2b36067 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -50,11 +50,14 @@
#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 "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,289 +231,6 @@
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);
- }
-};
-
/*
* Whether dexopt should use a swap file when compiling an APK.
*
@@ -868,6 +488,7 @@
/*for_boot_image*/false);
}
+ using ExecVHelper::Exec; // To suppress -Wno-overloaded-virtual
void Exec() {
ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
}
@@ -1022,7 +643,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));
@@ -2220,9 +1841,29 @@
}
}
+ 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(),
+ RunDex2Oat runner(dex2oat_bin, execv_helper.get());
+ runner.Initialize(input_fd.get(),
out_oat_fd.get(),
in_vdex_fd.get(),
out_vdex_fd.get(),
@@ -2235,7 +1876,6 @@
debuggable,
boot_complete,
for_restore,
- background_job_compile,
reference_profile_fd.get(),
class_loader_context,
join_fds(context_input_fds),
@@ -2243,6 +1883,7 @@
enable_hidden_api_checks,
generate_compact_dex,
dex_metadata_fd.get(),
+ use_jitzygote_image,
compilation_reason);
pid_t pid = fork();
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/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
new file mode 100644
index 0000000..8cac58f
--- /dev/null
+++ b/cmds/installd/run_dex2oat.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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>
+
+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(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,
+ 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,
+ bool use_jitzygote_image,
+ const char* compilation_reason) {
+ // Get the relative path to the input file.
+ std::string input_basename = Basename(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");
+
+ bool generate_minidebug_info = kEnableMinidebugInfo &&
+ GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
+
+ 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");
+ }
+
+ // 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", input_basename.c_str());
+ 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_.c_str(), input_basename.c_str(),
+ 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.
+ for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) {
+ AddArg(*it);
+ }
+
+ execv_helper_->PrepareArgs(dex2oat_bin_);
+}
+
+RunDex2Oat::~RunDex2Oat() {}
+
+void RunDex2Oat::Exec(int exit_code) {
+ LOG(ERROR) << "RunDex2Oat::Exec";
+ 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..e4450b0
--- /dev/null
+++ b/cmds/installd/run_dex2oat.h
@@ -0,0 +1,82 @@
+/*
+ * 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 RunDex2Oat {
+ public:
+ explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper);
+ virtual ~RunDex2Oat();
+
+ void Initialize(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,
+ 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,
+ bool use_jitzygote_image,
+ const char* compilation_reason);
+
+ void Exec(int exit_code);
+
+ protected:
+ 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..b1f429d
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -0,0 +1,588 @@
+/*
+ * 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"
+
+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}}";
+
+ static constexpr int ZIP_FD = 1;
+ static constexpr int OAT_FD = 2;
+ static constexpr int INPUT_VDEX_FD = 3;
+ static constexpr int OUTPUT_VDEX_FD = 4;
+ static constexpr int IMAGE_FD = 5;
+ static constexpr int PROFILE_FD = 6;
+ static constexpr int DEX_METADATA_FD = 7;
+ static constexpr int SWAP_FD = 8;
+
+ 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_file_name = INPUT_PATH;
+ args->zip_fd = ZIP_FD;
+ args->output_file_name = OUTPUT_PATH;
+ args->oat_fd = OAT_FD;
+ args->input_vdex_fd = INPUT_VDEX_FD;
+ args->output_vdex_fd = OUTPUT_VDEX_FD;
+ args->instruction_set = "arm64";
+ args->compilation_reason = "rundex2oattest";
+ return args;
+ }
+
+ int zip_fd = -1;
+ int oat_fd = -1;
+ int input_vdex_fd = -1;
+ int output_vdex_fd = -1;
+ int image_fd = -1;
+ const char* input_file_name = nullptr;
+ const char* output_file_name = nullptr;
+ 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;
+ int profile_fd = -1;
+ 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;
+ int dex_metadata_fd = -1;
+ 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;
+ }
+ };
+
+ 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->zip_fd,
+ args->oat_fd,
+ args->input_vdex_fd,
+ args->output_vdex_fd,
+ args->image_fd,
+ args->input_file_name,
+ args->output_file_name,
+ args->swap_fd,
+ args->instruction_set,
+ args->compiler_filter,
+ args->debuggable,
+ args->post_bootcomplete,
+ args->for_restore,
+ args->profile_fd,
+ args->class_loader_context,
+ args->class_loader_context_fds,
+ args->target_sdk_version,
+ args->enable_hidden_api_checks,
+ args->generate_compact_dex,
+ args->dex_metadata_fd,
+ 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->image_fd = IMAGE_FD;
+ args->profile_fd = PROFILE_FD;
+ 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_fd = INPUT_VDEX_FD;
+ args->output_vdex_fd = INPUT_VDEX_FD;
+ 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/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a805a48..92958d9 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -916,6 +916,18 @@
}
}
+// Get all values of enum type T, assuming the first value is 0 and the last value is T::LAST.
+// T::LAST is not included in the returned list.
+template <typename T>
+std::vector<T> GetAllValues() {
+ using BaseType = std::underlying_type_t<T>;
+ std::vector<T> ret;
+ for (BaseType i = 0; i < static_cast<BaseType>(T::LAST); ++i) {
+ ret.push_back(static_cast<T>(i));
+ }
+ return ret;
+}
+
void ListCommand::registerAllOptions() {
int v = mOptions.size();
// A list of acceptable command line options
@@ -989,6 +1001,15 @@
" - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n"
" - N/A: no information for passthrough HALs."});
+ mOptions.push_back({'A', "all", no_argument, v++,
+ [](ListCommand* thiz, const char*) {
+ auto allColumns = GetAllValues<TableColumnType>();
+ thiz->mSelectedColumns.insert(thiz->mSelectedColumns.end(),
+ allColumns.begin(), allColumns.end());
+ return OK;
+ },
+ "print all columns"});
+
// long options without short alternatives
mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
thiz->mVintf = true;
@@ -1019,46 +1040,55 @@
thiz->mNeat = true;
return OK;
}, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
- mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) {
- if (!arg) { return USAGE; }
+ mOptions.push_back(
+ {'\0', "types", required_argument, v++,
+ [](ListCommand* thiz, const char* arg) {
+ if (!arg) {
+ return USAGE;
+ }
- static const std::map<std::string, HalType> kHalTypeMap {
- {"binderized", HalType::BINDERIZED_SERVICES},
- {"b", HalType::BINDERIZED_SERVICES},
- {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS},
- {"c", HalType::PASSTHROUGH_CLIENTS},
- {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES},
- {"l", HalType::PASSTHROUGH_LIBRARIES},
- {"vintf", HalType::VINTF_MANIFEST},
- {"v", HalType::VINTF_MANIFEST},
- {"lazy", HalType::LAZY_HALS},
- {"z", HalType::LAZY_HALS},
- };
+ static const std::map<std::string, std::vector<HalType>> kHalTypeMap{
+ {"binderized", {HalType::BINDERIZED_SERVICES}},
+ {"b", {HalType::BINDERIZED_SERVICES}},
+ {"passthrough_clients", {HalType::PASSTHROUGH_CLIENTS}},
+ {"c", {HalType::PASSTHROUGH_CLIENTS}},
+ {"passthrough_libs", {HalType::PASSTHROUGH_LIBRARIES}},
+ {"l", {HalType::PASSTHROUGH_LIBRARIES}},
+ {"vintf", {HalType::VINTF_MANIFEST}},
+ {"v", {HalType::VINTF_MANIFEST}},
+ {"lazy", {HalType::LAZY_HALS}},
+ {"z", {HalType::LAZY_HALS}},
+ {"all", GetAllValues<HalType>()},
+ {"a", GetAllValues<HalType>()},
+ };
- std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
- for (const auto& halTypeArg : halTypesArgs) {
- if (halTypeArg.empty()) continue;
+ std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
+ for (const auto& halTypeArg : halTypesArgs) {
+ if (halTypeArg.empty()) continue;
- const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
- if (halTypeIter == kHalTypeMap.end()) {
+ const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
+ if (halTypeIter == kHalTypeMap.end()) {
+ thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
+ return USAGE;
+ }
- thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
- return USAGE;
- }
+ // Append unique (non-repeated) HAL types to the reporting list
+ for (auto halType : halTypeIter->second) {
+ if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
+ thiz->mListTypes.end()) {
+ thiz->mListTypes.push_back(halType);
+ }
+ }
+ }
- // Append unique (non-repeated) HAL types to the reporting list
- HalType halType = halTypeIter->second;
- if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
- thiz->mListTypes.end()) {
- thiz->mListTypes.push_back(halType);
- }
- }
-
- if (thiz->mListTypes.empty()) { return USAGE; }
- return OK;
- }, "comma-separated list of one or more sections.\nThe output is restricted to the selected "
- "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
- "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
+ if (thiz->mListTypes.empty()) {
+ return USAGE;
+ }
+ return OK;
+ },
+ "comma-separated list of one or more sections.\nThe output is restricted to the "
+ "selected section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
+ "passthrough_libs), (v|vintf), (z|lazy), and (a|all).\nDefault is `b,c,l`."});
}
// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index acc0dcf..412aadd 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -52,6 +52,9 @@
PASSTHROUGH_LIBRARIES,
VINTF_MANIFEST,
LAZY_HALS,
+
+ // Not a real HalType. Used to determine all HalTypes.
+ LAST,
};
class ListCommand : public Command {
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 0ff0c96..3c36813 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -35,19 +35,25 @@
using Pids = std::vector<int32_t>;
enum class TableColumnType : unsigned int {
- INTERFACE_NAME,
+ INTERFACE_NAME = 0,
TRANSPORT,
SERVER_PID,
- SERVER_CMD,
SERVER_ADDR,
- CLIENT_PIDS,
- CLIENT_CMDS,
ARCH,
THREADS,
RELEASED,
HASH,
VINTF,
SERVICE_STATUS,
+ CLIENT_PIDS,
+
+ // Not a real TableColumnType. Used to determine all TableColumnTypes.
+ LAST,
+
+ // Not included in all TableColumnTypes because they replace *PID(S) when the
+ // --cmdline option is set.
+ SERVER_CMD,
+ CLIENT_CMDS,
};
enum : unsigned int {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index afe5d63..9964888 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -708,8 +708,8 @@
TEST_F(ListTest, UnknownHalType) {
optind = 1; // mimic Lshal::parseArg()
- EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"})));
- EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a"));
+ EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,r"})));
+ EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: r"));
}
TEST_F(ListTest, Vintf) {
@@ -793,6 +793,71 @@
EXPECT_EQ("", err.str());
}
+TEST_F(ListTest, AllColumns) {
+ // clang-format off
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive 3 5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough N/A N/A 32 N/A ? X N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough N/A N/A 32 N/A ? X N/A 5 7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough N/A N/A 32 N/A ? X N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough N/A N/A 32 N/A ? X N/A 7 9\n"
+ "\n";
+ // clang-format on
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--all"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllColumnsWithCmd) {
+ // clang-format off
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder command_line_1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive command_line_2;command_line_4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder command_line_2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive command_line_3;command_line_5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough N/A 32 N/A ? X N/A command_line_4;command_line_6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough N/A 32 N/A ? X N/A command_line_5;command_line_7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough N/A 32 N/A ? X N/A command_line_6;command_line_8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough N/A 32 N/A ? X N/A command_line_7;command_line_9\n"
+ "\n";
+ // clang-format on
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Am"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllSections) {
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=all"})));
+ using HalTypeBase = std::underlying_type_t<HalType>;
+ for (HalTypeBase i = 0; i < static_cast<HalTypeBase>(HalType::LAST); ++i) {
+ EXPECT_THAT(out.str(), HasSubstr("[fake description " + std::to_string(i) + "]"));
+ }
+ EXPECT_THAT(out.str(),
+ Not(HasSubstr("[fake description " +
+ std::to_string(static_cast<HalTypeBase>(HalType::LAST)) + "]")));
+ EXPECT_EQ("", err.str());
+}
+
// Fake service returned by mocked IServiceManager::get for DumpDebug.
// The interfaceChain and getHashChain functions returns
// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 1f9892a..e80c321 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -213,17 +213,18 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- auto entry = mNameToService.emplace(name, Service {
+ // Overwrite the old service if it exists
+ mNameToService[name] = Service {
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.debugPid = ctx.debugPid,
- });
+ };
auto it = mNameToRegistrationCallback.find(name);
if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
- entry.first->second.guaranteeClient = true;
+ mNameToService[name].guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 25245be..fb9f9df 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -135,6 +135,26 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
}
+TEST(AddService, OverwriteExistingService) {
+ auto sm = getPermissiveServiceManager();
+ sp<IBinder> serviceA = getBinder();
+ EXPECT_TRUE(sm->addService("foo", serviceA, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> outA;
+ EXPECT_TRUE(sm->getService("foo", &outA).isOk());
+ EXPECT_EQ(serviceA, outA);
+
+ // serviceA should be overwritten by serviceB
+ sp<IBinder> serviceB = getBinder();
+ EXPECT_TRUE(sm->addService("foo", serviceB, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> outB;
+ EXPECT_TRUE(sm->getService("foo", &outB).isOk());
+ EXPECT_EQ(serviceB, outB);
+}
+
TEST(AddService, NoPermissions) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 6d711bc..d18c88e 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -26,7 +26,19 @@
class ClientCounterCallback;
} // namespace internal
-/** Exits when all services registered through this object have 0 clients */
+/**
+ * Exits when all services registered through this object have 0 clients
+ *
+ * In order to use this class, it's expected that your service:
+ * - registers all services in the process with this API
+ * - configures services as oneshot in init .rc files
+ * - configures services as disabled in init.rc files, unless a client is
+ * guaranteed early in boot, in which case, forcePersist should also be used
+ * to avoid races.
+ * - uses 'interface' declarations in init .rc files
+ *
+ * For more information on init .rc configuration, see system/core/init/README.md
+ **/
class LazyServiceRegistrar {
public:
static LazyServiceRegistrar& getInstance();
@@ -47,4 +59,4 @@
};
} // namespace binder
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 33e4586..f44ce0c 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -88,13 +88,21 @@
static void operator delete(void* p) { std::free(p); }
+ // Once minSdkVersion is 30, we are guaranteed to be building with the
+ // Android 11 AIDL compiler which supports the SharedRefBase::make API.
+ //
+ // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
+ // ownership. Making this operator private to avoid double-ownership.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30
+ private:
+#else
+ [[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
+#endif
+ static void* operator new(size_t s) { return std::malloc(s); }
+
private:
std::once_flag mFlagThis;
std::weak_ptr<SharedRefBase> mThis;
-
- // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
- // ownership. Making this operator private to avoid double-ownership.
- static void* operator new(size_t s) { return std::malloc(s); }
};
/**
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 055c79b..52bcd20 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,6 +18,7 @@
#include <android/binder_ibinder.h>
#include <android/binder_status.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -50,4 +51,47 @@
*/
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+/**
+ * Registers a lazy service with the default service manager under the 'instance' name.
+ * Does not take ownership of binder.
+ * The service must be configured statically with init so it can be restarted with
+ * ctl.interface.* messages from servicemanager.
+ * AServiceManager_registerLazyService cannot safely be used with AServiceManager_addService
+ * in the same process. If one service is registered with AServiceManager_registerLazyService,
+ * the entire process will have its lifetime controlled by servicemanager.
+ * Instead, all services in the process should be registered using
+ * AServiceManager_registerLazyService.
+ *
+ * \param binder object to register globally with the service manager.
+ * \param instance identifier of the service. This will be used to lookup the service.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance)
+ __INTRODUCED_IN(31);
+
+/**
+ * Gets a binder object with this specific instance name. Efficiently waits for the service.
+ * If the service is not declared, it will wait indefinitely. Requires the threadpool
+ * to be started in the service.
+ * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
+ * for calling AIBinder_decStrong).
+ *
+ * \param instance identifier of the service used to lookup the service.
+ *
+ * \return service if registered, null if not.
+ */
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(const char* instance)
+ __INTRODUCED_IN(31);
+
+/**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * \param instance identifier of the service.
+ *
+ * \return true on success, meaning AServiceManager_waitForService should always
+ * be able to return the service.
+ */
+bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index d435382..1701fb5 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -117,6 +117,9 @@
ABinderProcess_setupPolling; # apex
AIBinder_getCallingSid; # apex
AIBinder_setRequestingSid; # apex
+ AServiceManager_isDeclared; # apex llndk
+ AServiceManager_registerLazyService; # llndk
+ AServiceManager_waitForService; # apex llndk
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index d0b166d..6b2184e 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -20,6 +20,7 @@
#include "status_internal.h"
#include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
using ::android::defaultServiceManager;
using ::android::IBinder;
@@ -61,3 +62,33 @@
AIBinder_incStrong(ret.get());
return ret.get();
}
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance) {
+ if (binder == nullptr || instance == nullptr) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+
+ auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
+ status_t status = serviceRegistrar.registerService(binder->getBinder(), instance);
+
+ return PruneStatusT(status);
+}
+AIBinder* AServiceManager_waitForService(const char* instance) {
+ if (instance == nullptr) {
+ return nullptr;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->waitForService(String16(instance));
+
+ sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder_incStrong(ret.get());
+ return ret.get();
+}
+bool AServiceManager_isDeclared(const char* instance) {
+ if (instance == nullptr) {
+ return false;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ return sm->isDeclared(String16(instance));
+}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 9b2fcf0..b2c412d 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -43,6 +43,7 @@
constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) {
@@ -143,11 +144,31 @@
return 1;
}
-// This is too slow
-// TEST(NdkBinder, GetServiceThatDoesntExist) {
-// sp<IFoo> foo = IFoo::getService("asdfghkl;");
-// EXPECT_EQ(nullptr, foo.get());
-// }
+int lazyService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ // Wait to register this service to make sure the main test process will
+ // actually wait for the service to be available. Tested with sleep(60),
+ // and reduced for sake of time.
+ sleep(1);
+ // Strong reference to MyBinderNdkUnitTest kept by service manager.
+ // This is just for testing, it has no corresponding init behavior.
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ auto binder = service->asBinder();
+
+ binder_status_t status = AServiceManager_registerLazyService(binder.get(), instance);
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+TEST(NdkBinder, GetServiceThatDoesntExist) {
+ sp<IFoo> foo = IFoo::getService("asdfghkl;");
+ EXPECT_EQ(nullptr, foo.get());
+}
TEST(NdkBinder, CheckServiceThatDoesntExist) {
AIBinder* binder = AServiceManager_checkService("asdfghkl;");
@@ -171,6 +192,33 @@
EXPECT_EQ(2, out);
}
+TEST(NdkBinder, GetLazyService) {
+ // Not declared in the vintf manifest
+ ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService));
+ ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+ ASSERT_NE(service, nullptr);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+// This is too slow
+TEST(NdkBinder, CheckLazyServiceShutDown) {
+ ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+ ASSERT_NE(service, nullptr);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+ binder = nullptr;
+ service = nullptr;
+ IPCThreadState::self()->flushCommands();
+ // Make sure the service is dead after some time of no use
+ sleep(10);
+ ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService));
+}
+
void LambdaOnDeath(void* cookie) {
auto onDeath = static_cast<std::function<void(void)>*>(cookie);
(*onDeath)();
@@ -477,6 +525,10 @@
}
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return lazyService(kLazyBinderNdkUnitTestService);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
return generatedService();
}
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index c00fbba..b44456b 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -1,2 +1,4 @@
+alecmouri@google.com
+jreck@google.com
lpy@google.com
stoza@google.com
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 203a739..b1317b1 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,6 +1,7 @@
+alecmouri@google.com
chrisforbes@google.com
+jreck@google.com
lpy@google.com
mathias@google.com
romainguy@google.com
stoza@google.com
-vhau@google.com
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index 7b3717e..07e2121 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -308,14 +308,6 @@
class DvrDisplayManagerTest : public Test {
protected:
void SetUp() override {
- // dvr display manager test doesn't apply to standalone vr devices because
- // tests cannot create display manager client on these devices.
- if (property_get_bool("ro.boot.vr", false)) {
- GTEST_SKIP()
- << "All tests in DvrDisplayManagerTest test case are skipped "
- "because the device boot to VR.";
- }
-
int ret;
DvrDisplayManager* display_manager;
DvrSurfaceState* surface_state;
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index e1240d6..57a77cf 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -47,7 +47,6 @@
namespace {
const char kDvrPerformanceProperty[] = "sys.dvr.performance";
-const char kDvrStandaloneProperty[] = "ro.boot.vr";
const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
@@ -147,8 +146,6 @@
return false;
}
- is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false);
-
request_display_callback_ = request_display_callback;
primary_display_ = GetDisplayParams(composer, primary_display_id, true);
@@ -192,8 +189,6 @@
return;
boot_finished_ = true;
post_thread_wait_.notify_one();
- if (is_standalone_device_)
- request_display_callback_(true);
}
// Update the post thread quiescent state based on idle and suspended inputs.
@@ -253,17 +248,11 @@
layers_.clear();
// Phones create a new composer client on resume and destroy it on pause.
- // Standalones only create the composer client once and then use SetPowerMode
- // to control the screen on pause/resume.
- if (!is_standalone_device_) {
- if (composer_callback_ != nullptr) {
- composer_callback_->SetVsyncService(nullptr);
- composer_callback_ = nullptr;
- }
- composer_.reset(nullptr);
- } else {
- EnableDisplay(*target_display_, false);
+ if (composer_callback_ != nullptr) {
+ composer_callback_->SetVsyncService(nullptr);
+ composer_callback_ = nullptr;
}
+ composer_.reset(nullptr);
// Trigger target-specific performance mode change.
property_set(kDvrPerformanceProperty, "idle");
@@ -574,7 +563,7 @@
surfaces_changed_ = true;
}
- if (request_display_callback_ && !is_standalone_device_)
+ if (request_display_callback_)
request_display_callback_(!display_idle);
// Set idle state based on whether there are any surfaces to handle.
@@ -759,28 +748,6 @@
VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
- if (is_standalone_device_) {
- // First, wait until boot finishes.
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- if (PostThreadCondWait(lock, -1, [this] { return boot_finished_; })) {
- return;
- }
-
- // Then, wait until we're either leaving the quiescent state, or the boot
- // finished display off timeout expires.
- if (PostThreadCondWait(lock, kBootFinishedDisplayOffTimeoutSec,
- [this] { return !post_thread_quiescent_; })) {
- return;
- }
-
- LOG_ALWAYS_FATAL_IF(post_thread_state_ & PostThreadState::Suspended,
- "Vr flinger should own the display by now.");
- post_thread_resumed_ = true;
- post_thread_ready_.notify_all();
- if (!composer_)
- CreateComposer();
- }
-
while (1) {
ATRACE_NAME("HardwareComposer::PostThread");
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index 7075e88..3b449f2 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -42,9 +42,6 @@
// completed.
constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
-// How long to wait for a device that boots to VR to have vr flinger ready.
-constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30);
-
// A Binder connection to surface flinger.
class SurfaceFlingerConnection {
public:
@@ -154,11 +151,6 @@
return;
}
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
auto surface_flinger_connection = SurfaceFlingerConnection::Create();
ASSERT_NE(surface_flinger_connection, nullptr);
@@ -231,31 +223,5 @@
SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
}
-// This test runs only on devices that boot to vr. Such a device should boot to
-// a state where vr flinger is running, and the test verifies this after a
-// delay.
-TEST(BootVrFlingerTest, BootsToVrFlinger) {
- // Exit if we are not running on a device that boots to vr.
- if (!property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
- auto surface_flinger_connection = SurfaceFlingerConnection::Create();
- ASSERT_NE(surface_flinger_connection, nullptr);
-
- // Verify that vr flinger is enabled.
- ASSERT_TRUE(surface_flinger_connection->IsAlive());
- auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
- ASSERT_TRUE(vr_flinger_active.has_value());
-
- bool active_value = vr_flinger_active.value();
- if (!active_value) {
- // Try again, but delay up to 30 seconds.
- ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true,
- kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout),
- SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
- }
-}
-
} // namespace dvr
} // namespace android