installd: Unit test for RunDex2oat

Add unit tests run_dex2oat_test.

In order to make RunDex2oat testable,
 - RunDex2Oat is extracted from dexopt.cpp such that the class is
   visible to test
 - ExecVHelper is extracted from dexopt.cpp such that it can be used by
   RunDex2Oat and the others in the original file.
 - Main logic of RunDex2Oat is removed from constructor (otherwise
   virtual functions can't be overridden in derived class).

Bug: 161470356
Test: atest run_dex2oat_test
Test: atest frameworks/native/cmds/installd/tests
Test: cmd package compile -m speed -f com.android.egg

Change-Id: I416d57a008374a598def9160b65407ed043ab0eb
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 dfdcab8..2b36067 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -50,9 +50,11 @@
 
 #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;
@@ -68,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 {
@@ -187,82 +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, " ");
-}
-
-// 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,
@@ -301,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";
@@ -318,272 +231,6 @@
     return profile_boot_class_path == "true";
 }
 
-class RunDex2Oat : public ExecVHelper {
-  public:
-    RunDex2Oat(const char* dex2oat_bin,
-               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, 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.
-        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.
  *
@@ -841,6 +488,7 @@
                   /*for_boot_image*/false);
     }
 
+    using ExecVHelper::Exec;  // To suppress -Wno-overloaded-virtual
     void Exec() {
         ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
     }
@@ -2210,10 +1858,12 @@
         (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
         background_job_compile);
 
+    auto execv_helper = std::make_unique<ExecVHelper>();
+
     LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
 
-    RunDex2Oat runner(dex2oat_bin,
-                      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(),
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>