Loading cmds/installd/dexopt.cpp +153 −12 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ using android::base::EndsWith; using android::base::GetBoolProperty; using android::base::GetProperty; using android::base::ReadFdToString; using android::base::ReadFully; using android::base::StringPrintf; using android::base::WriteFully; Loading Loading @@ -319,6 +320,7 @@ class RunDex2Oat : public ExecVHelper { 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, Loading Loading @@ -423,9 +425,14 @@ class RunDex2Oat : public ExecVHelper { 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) { Loading Loading @@ -518,6 +525,7 @@ class RunDex2Oat : public ExecVHelper { AddArg(profile_arg); AddArg(base_dir); AddArg(class_loader_context_arg); AddArg(class_loader_context_fds_arg); if (generate_minidebug_info) { AddArg(kMinidebugDex2oatFlag); } Loading Loading @@ -1521,7 +1529,8 @@ class RunDexoptAnalyzer : public ExecVHelper { const std::string& compiler_filter, bool profile_was_updated, bool downgrade, const char* class_loader_context) { const char* class_loader_context, const std::string& class_loader_context_fds) { CHECK_GE(zip_fd, 0); // We always run the analyzer in the background job. Loading @@ -1540,6 +1549,10 @@ class RunDexoptAnalyzer : public ExecVHelper { if (class_loader_context != nullptr) { class_loader_context_arg += class_loader_context; } std::string class_loader_context_fds_arg = "--class-loader-context-fds="; if (!class_loader_context_fds.empty()) { class_loader_context_fds_arg += class_loader_context_fds; } // program name, dex file, isa, filter AddArg(dex_file_arg); Loading @@ -1560,8 +1573,25 @@ class RunDexoptAnalyzer : public ExecVHelper { } if (class_loader_context != nullptr) { AddArg(class_loader_context_arg.c_str()); if (!class_loader_context_fds.empty()) { AddArg(class_loader_context_fds_arg.c_str()); } } PrepareArgs(dexoptanalyzer_bin); } // Dexoptanalyzer mode which flattens the given class loader context and // prints a list of its dex files in that flattened order. RunDexoptAnalyzer(const char* class_loader_context) { CHECK(class_loader_context != nullptr); // We always run the analyzer in the background job. const char* dexoptanalyzer_bin = select_execution_binary( kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); AddArg("--flatten-class-loader-context"); AddArg(std::string("--class-loader-context=") + class_loader_context); PrepareArgs(dexoptanalyzer_bin); } }; Loading Loading @@ -1743,6 +1773,95 @@ static bool validate_dexopt_storage_flags(int dexopt_flags, return true; } static bool get_class_loader_context_dex_paths(const char* class_loader_context, int uid, /* out */ std::vector<std::string>* context_dex_paths) { if (class_loader_context == nullptr) { return true; } LOG(DEBUG) << "Getting dex paths for context " << class_loader_context; // Pipe to get the hash result back from our child process. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { PLOG(ERROR) << "Failed to create pipe"; return false; } pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); // Route stdout to `pipe_write` while ((dup2(pipe_write, STDOUT_FILENO) == -1) && (errno == EINTR)) {} pipe_write.reset(); pipe_read.reset(); RunDexoptAnalyzer run_dexopt_analyzer(class_loader_context); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } /* parent */ pipe_write.reset(); std::string str_dex_paths; if (!ReadFdToString(pipe_read, &str_dex_paths)) { PLOG(ERROR) << "Failed to read from pipe"; return false; } pipe_read.reset(); int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { PLOG(ERROR) << "Error waiting for child dexoptanalyzer process"; return false; } constexpr int kFlattenClassLoaderContextSuccess = 50; return_code = WEXITSTATUS(return_code); if (return_code != kFlattenClassLoaderContextSuccess) { LOG(ERROR) << "Dexoptanalyzer could not flatten class loader context, code=" << return_code; return false; } if (!str_dex_paths.empty()) { *context_dex_paths = android::base::Split(str_dex_paths, ":"); } return true; } static int open_dex_paths(const std::vector<std::string>& dex_paths, /* out */ std::vector<unique_fd>* zip_fds, /* out */ std::string* error_msg) { for (const std::string& dex_path : dex_paths) { zip_fds->emplace_back(open(dex_path.c_str(), O_RDONLY)); if (zip_fds->back().get() < 0) { *error_msg = StringPrintf( "installd cannot open '%s' for input during dexopt", dex_path.c_str()); if (errno == ENOENT) { return kSecondaryDexDexoptAnalyzerSkippedNoFile; } else { return kSecondaryDexDexoptAnalyzerSkippedOpenZip; } } } return 0; } static std::string join_fds(const std::vector<unique_fd>& fds) { std::stringstream ss; bool is_first = true; for (const unique_fd& fd : fds) { if (is_first) { is_first = false; } else { ss << ":"; } ss << fd.get(); } return ss.str(); } // Processes the dex_path as a secondary dex files and return true if the path dex file should // be compiled. Returns false for errors (logged) or true if the secondary dex path was process // successfully. Loading @@ -1754,7 +1873,7 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, const char* class_loader_context, /* out */ std::string* error_msg) { const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { Loading Loading @@ -1794,6 +1913,13 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char } } // Open class loader context dex files. std::vector<unique_fd> context_zip_fds; int open_dex_paths_rc = open_dex_paths(context_dex_paths, &context_zip_fds, error_msg); if (open_dex_paths_rc != 0) { _exit(open_dex_paths_rc); } // Prepare the oat directories. if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) { _exit(kSecondaryDexDexoptAnalyzerSkippedPrepareDir); Loading Loading @@ -1825,7 +1951,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char instruction_set, compiler_filter, profile_was_updated, downgrade, class_loader_context); class_loader_context, join_fds(context_zip_fds)); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } Loading Loading @@ -1913,10 +2040,16 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; std::vector<std::string> context_dex_paths; if (is_secondary_dex) { if (!get_class_loader_context_dex_paths(class_loader_context, uid, &context_dex_paths)) { *error_msg = "Failed acquiring context dex paths"; return -1; // We had an error, logged in the process method. } if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context, error_msg)) { downgrade, class_loader_context, context_dex_paths, error_msg)) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. Loading @@ -1928,7 +2061,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins return -1; // We had an error, logged in the process method. } } else { // Currently these flags are only use for secondary dex files. // Currently these flags are only used for secondary dex files. // Verify that they are not set for primary apks. CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0); CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0); Loading @@ -1942,6 +2075,13 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins return -1; } // Open class loader context dex files. std::vector<unique_fd> context_input_fds; if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) { LOG(ERROR) << *error_msg; return -1; } // Create the output OAT file. char out_oat_path[PKG_PATH_MAX]; Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid, Loading Loading @@ -2010,6 +2150,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins background_job_compile, reference_profile_fd.get(), class_loader_context, join_fds(context_input_fds), target_sdk_version, enable_hidden_api_checks, generate_compact_dex, Loading cmds/installd/tests/installd_dexopt_test.cpp +21 −2 Original line number Diff line number Diff line Loading @@ -327,16 +327,21 @@ protected: void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag, bool should_binder_call_succeed, bool should_dex_be_compiled = true, /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1) { /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1, const char* class_loader_context = nullptr) { if (uid == -1) { uid = kTestAppUid; } if (class_loader_context == nullptr) { class_loader_context = "&"; } std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); int32_t dexopt_needed = 0; // does not matter; std::unique_ptr<std::string> out_path = nullptr; // does not matter int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; std::string compiler_filter = "speed-profile"; std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); std::unique_ptr<std::string> class_loader_context_ptr( new std::string(class_loader_context)); std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default Loading Loading @@ -555,12 +560,26 @@ TEST_F(DexoptTest, DexoptSecondaryCeLink) { /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryCeWithContext) { LOG(INFO) << "DexoptSecondaryCeWithContext"; std::string class_loader_context = "PCL[" + secondary_dex_ce_ + "]"; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); } TEST_F(DexoptTest, DexoptSecondaryDe) { LOG(INFO) << "DexoptSecondaryDe"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryDeWithContext) { LOG(INFO) << "DexoptSecondaryDeWithContext"; std::string class_loader_context = "PCL[" + secondary_dex_de_ + "]"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); } TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) { LOG(INFO) << "DexoptSecondaryDoesNotExist"; // If the file validates but does not exist we do not treat it as an error. Loading Loading
cmds/installd/dexopt.cpp +153 −12 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ using android::base::EndsWith; using android::base::GetBoolProperty; using android::base::GetProperty; using android::base::ReadFdToString; using android::base::ReadFully; using android::base::StringPrintf; using android::base::WriteFully; Loading Loading @@ -319,6 +320,7 @@ class RunDex2Oat : public ExecVHelper { 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, Loading Loading @@ -423,9 +425,14 @@ class RunDex2Oat : public ExecVHelper { 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) { Loading Loading @@ -518,6 +525,7 @@ class RunDex2Oat : public ExecVHelper { AddArg(profile_arg); AddArg(base_dir); AddArg(class_loader_context_arg); AddArg(class_loader_context_fds_arg); if (generate_minidebug_info) { AddArg(kMinidebugDex2oatFlag); } Loading Loading @@ -1521,7 +1529,8 @@ class RunDexoptAnalyzer : public ExecVHelper { const std::string& compiler_filter, bool profile_was_updated, bool downgrade, const char* class_loader_context) { const char* class_loader_context, const std::string& class_loader_context_fds) { CHECK_GE(zip_fd, 0); // We always run the analyzer in the background job. Loading @@ -1540,6 +1549,10 @@ class RunDexoptAnalyzer : public ExecVHelper { if (class_loader_context != nullptr) { class_loader_context_arg += class_loader_context; } std::string class_loader_context_fds_arg = "--class-loader-context-fds="; if (!class_loader_context_fds.empty()) { class_loader_context_fds_arg += class_loader_context_fds; } // program name, dex file, isa, filter AddArg(dex_file_arg); Loading @@ -1560,8 +1573,25 @@ class RunDexoptAnalyzer : public ExecVHelper { } if (class_loader_context != nullptr) { AddArg(class_loader_context_arg.c_str()); if (!class_loader_context_fds.empty()) { AddArg(class_loader_context_fds_arg.c_str()); } } PrepareArgs(dexoptanalyzer_bin); } // Dexoptanalyzer mode which flattens the given class loader context and // prints a list of its dex files in that flattened order. RunDexoptAnalyzer(const char* class_loader_context) { CHECK(class_loader_context != nullptr); // We always run the analyzer in the background job. const char* dexoptanalyzer_bin = select_execution_binary( kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); AddArg("--flatten-class-loader-context"); AddArg(std::string("--class-loader-context=") + class_loader_context); PrepareArgs(dexoptanalyzer_bin); } }; Loading Loading @@ -1743,6 +1773,95 @@ static bool validate_dexopt_storage_flags(int dexopt_flags, return true; } static bool get_class_loader_context_dex_paths(const char* class_loader_context, int uid, /* out */ std::vector<std::string>* context_dex_paths) { if (class_loader_context == nullptr) { return true; } LOG(DEBUG) << "Getting dex paths for context " << class_loader_context; // Pipe to get the hash result back from our child process. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { PLOG(ERROR) << "Failed to create pipe"; return false; } pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); // Route stdout to `pipe_write` while ((dup2(pipe_write, STDOUT_FILENO) == -1) && (errno == EINTR)) {} pipe_write.reset(); pipe_read.reset(); RunDexoptAnalyzer run_dexopt_analyzer(class_loader_context); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } /* parent */ pipe_write.reset(); std::string str_dex_paths; if (!ReadFdToString(pipe_read, &str_dex_paths)) { PLOG(ERROR) << "Failed to read from pipe"; return false; } pipe_read.reset(); int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { PLOG(ERROR) << "Error waiting for child dexoptanalyzer process"; return false; } constexpr int kFlattenClassLoaderContextSuccess = 50; return_code = WEXITSTATUS(return_code); if (return_code != kFlattenClassLoaderContextSuccess) { LOG(ERROR) << "Dexoptanalyzer could not flatten class loader context, code=" << return_code; return false; } if (!str_dex_paths.empty()) { *context_dex_paths = android::base::Split(str_dex_paths, ":"); } return true; } static int open_dex_paths(const std::vector<std::string>& dex_paths, /* out */ std::vector<unique_fd>* zip_fds, /* out */ std::string* error_msg) { for (const std::string& dex_path : dex_paths) { zip_fds->emplace_back(open(dex_path.c_str(), O_RDONLY)); if (zip_fds->back().get() < 0) { *error_msg = StringPrintf( "installd cannot open '%s' for input during dexopt", dex_path.c_str()); if (errno == ENOENT) { return kSecondaryDexDexoptAnalyzerSkippedNoFile; } else { return kSecondaryDexDexoptAnalyzerSkippedOpenZip; } } } return 0; } static std::string join_fds(const std::vector<unique_fd>& fds) { std::stringstream ss; bool is_first = true; for (const unique_fd& fd : fds) { if (is_first) { is_first = false; } else { ss << ":"; } ss << fd.get(); } return ss.str(); } // Processes the dex_path as a secondary dex files and return true if the path dex file should // be compiled. Returns false for errors (logged) or true if the secondary dex path was process // successfully. Loading @@ -1754,7 +1873,7 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, const char* class_loader_context, /* out */ std::string* error_msg) { const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { Loading Loading @@ -1794,6 +1913,13 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char } } // Open class loader context dex files. std::vector<unique_fd> context_zip_fds; int open_dex_paths_rc = open_dex_paths(context_dex_paths, &context_zip_fds, error_msg); if (open_dex_paths_rc != 0) { _exit(open_dex_paths_rc); } // Prepare the oat directories. if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) { _exit(kSecondaryDexDexoptAnalyzerSkippedPrepareDir); Loading Loading @@ -1825,7 +1951,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char instruction_set, compiler_filter, profile_was_updated, downgrade, class_loader_context); class_loader_context, join_fds(context_zip_fds)); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } Loading Loading @@ -1913,10 +2040,16 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; std::vector<std::string> context_dex_paths; if (is_secondary_dex) { if (!get_class_loader_context_dex_paths(class_loader_context, uid, &context_dex_paths)) { *error_msg = "Failed acquiring context dex paths"; return -1; // We had an error, logged in the process method. } if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context, error_msg)) { downgrade, class_loader_context, context_dex_paths, error_msg)) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. Loading @@ -1928,7 +2061,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins return -1; // We had an error, logged in the process method. } } else { // Currently these flags are only use for secondary dex files. // Currently these flags are only used for secondary dex files. // Verify that they are not set for primary apks. CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0); CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0); Loading @@ -1942,6 +2075,13 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins return -1; } // Open class loader context dex files. std::vector<unique_fd> context_input_fds; if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) { LOG(ERROR) << *error_msg; return -1; } // Create the output OAT file. char out_oat_path[PKG_PATH_MAX]; Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid, Loading Loading @@ -2010,6 +2150,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins background_job_compile, reference_profile_fd.get(), class_loader_context, join_fds(context_input_fds), target_sdk_version, enable_hidden_api_checks, generate_compact_dex, Loading
cmds/installd/tests/installd_dexopt_test.cpp +21 −2 Original line number Diff line number Diff line Loading @@ -327,16 +327,21 @@ protected: void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag, bool should_binder_call_succeed, bool should_dex_be_compiled = true, /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1) { /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1, const char* class_loader_context = nullptr) { if (uid == -1) { uid = kTestAppUid; } if (class_loader_context == nullptr) { class_loader_context = "&"; } std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); int32_t dexopt_needed = 0; // does not matter; std::unique_ptr<std::string> out_path = nullptr; // does not matter int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; std::string compiler_filter = "speed-profile"; std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); std::unique_ptr<std::string> class_loader_context_ptr( new std::string(class_loader_context)); std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default Loading Loading @@ -555,12 +560,26 @@ TEST_F(DexoptTest, DexoptSecondaryCeLink) { /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryCeWithContext) { LOG(INFO) << "DexoptSecondaryCeWithContext"; std::string class_loader_context = "PCL[" + secondary_dex_ce_ + "]"; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); } TEST_F(DexoptTest, DexoptSecondaryDe) { LOG(INFO) << "DexoptSecondaryDe"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryDeWithContext) { LOG(INFO) << "DexoptSecondaryDeWithContext"; std::string class_loader_context = "PCL[" + secondary_dex_de_ + "]"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); } TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) { LOG(INFO) << "DexoptSecondaryDoesNotExist"; // If the file validates but does not exist we do not treat it as an error. Loading