Fix odrefresh partial compilation.
When some artifacts are newly generated while some existing artifacts
are kept, odsign will try to sign all the artifacts, including the
existing ones. The signing will fail because the signatures for the
existing artifacts already exist in fs-verity, and odsign will remove
all artifacts when the signing fails.
This CL works around the problem by reading each existing artifact into
the memory, deleting it, and writing it back, which essentially removes
them from fs-verity so that odsign can sign them again as if they are
newly generated.
Note: This CL add a new dependency "libc++fs" to the ART module, which
increases the size of the ART APEX by 40KB (or 0.1%).
Bug: 205276874
Test: atest odsign_e2e_tests
Change-Id: I0003c8502977d74c6dadf958b2e7e558220bec81
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index 64859e3..b527bbf 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -47,7 +47,10 @@
"libbase",
"liblog",
],
- static_libs: ["libtinyxml2"],
+ static_libs: [
+ "libc++fs",
+ "libtinyxml2",
+ ],
tidy: true,
tidy_flags: [
"-format-style=file",
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index 0cebd46..1798095 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -17,6 +17,7 @@
#ifndef ART_ODREFRESH_ODR_CONFIG_H_
#define ART_ODREFRESH_ODR_CONFIG_H_
+#include <optional>
#include <string>
#include <vector>
@@ -56,6 +57,8 @@
std::string dex2oat_;
std::string dex2oat_boot_classpath_;
bool dry_run_;
+ std::optional<bool> refresh_;
+ std::optional<bool> partial_compilation_;
InstructionSet isa_;
std::string program_name_;
std::string system_server_classpath_;
@@ -128,6 +131,12 @@
}
bool GetDryRun() const { return dry_run_; }
+ bool GetPartialCompilation() const {
+ return partial_compilation_.value_or(true);
+ }
+ bool GetRefresh() const {
+ return refresh_.value_or(true);
+ }
const std::string& GetSystemServerClasspath() const {
return system_server_classpath_;
}
@@ -151,6 +160,12 @@
}
void SetDryRun() { dry_run_ = true; }
+ void SetPartialCompilation(bool value) {
+ partial_compilation_ = value;
+ }
+ void SetRefresh(bool value) {
+ refresh_ = value;
+ }
void SetIsa(const InstructionSet isa) { isa_ = isa; }
void SetCompilationOsAddress(int address) { compilation_os_address_ = address; }
void SetMaxExecutionSeconds(int seconds) { max_execution_seconds_ = seconds; }
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index c0b9e1f..e92e60c 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -30,7 +30,9 @@
#include <algorithm>
#include <cstdarg>
#include <cstdint>
+#include <cstdio>
#include <cstdlib>
+#include <filesystem>
#include <fstream>
#include <initializer_list>
#include <iosfwd>
@@ -42,17 +44,24 @@
#include <sstream>
#include <string>
#include <string_view>
+#include <system_error>
#include <type_traits>
#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
+#include "aidl/com/android/art/CompilerFilter.h"
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+#include "aidl/com/android/art/Isa.h"
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "android-base/parseint.h"
#include "android-base/properties.h"
#include "android-base/result.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android/log.h"
@@ -61,6 +70,7 @@
#include "base/globals.h"
#include "base/macros.h"
#include "base/os.h"
+#include "base/stl_util.h"
#include "base/string_view_cpp20.h"
#include "base/unix_file/fd_file.h"
#include "com_android_apex.h"
@@ -81,11 +91,6 @@
#include "palette/palette.h"
#include "palette/palette_types.h"
-#include "aidl/com/android/art/CompilerFilter.h"
-#include "aidl/com/android/art/DexoptBcpExtArgs.h"
-#include "aidl/com/android/art/DexoptSystemServerArgs.h"
-#include "aidl/com/android/art/Isa.h"
-
namespace art {
namespace odrefresh {
@@ -103,6 +108,8 @@
// Name of cache info file in the ART Apex artifact cache.
constexpr const char* kCacheInfoFile = "cache-info.xml";
+constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+
void EraseFiles(const std::vector<std::unique_ptr<File>>& files) {
for (auto& file : files) {
file->Erase(/*unlink=*/true);
@@ -132,7 +139,6 @@
return false;
}
- static constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
if (fchmod(output_files.back()->Fd(), kFileMode) != 0) {
PLOG(ERROR) << "Could not set file mode on " << QuotePath(output_file_path);
EraseFiles(output_files);
@@ -270,9 +276,11 @@
}
// Checks whether a group of artifacts exists. Returns true if all are present, false otherwise.
+// If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
bool ArtifactsExist(const OdrArtifacts& artifacts,
bool check_art_file,
- /*out*/ std::string* error_msg) {
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::string>* checked_artifacts = nullptr) {
std::vector<const char*> paths{artifacts.OatPath().c_str(), artifacts.VdexPath().c_str()};
if (check_art_file) {
paths.push_back(artifacts.ImagePath().c_str());
@@ -286,6 +294,13 @@
return false;
}
}
+ // This should be done after checking all artifacts because either all of them are valid or none
+ // of them is valid.
+ if (checked_artifacts != nullptr) {
+ for (const char* path : paths) {
+ checked_artifacts->emplace_back(path);
+ }
+ }
return true;
}
@@ -293,8 +308,8 @@
DCHECK(threads);
DCHECK(cpu_set && cpu_set->empty());
*threads = android::base::GetIntProperty("dalvik.vm.boot-dex2oat-threads",
- /*default_value=*/ 0,
- /*min=*/ 1);
+ /*default_value=*/0,
+ /*min=*/1);
std::string cpu_set_spec = android::base::GetProperty("dalvik.vm.boot-dex2oat-cpu-set", "");
if (cpu_set_spec.empty()) {
@@ -493,7 +508,8 @@
}
time_t OnDeviceRefresh::GetExecutionTimeRemaining() const {
- return std::max(static_cast<time_t>(0), config_.GetMaxExecutionSeconds() - GetExecutionTimeUsed());
+ return std::max(static_cast<time_t>(0),
+ config_.GetMaxExecutionSeconds() - GetExecutionTimeUsed());
}
time_t OnDeviceRefresh::GetSubprocessTimeout() const {
@@ -632,51 +648,6 @@
}
}
-WARN_UNUSED bool OnDeviceRefresh::RemoveBootExtensionArtifactsFromData(InstructionSet isa) const {
- if (config_.GetDryRun()) {
- LOG(INFO) << "Removal of bcp extension artifacts on /data skipped (dry-run).";
- return true;
- }
-
- const std::string apexdata_image_location =
- GetBootImageExtensionImagePath(/*on_system=*/false, isa);
- LOG(INFO) << "Removing boot class path artifacts on /data for "
- << QuotePath(apexdata_image_location);
- return RemoveArtifacts(OdrArtifacts::ForBootImageExtension(apexdata_image_location));
-}
-
-WARN_UNUSED bool OnDeviceRefresh::RemoveSystemServerArtifactsFromData() const {
- if (config_.GetDryRun()) {
- LOG(INFO) << "Removal of system_server artifacts on /data skipped (dry-run).";
- return true;
- }
-
- bool success = true;
- for (const std::string& jar_path : systemserver_compilable_jars_) {
- const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar_path);
- const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
- LOG(INFO) << "Removing system_server artifacts on /data for " << QuotePath(jar_path);
- success &= RemoveArtifacts(artifacts);
- }
- return success;
-}
-
-WARN_UNUSED bool OnDeviceRefresh::RemoveArtifacts(const OdrArtifacts& artifacts) const {
- bool success = true;
- for (const auto& location : {artifacts.ImagePath(), artifacts.OatPath(), artifacts.VdexPath()}) {
- if (config_.GetDryRun()) {
- LOG(INFO) << "Removing " << QuotePath(location) << " (dry-run).";
- continue;
- }
-
- if (OS::FileExists(location.c_str()) && unlink(location.c_str()) != 0) {
- PLOG(ERROR) << "Failed to remove: " << QuotePath(location);
- success = false;
- }
- }
- return success;
-}
-
WARN_UNUSED bool OnDeviceRefresh::RemoveArtifactsDirectory() const {
if (config_.GetDryRun()) {
LOG(INFO) << "Directory " << QuotePath(config_.GetArtifactDirectory())
@@ -689,20 +660,23 @@
WARN_UNUSED bool OnDeviceRefresh::BootExtensionArtifactsExist(
bool on_system,
const InstructionSet isa,
- /*out*/ std::string* error_msg) const {
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
const std::string apexdata_image_location = GetBootImageExtensionImagePath(on_system, isa);
const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(apexdata_image_location);
- return ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg);
+ return ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts);
}
-WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsExist(bool on_system,
- /*out*/ std::string* error_msg) const {
+WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsExist(
+ bool on_system,
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
for (const std::string& jar_path : systemserver_compilable_jars_) {
const std::string image_location = GetSystemServerImagePath(on_system, jar_path);
const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
// .art files are optional and are not generated for all jars by the build system.
const bool check_art_file = !on_system;
- if (!ArtifactsExist(artifacts, check_art_file, error_msg)) {
+ if (!ArtifactsExist(artifacts, check_art_file, error_msg, checked_artifacts)) {
return false;
}
}
@@ -714,7 +688,7 @@
const InstructionSet isa,
const apex::ApexInfo& art_apex_info,
const std::optional<art_apex::CacheInfo>& cache_info,
- /*out*/ bool* cleanup_required) const {
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
std::string error_msg;
if (art_apex_info.getIsFactory()) {
@@ -722,8 +696,6 @@
// ART is not updated, so we can use the artifacts on /system. Check if they exist.
if (BootExtensionArtifactsExist(/*on_system=*/true, isa, &error_msg)) {
- // We don't need the artifacts on /data since we can use those on /system.
- *cleanup_required = true;
return true;
}
@@ -736,7 +708,6 @@
// before.
PLOG(INFO) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- *cleanup_required = true;
return false;
}
@@ -746,7 +717,6 @@
if (cached_art_info == nullptr) {
LOG(INFO) << "Missing ART APEX info from cache-info.";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -754,7 +724,6 @@
LOG(INFO) << "ART APEX version code mismatch (" << cached_art_info->getVersionCode()
<< " != " << art_apex_info.getVersionCode() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -762,7 +731,6 @@
LOG(INFO) << "ART APEX version name mismatch (" << cached_art_info->getVersionName()
<< " != " << art_apex_info.getVersionName() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -775,7 +743,6 @@
LOG(INFO) << "ART APEX last update time mismatch (" << cached_art_last_update_millis
<< " != " << art_apex_info.getLastUpdateMillis() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -794,7 +761,6 @@
!cache_info->getFirstDex2oatBootClasspath()->hasComponent())) {
LOG(INFO) << "Missing Dex2oatBootClasspath components.";
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- *cleanup_required = true;
return false;
}
@@ -803,19 +769,16 @@
if (!CheckComponents(expected_bcp_compilable_components, bcp_compilable_components, &error_msg)) {
LOG(INFO) << "Dex2OatClasspath components mismatch: " << error_msg;
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- *cleanup_required = true;
return false;
}
// Cache info looks good, check all compilation artifacts exist.
- if (!BootExtensionArtifactsExist(/*on_system=*/false, isa, &error_msg)) {
+ if (!BootExtensionArtifactsExist(/*on_system=*/false, isa, &error_msg, checked_artifacts)) {
LOG(INFO) << "Incomplete boot extension artifacts. " << error_msg;
metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- *cleanup_required = true;
return false;
}
- *cleanup_required = false;
return true;
}
@@ -823,7 +786,7 @@
OdrMetrics& metrics,
const std::vector<apex::ApexInfo>& apex_info_list,
const std::optional<art_apex::CacheInfo>& cache_info,
- /*out*/ bool* cleanup_required) const {
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
std::string error_msg;
if (std::all_of(apex_info_list.begin(),
@@ -833,8 +796,6 @@
// APEXes are not updated, so we can use the artifacts on /system. Check if they exist.
if (SystemServerArtifactsExist(/*on_system=*/true, &error_msg)) {
- // We don't need the artifacts on /data since we can use those on /system.
- *cleanup_required = true;
return true;
}
@@ -847,7 +808,6 @@
// before.
PLOG(INFO) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- *cleanup_required = true;
return false;
}
@@ -857,7 +817,6 @@
if (cached_module_info_list == nullptr) {
LOG(INFO) << "Missing APEX info list from cache-info.";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -866,7 +825,6 @@
if (!module_info.hasName()) {
LOG(INFO) << "Unexpected module info from cache-info. Missing module name.";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
cached_module_info_map[module_info.getName()] = &module_info;
@@ -878,7 +836,6 @@
LOG(INFO) << "Missing APEX info from cache-info (" << current_apex_info.getModuleName()
<< ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -889,7 +846,6 @@
<< cached_module_info->getVersionCode()
<< " != " << current_apex_info.getVersionCode() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -898,7 +854,6 @@
<< cached_module_info->getVersionName()
<< " != " << current_apex_info.getVersionName() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
@@ -908,7 +863,6 @@
<< cached_module_info->getLastUpdateMillis()
<< " != " << current_apex_info.getLastUpdateMillis() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- *cleanup_required = true;
return false;
}
}
@@ -929,7 +883,6 @@
!cache_info->getFirstSystemServerClasspath()->hasComponent())) {
LOG(INFO) << "Missing SystemServerClasspath components.";
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- *cleanup_required = true;
return false;
}
@@ -938,7 +891,6 @@
if (!CheckComponents(expected_system_server_components, system_server_components, &error_msg)) {
LOG(INFO) << "SystemServerClasspath components mismatch: " << error_msg;
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- *cleanup_required = true;
return false;
}
@@ -948,7 +900,6 @@
(!cache_info->hasBootClasspath() || !cache_info->getFirstBootClasspath()->hasComponent())) {
LOG(INFO) << "Missing BootClasspath components.";
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- *cleanup_required = true;
return false;
}
@@ -959,25 +910,86 @@
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
// Boot classpath components can be dependencies of system_server components, so system_server
// components need to be recompiled if boot classpath components are changed.
- *cleanup_required = true;
return false;
}
- if (!SystemServerArtifactsExist(/*on_system=*/false, &error_msg)) {
+ if (!SystemServerArtifactsExist(/*on_system=*/false, &error_msg, checked_artifacts)) {
LOG(INFO) << "Incomplete system_server artifacts. " << error_msg;
// No clean-up is required here: we have boot extension artifacts. The method
// `SystemServerArtifactsExistOnData()` checks in compilation order so it is possible some of
// the artifacts are here. We likely ran out of space compiling the system_server artifacts.
// Any artifacts present are usable.
metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- *cleanup_required = false;
return false;
}
- *cleanup_required = false;
return true;
}
+Result<void> OnDeviceRefresh::RefreshExistingArtifactsAndCleanup(
+ const std::vector<std::string>& artifacts) const {
+ const std::string& artifact_dir = config_.GetArtifactDirectory();
+ std::unordered_set<std::string> artifact_set{artifacts.begin(), artifacts.end()};
+
+ // When anything unexpected happens, remove all artifacts.
+ auto remove_artifact_dir = android::base::make_scope_guard([&]() {
+ if (!RemoveDirectory(artifact_dir)) {
+ LOG(ERROR) << "Failed to remove the artifact directory";
+ }
+ });
+
+ std::vector<std::filesystem::directory_entry> entries;
+ std::error_code ec;
+ for (const auto& entry : std::filesystem::recursive_directory_iterator(artifact_dir, ec)) {
+ // Save the entries and use them later because modifications during the iteration will result in
+ // undefined behavior;
+ entries.push_back(entry);
+ }
+
+ if (ec) {
+ return Errorf("Failed to iterate over entries in the artifact directory: {}", ec.message());
+ }
+
+ for (const std::filesystem::directory_entry& entry : entries) {
+ std::string path = entry.path().string();
+ if (entry.is_regular_file()) {
+ if (ContainsElement(artifact_set, path)) {
+ if (!config_.GetRefresh()) {
+ continue;
+ }
+ LOG(INFO) << "Refreshing " << path;
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ return Errorf("Failed to read file {}", QuotePath(path));
+ }
+ if (unlink(path.c_str()) != 0) {
+ return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
+ }
+ if (!android::base::WriteStringToFile(content, path)) {
+ return Errorf("Failed to write file {}", QuotePath(path));
+ }
+ if (chmod(path.c_str(), kFileMode) != 0) {
+ return ErrnoErrorf("Failed to chmod file {}", QuotePath(path));
+ }
+ } else {
+ LOG(INFO) << "Removing " << path;
+ if (unlink(path.c_str()) != 0) {
+ return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
+ }
+ }
+ } else if (!entry.is_directory()) {
+ // Neither a regular file nor a directory. Unexpected file type.
+ LOG(INFO) << "Removing " << path;
+ if (unlink(path.c_str()) != 0) {
+ return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
+ }
+ }
+ }
+
+ remove_artifact_dir.Disable();
+ return {};
+}
+
WARN_UNUSED ExitCode OnDeviceRefresh::CheckArtifactsAreUpToDate(
OdrMetrics& metrics,
/*out*/ std::vector<InstructionSet>* compile_boot_extensions,
@@ -1027,43 +1039,40 @@
}
InstructionSet system_server_isa = config_.GetSystemServerIsa();
- bool cleanup_required;
+ std::vector<std::string> checked_artifacts;
+ checked_artifacts.push_back(cache_info_filename_);
for (const InstructionSet isa : config_.GetBootExtensionIsas()) {
- cleanup_required = false;
if (!CheckBootExtensionArtifactsAreUpToDate(
- metrics, isa, art_apex_info.value(), cache_info, &cleanup_required)) {
+ metrics, isa, art_apex_info.value(), cache_info, &checked_artifacts)) {
compile_boot_extensions->push_back(isa);
// system_server artifacts are invalid without valid boot extension artifacts.
if (isa == system_server_isa) {
*compile_system_server = true;
- if (!RemoveSystemServerArtifactsFromData()) {
- return ExitCode::kCleanupFailed;
- }
- }
- }
- if (cleanup_required) {
- if (!RemoveBootExtensionArtifactsFromData(isa)) {
- return ExitCode::kCleanupFailed;
}
}
}
- cleanup_required = false;
if (!*compile_system_server &&
!CheckSystemServerArtifactsAreUpToDate(
- metrics, apex_info_list.value(), cache_info, &cleanup_required)) {
+ metrics, apex_info_list.value(), cache_info, &checked_artifacts)) {
*compile_system_server = true;
}
- if (cleanup_required) {
- if (!RemoveSystemServerArtifactsFromData()) {
+
+ bool compilation_required = !compile_boot_extensions->empty() || *compile_system_server;
+
+ if (compilation_required) {
+ if (!config_.GetPartialCompilation()) {
+ return cleanup_and_compile_all();
+ }
+ Result<void> result = RefreshExistingArtifactsAndCleanup(checked_artifacts);
+ if (!result.ok()) {
+ LOG(ERROR) << result.error();
return ExitCode::kCleanupFailed;
}
}
- return (!compile_boot_extensions->empty() || *compile_system_server) ?
- ExitCode::kCompilationRequired :
- ExitCode::kOkay;
+ return compilation_required ? ExitCode::kCompilationRequired : ExitCode::kOkay;
}
WARN_UNUSED bool OnDeviceRefresh::CompileBootExtensionArtifacts(const InstructionSet isa,
@@ -1078,8 +1087,8 @@
std::vector<std::unique_ptr<File>> readonly_files_raii;
const std::string boot_profile_file(GetAndroidRoot() + "/etc/boot-image.prof");
- if (!PrepareDex2OatProfileIfExists(&dexopt_args.profileFd, &readonly_files_raii,
- boot_profile_file)) {
+ if (!PrepareDex2OatProfileIfExists(
+ &dexopt_args.profileFd, &readonly_files_raii, boot_profile_file)) {
LOG(ERROR) << "Missing expected profile for boot extension: " << boot_profile_file;
return false;
}
@@ -1103,9 +1112,7 @@
auto bcp_jars = android::base::Split(config_.GetDex2oatBootClasspath(), ":");
dexopt_args.bootClasspaths = bcp_jars;
- if (!PrepareBootClasspathFds(dexopt_args.bootClasspathFds,
- readonly_files_raii,
- bcp_jars)) {
+ if (!PrepareBootClasspathFds(dexopt_args.bootClasspathFds, readonly_files_raii, bcp_jars)) {
return false;
}
@@ -1161,8 +1168,8 @@
}
bool timed_out = false;
- int dex2oat_exit_code = odr_dexopt_->DexoptBcpExtension(
- dexopt_args, timeout, &timed_out, error_msg);
+ int dex2oat_exit_code =
+ odr_dexopt_->DexoptBcpExtension(dexopt_args, timeout, &timed_out, error_msg);
if (dex2oat_exit_code != 0) {
if (timed_out) {
@@ -1257,9 +1264,7 @@
auto bcp_jars = android::base::Split(config_.GetBootClasspath(), ":");
dexopt_args.bootClasspaths = bcp_jars;
- if (!PrepareBootClasspathFds(dexopt_args.bootClasspathFds,
- readonly_files_raii,
- bcp_jars)) {
+ if (!PrepareBootClasspathFds(dexopt_args.bootClasspathFds, readonly_files_raii, bcp_jars)) {
return false;
}
std::string unused_error_msg;
@@ -1300,8 +1305,8 @@
}
bool timed_out = false;
- int dex2oat_exit_code = odr_dexopt_->DexoptSystemServer(
- dexopt_args, timeout, &timed_out, error_msg);
+ int dex2oat_exit_code =
+ odr_dexopt_->DexoptSystemServer(dexopt_args, timeout, &timed_out, error_msg);
if (dex2oat_exit_code != 0) {
if (timed_out) {
diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h
index 6b91bd8..54791d6 100644
--- a/odrefresh/odrefresh.h
+++ b/odrefresh/odrefresh.h
@@ -23,6 +23,7 @@
#include <string>
#include <vector>
+#include "android-base/result.h"
#include "com_android_apex.h"
#include "com_android_art.h"
#include "exec_utils.h"
@@ -88,36 +89,47 @@
std::string GetSystemServerImagePath(bool on_system, const std::string& jar_path) const;
- WARN_UNUSED bool RemoveSystemServerArtifactsFromData() const;
-
- // Remove boot extension artifacts from /data.
- WARN_UNUSED bool RemoveBootExtensionArtifactsFromData(InstructionSet isa) const;
-
- WARN_UNUSED bool RemoveArtifacts(const OdrArtifacts& artifacts) const;
+ // Loads artifacts to memory and writes them back. Also cleans up other files in the artifact
+ // directory. This essentially removes the existing artifacts from fs-verity so that odsign will
+ // not encounter "file exists" error when it adds the existing artifacts to fs-verity.
+ android::base::Result<void> RefreshExistingArtifactsAndCleanup(
+ const std::vector<std::string>& artifacts) const;
// Checks whether all boot extension artifacts are present. Returns true if all are present, false
// otherwise.
- WARN_UNUSED bool BootExtensionArtifactsExist(bool on_system,
- const InstructionSet isa,
- /*out*/ std::string* error_msg) const;
+ // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
+ WARN_UNUSED bool BootExtensionArtifactsExist(
+ bool on_system,
+ const InstructionSet isa,
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::string>* checked_artifacts = nullptr) const;
// Checks whether all system_server artifacts are present. The artifacts are checked in their
// order of compilation. Returns true if all are present, false otherwise.
- WARN_UNUSED bool SystemServerArtifactsExist(bool on_system,
- /*out*/ std::string* error_msg) const;
+ // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
+ WARN_UNUSED bool SystemServerArtifactsExist(
+ bool on_system,
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::string>* checked_artifacts = nullptr) const;
+ // Checks whether all boot extension artifacts are up to date. Returns true if all are present,
+ // false otherwise.
+ // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
WARN_UNUSED bool CheckBootExtensionArtifactsAreUpToDate(
OdrMetrics& metrics,
const InstructionSet isa,
const com::android::apex::ApexInfo& art_apex_info,
const std::optional<com::android::art::CacheInfo>& cache_info,
- /*out*/ bool* cleanup_required) const;
+ /*out*/ std::vector<std::string>* checked_artifacts) const;
+ // Checks whether all system_server artifacts are up to date. The artifacts are checked in their
+ // order of compilation. Returns true if all are present, false otherwise.
+ // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
WARN_UNUSED bool CheckSystemServerArtifactsAreUpToDate(
OdrMetrics& metrics,
const std::vector<com::android::apex::ApexInfo>& apex_info_list,
const std::optional<com::android::art::CacheInfo>& cache_info,
- /*out*/ bool* cleanup_required) const;
+ /*out*/ std::vector<std::string>* checked_artifacts) const;
WARN_UNUSED bool CompileBootExtensionArtifacts(const InstructionSet isa,
const std::string& staging_dir,
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index a740252..aecabd0 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -108,16 +108,25 @@
}
bool InitializeCommonConfig(std::string_view argument, OdrConfig* config) {
- static constexpr std::string_view kDryRunArgument{"--dry-run"};
- if (ArgumentEquals(argument, kDryRunArgument)) {
+ if (ArgumentEquals(argument, "--dry-run")) {
config->SetDryRun();
return true;
}
+ if (ArgumentEquals(argument, "--partial-compilation")) {
+ config->SetPartialCompilation(true);
+ return true;
+ }
+ if (ArgumentEquals(argument, "--no-refresh")) {
+ config->SetRefresh(false);
+ return true;
+ }
return false;
}
void CommonOptionsHelp() {
UsageError("--dry-run");
+ UsageError("--partial-compilation Only generate artifacts that are out-of-date or missing.");
+ UsageError("--no-refresh Do not refresh existing artifacts.");
}
int InitializeHostConfig(int argc, char** argv, OdrConfig* config) {
@@ -307,7 +316,8 @@
OdrCompilationLog compilation_log;
if (!compilation_log.ShouldAttemptCompile(metrics.GetTrigger())) {
LOG(INFO) << "Compilation skipped because it was attempted recently";
- return ExitCode::kOkay;
+ // Artifacts refreshed. Return `kCompilationFailed` so that odsign will sign them again.
+ return ExitCode::kCompilationFailed;
}
ExitCode compile_result =
odr.Compile(metrics, compile_boot_extensions, compile_system_server);
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
index 3f6be8a..22fbde7 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
@@ -42,6 +42,8 @@
public class OdrefreshHostTest extends BaseHostJUnit4Test {
private static final String CACHE_INFO_FILE =
OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME + "/cache-info.xml";
+ private static final String ODREFRESH_COMMAND =
+ "odrefresh --partial-compilation --no-refresh --compile";
private static OdsignTestUtils sTestUtils;
@@ -70,7 +72,7 @@
public void verifyArtSamegradeUpdateTriggersCompilation() throws Exception {
simulateArtApexUpgrade();
long timeMs = getCurrentTimeMs();
- getDevice().executeShellV2Command("odrefresh --compile");
+ getDevice().executeShellV2Command(ODREFRESH_COMMAND);
assertArtifactsModifiedAfter(sZygoteArtifacts, timeMs);
assertArtifactsModifiedAfter(sSystemServerArtifacts, timeMs);
@@ -80,7 +82,7 @@
public void verifyOtherApexSamegradeUpdateTriggersCompilation() throws Exception {
simulateApexUpgrade();
long timeMs = getCurrentTimeMs();
- getDevice().executeShellV2Command("odrefresh --compile");
+ getDevice().executeShellV2Command(ODREFRESH_COMMAND);
assertArtifactsNotModifiedAfter(sZygoteArtifacts, timeMs);
assertArtifactsModifiedAfter(sSystemServerArtifacts, timeMs);
@@ -90,7 +92,7 @@
public void verifyBootClasspathOtaTriggersCompilation() throws Exception {
simulateBootClasspathOta();
long timeMs = getCurrentTimeMs();
- getDevice().executeShellV2Command("odrefresh --compile");
+ getDevice().executeShellV2Command(ODREFRESH_COMMAND);
assertArtifactsModifiedAfter(sZygoteArtifacts, timeMs);
assertArtifactsModifiedAfter(sSystemServerArtifacts, timeMs);
@@ -100,7 +102,7 @@
public void verifySystemServerOtaTriggersCompilation() throws Exception {
simulateSystemServerOta();
long timeMs = getCurrentTimeMs();
- getDevice().executeShellV2Command("odrefresh --compile");
+ getDevice().executeShellV2Command(ODREFRESH_COMMAND);
assertArtifactsNotModifiedAfter(sZygoteArtifacts, timeMs);
assertArtifactsModifiedAfter(sSystemServerArtifacts, timeMs);
@@ -110,7 +112,7 @@
public void verifyNoCompilationWhenCacheIsGood() throws Exception {
sTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = getCurrentTimeMs();
- getDevice().executeShellV2Command("odrefresh --compile");
+ getDevice().executeShellV2Command(ODREFRESH_COMMAND);
assertArtifactsNotModifiedAfter(sZygoteArtifacts, timeMs);
assertArtifactsNotModifiedAfter(sSystemServerArtifacts, timeMs);
diff --git a/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
index ca53188..867c849 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
@@ -99,6 +99,16 @@
verifyGeneratedArtifactsLoaded();
}
+ @Test
+ public void verifyGeneratedArtifactsLoadedAfterPartialCompilation() throws Exception {
+ Set<String> mappedArtifacts = sTestUtils.getSystemServerLoadedArtifacts();
+ // Delete an arbitrary artifact.
+ getDevice().deleteFile(mappedArtifacts.iterator().next());
+ sTestUtils.removeCompilationLogToAvoidBackoff();
+ sTestUtils.reboot();
+ verifyGeneratedArtifactsLoaded();
+ }
+
private String[] getSystemServerClasspath() throws Exception {
String systemServerClasspath =
getDevice().executeShellCommand("echo $SYSTEMSERVERCLASSPATH");