• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "odrefresh.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sysexits.h>
27 #include <time.h>
28 #include <unistd.h>
29 
30 #include <algorithm>
31 #include <cstdarg>
32 #include <cstdint>
33 #include <cstdio>
34 #include <cstdlib>
35 #include <filesystem>
36 #include <fstream>
37 #include <functional>
38 #include <initializer_list>
39 #include <iosfwd>
40 #include <iostream>
41 #include <iterator>
42 #include <memory>
43 #include <optional>
44 #include <ostream>
45 #include <set>
46 #include <sstream>
47 #include <string>
48 #include <string_view>
49 #include <system_error>
50 #include <type_traits>
51 #include <unordered_map>
52 #include <unordered_set>
53 #include <utility>
54 #include <vector>
55 
56 #include "android-base/chrono_utils.h"
57 #include "android-base/file.h"
58 #include "android-base/logging.h"
59 #include "android-base/macros.h"
60 #include "android-base/parsebool.h"
61 #include "android-base/parseint.h"
62 #include "android-base/properties.h"
63 #include "android-base/result.h"
64 #include "android-base/scopeguard.h"
65 #include "android-base/stringprintf.h"
66 #include "android-base/strings.h"
67 #include "android-modules-utils/sdk_level.h"
68 #include "android/log.h"
69 #include "arch/instruction_set.h"
70 #include "base/file_utils.h"
71 #include "base/globals.h"
72 #include "base/logging.h"
73 #include "base/macros.h"
74 #include "base/os.h"
75 #include "base/stl_util.h"
76 #include "base/string_view_cpp20.h"
77 #include "base/unix_file/fd_file.h"
78 #include "com_android_apex.h"
79 #include "com_android_art.h"
80 #include "dex/art_dex_file_loader.h"
81 #include "dexoptanalyzer.h"
82 #include "exec_utils.h"
83 #include "fmt/format.h"
84 #include "gc/collector/mark_compact.h"
85 #include "log/log.h"
86 #include "odr_artifacts.h"
87 #include "odr_common.h"
88 #include "odr_compilation_log.h"
89 #include "odr_config.h"
90 #include "odr_fs_utils.h"
91 #include "odr_metrics.h"
92 #include "odrefresh/odrefresh.h"
93 #include "palette/palette.h"
94 #include "palette/palette_types.h"
95 #include "read_barrier_config.h"
96 
97 namespace art {
98 namespace odrefresh {
99 
100 namespace {
101 
102 namespace apex = com::android::apex;
103 namespace art_apex = com::android::art;
104 
105 using ::android::base::Basename;
106 using ::android::base::Dirname;
107 using ::android::base::GetProperty;
108 using ::android::base::Join;
109 using ::android::base::ParseBool;
110 using ::android::base::ParseBoolResult;
111 using ::android::base::ParseInt;
112 using ::android::base::Result;
113 using ::android::base::SetProperty;
114 using ::android::base::Split;
115 using ::android::base::StartsWith;
116 using ::android::base::StringPrintf;
117 using ::android::base::Timer;
118 using ::android::modules::sdklevel::IsAtLeastU;
119 
120 using ::fmt::literals::operator""_format;  // NOLINT
121 
122 // Name of cache info file in the ART Apex artifact cache.
123 constexpr const char* kCacheInfoFile = "cache-info.xml";
124 
125 // Maximum execution time for odrefresh from start to end.
126 constexpr time_t kMaximumExecutionSeconds = 480;
127 
128 // Maximum execution time for any child process spawned.
129 constexpr time_t kMaxChildProcessSeconds = 120;
130 
131 constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
132 
133 constexpr const char* kFirstBootImageBasename = "boot.art";
134 constexpr const char* kMinimalBootImageBasename = "boot_minimal.art";
135 
136 // The default compiler filter for primary boot image.
137 constexpr const char* kPrimaryCompilerFilter = "speed-profile";
138 
139 // The compiler filter for boot image mainline extension. We don't have profiles for mainline BCP
140 // jars, so we always use "verify".
141 constexpr const char* kMainlineCompilerFilter = "verify";
142 
EraseFiles(const std::vector<std::unique_ptr<File>> & files)143 void EraseFiles(const std::vector<std::unique_ptr<File>>& files) {
144   for (auto& file : files) {
145     file->Erase(/*unlink=*/true);
146   }
147 }
148 
149 // Moves `files` to the directory `output_directory_path`.
150 //
151 // If any of the files cannot be moved, then all copies of the files are removed from both
152 // the original location and the output location.
153 //
154 // Returns true if all files are moved, false otherwise.
MoveOrEraseFiles(const std::vector<std::unique_ptr<File>> & files,std::string_view output_directory_path)155 bool MoveOrEraseFiles(const std::vector<std::unique_ptr<File>>& files,
156                       std::string_view output_directory_path) {
157   std::vector<std::unique_ptr<File>> output_files;
158   for (auto& file : files) {
159     std::string file_basename(Basename(file->GetPath()));
160     std::string output_file_path = "{}/{}"_format(output_directory_path, file_basename);
161     std::string input_file_path = file->GetPath();
162 
163     output_files.emplace_back(OS::CreateEmptyFileWriteOnly(output_file_path.c_str()));
164     if (output_files.back() == nullptr) {
165       PLOG(ERROR) << "Failed to open " << QuotePath(output_file_path);
166       output_files.pop_back();
167       EraseFiles(output_files);
168       EraseFiles(files);
169       return false;
170     }
171 
172     if (fchmod(output_files.back()->Fd(), kFileMode) != 0) {
173       PLOG(ERROR) << "Could not set file mode on " << QuotePath(output_file_path);
174       EraseFiles(output_files);
175       EraseFiles(files);
176       return false;
177     }
178 
179     size_t file_bytes = file->GetLength();
180     if (!output_files.back()->Copy(file.get(), /*offset=*/0, file_bytes)) {
181       PLOG(ERROR) << "Failed to copy " << QuotePath(file->GetPath()) << " to "
182                   << QuotePath(output_file_path);
183       EraseFiles(output_files);
184       EraseFiles(files);
185       return false;
186     }
187 
188     if (!file->Erase(/*unlink=*/true)) {
189       PLOG(ERROR) << "Failed to erase " << QuotePath(file->GetPath());
190       EraseFiles(output_files);
191       EraseFiles(files);
192       return false;
193     }
194 
195     if (output_files.back()->FlushCloseOrErase() != 0) {
196       PLOG(ERROR) << "Failed to flush and close file " << QuotePath(output_file_path);
197       EraseFiles(output_files);
198       EraseFiles(files);
199       return false;
200     }
201   }
202   return true;
203 }
204 
205 // Gets the `ApexInfo` associated with the currently active ART APEX.
GetArtApexInfo(const std::vector<apex::ApexInfo> & info_list)206 std::optional<apex::ApexInfo> GetArtApexInfo(const std::vector<apex::ApexInfo>& info_list) {
207   auto it = std::find_if(info_list.begin(), info_list.end(), [](const apex::ApexInfo& info) {
208     return info.getModuleName() == "com.android.art";
209   });
210   return it != info_list.end() ? std::make_optional(*it) : std::nullopt;
211 }
212 
213 // Returns cache provenance information based on the current APEX version and filesystem
214 // information.
GenerateModuleInfo(const apex::ApexInfo & apex_info)215 art_apex::ModuleInfo GenerateModuleInfo(const apex::ApexInfo& apex_info) {
216   // The lastUpdateMillis is an addition to ApexInfoList.xsd to support samegrade installs.
217   int64_t last_update_millis =
218       apex_info.hasLastUpdateMillis() ? apex_info.getLastUpdateMillis() : 0;
219   return art_apex::ModuleInfo{apex_info.getModuleName(),
220                               apex_info.getVersionCode(),
221                               apex_info.getVersionName(),
222                               last_update_millis};
223 }
224 
225 // Returns cache provenance information for all APEXes.
GenerateModuleInfoList(const std::vector<apex::ApexInfo> & apex_info_list)226 std::vector<art_apex::ModuleInfo> GenerateModuleInfoList(
227     const std::vector<apex::ApexInfo>& apex_info_list) {
228   std::vector<art_apex::ModuleInfo> module_info_list;
229   std::transform(apex_info_list.begin(),
230                  apex_info_list.end(),
231                  std::back_inserter(module_info_list),
232                  GenerateModuleInfo);
233   return module_info_list;
234 }
235 
236 // Returns a rewritten path based on environment variables for interesting paths.
RewriteParentDirectoryIfNeeded(const std::string & path)237 std::string RewriteParentDirectoryIfNeeded(const std::string& path) {
238   if (StartsWith(path, "/system/")) {
239     return GetAndroidRoot() + path.substr(7);
240   } else if (StartsWith(path, "/system_ext/")) {
241     return GetSystemExtRoot() + path.substr(11);
242   } else {
243     return path;
244   }
245 }
246 
247 template <typename T>
CheckComponents(const std::vector<T> & expected_components,const std::vector<T> & actual_components,const std::function<Result<void> (const T & expected,const T & actual)> & custom_checker=[](const T &,const T &)->Result<void>{})248 Result<void> CheckComponents(
249     const std::vector<T>& expected_components,
250     const std::vector<T>& actual_components,
251     const std::function<Result<void>(const T& expected, const T& actual)>& custom_checker =
252         [](const T&, const T&) -> Result<void> { return {}; }) {
253   if (expected_components.size() != actual_components.size()) {
254     return Errorf(
255         "Component count differs ({} != {})", expected_components.size(), actual_components.size());
256   }
257 
258   for (size_t i = 0; i < expected_components.size(); ++i) {
259     const T& expected = expected_components[i];
260     const T& actual = actual_components[i];
261 
262     if (expected.getFile() != actual.getFile()) {
263       return Errorf(
264           "Component {} file differs ('{}' != '{}')", i, expected.getFile(), actual.getFile());
265     }
266 
267     if (expected.getSize() != actual.getSize()) {
268       return Errorf(
269           "Component {} size differs ({} != {})", i, expected.getSize(), actual.getSize());
270     }
271 
272     if (expected.getChecksums() != actual.getChecksums()) {
273       return Errorf("Component {} checksums differ ('{}' != '{}')",
274                     i,
275                     expected.getChecksums(),
276                     actual.getChecksums());
277     }
278 
279     Result<void> result = custom_checker(expected, actual);
280     if (!result.ok()) {
281       return Errorf("Component {} {}", i, result.error().message());
282     }
283   }
284 
285   return {};
286 }
287 
CheckSystemServerComponents(const std::vector<art_apex::SystemServerComponent> & expected_components,const std::vector<art_apex::SystemServerComponent> & actual_components)288 Result<void> CheckSystemServerComponents(
289     const std::vector<art_apex::SystemServerComponent>& expected_components,
290     const std::vector<art_apex::SystemServerComponent>& actual_components) {
291   return CheckComponents<art_apex::SystemServerComponent>(
292       expected_components,
293       actual_components,
294       [](const art_apex::SystemServerComponent& expected,
295          const art_apex::SystemServerComponent& actual) -> Result<void> {
296         if (expected.getIsInClasspath() != actual.getIsInClasspath()) {
297           return Errorf("isInClasspath differs ({} != {})",
298                         expected.getIsInClasspath(),
299                         actual.getIsInClasspath());
300         }
301 
302         return {};
303       });
304 }
305 
306 template <typename T>
GenerateComponents(const std::vector<std::string> & jars,const std::function<T (const std::string & path,uint64_t size,const std::string & checksum)> & custom_generator)307 std::vector<T> GenerateComponents(
308     const std::vector<std::string>& jars,
309     const std::function<T(const std::string& path, uint64_t size, const std::string& checksum)>&
310         custom_generator) {
311   std::vector<T> components;
312 
313   for (const std::string& path : jars) {
314     std::string actual_path = RewriteParentDirectoryIfNeeded(path);
315     struct stat sb;
316     if (stat(actual_path.c_str(), &sb) == -1) {
317       PLOG(ERROR) << "Failed to stat component: " << QuotePath(actual_path);
318       return {};
319     }
320 
321     std::vector<uint32_t> checksums;
322     std::vector<std::string> dex_locations;
323     std::string error_msg;
324     if (!ArtDexFileLoader::GetMultiDexChecksums(
325             actual_path.c_str(), &checksums, &dex_locations, &error_msg)) {
326       LOG(ERROR) << "Failed to get multi-dex checksums: " << error_msg;
327       return {};
328     }
329 
330     std::ostringstream oss;
331     for (size_t i = 0; i < checksums.size(); ++i) {
332       if (i != 0) {
333         oss << ';';
334       }
335       oss << StringPrintf("%08x", checksums[i]);
336     }
337     const std::string checksum = oss.str();
338 
339     Result<T> component = custom_generator(path, static_cast<uint64_t>(sb.st_size), checksum);
340     if (!component.ok()) {
341       LOG(ERROR) << "Failed to generate component: " << component.error();
342       return {};
343     }
344 
345     components.push_back(*std::move(component));
346   }
347 
348   return components;
349 }
350 
GenerateComponents(const std::vector<std::string> & jars)351 std::vector<art_apex::Component> GenerateComponents(const std::vector<std::string>& jars) {
352   return GenerateComponents<art_apex::Component>(
353       jars, [](const std::string& path, uint64_t size, const std::string& checksum) {
354         return art_apex::Component{path, size, checksum};
355       });
356 }
357 
358 // Checks whether a group of artifacts exists. Returns true if all are present, false otherwise.
359 // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
ArtifactsExist(const OdrArtifacts & artifacts,bool check_art_file,std::string * error_msg,std::vector<std::string> * checked_artifacts=nullptr)360 bool ArtifactsExist(const OdrArtifacts& artifacts,
361                     bool check_art_file,
362                     /*out*/ std::string* error_msg,
363                     /*out*/ std::vector<std::string>* checked_artifacts = nullptr) {
364   std::vector<const char*> paths{artifacts.OatPath().c_str(), artifacts.VdexPath().c_str()};
365   if (check_art_file) {
366     paths.push_back(artifacts.ImagePath().c_str());
367   }
368   for (const char* path : paths) {
369     if (!OS::FileExists(path)) {
370       if (errno == EACCES) {
371         PLOG(ERROR) << "Failed to stat() " << path;
372       }
373       *error_msg = "Missing file: " + QuotePath(path);
374       return false;
375     }
376   }
377   // This should be done after checking all artifacts because either all of them are valid or none
378   // of them is valid.
379   if (checked_artifacts != nullptr) {
380     for (const char* path : paths) {
381       checked_artifacts->emplace_back(path);
382     }
383   }
384   return true;
385 }
386 
AddDex2OatCommonOptions(std::vector<std::string> & args)387 void AddDex2OatCommonOptions(/*inout*/ std::vector<std::string>& args) {
388   args.emplace_back("--android-root=out/empty");
389   args.emplace_back("--abort-on-hard-verifier-error");
390   args.emplace_back("--no-abort-on-soft-verifier-error");
391   args.emplace_back("--compilation-reason=boot");
392   args.emplace_back("--image-format=lz4");
393   args.emplace_back("--force-determinism");
394   args.emplace_back("--resolve-startup-const-strings=true");
395 
396   // Avoid storing dex2oat cmdline in oat header. We want to be sure that the compiled artifacts
397   // are identical regardless of where the compilation happened. But some of the cmdline flags tends
398   // to be unstable, e.g. those contains FD numbers. To avoid the problem, the whole cmdline is not
399   // added to the oat header.
400   args.emplace_back("--avoid-storing-invocation");
401 }
402 
IsCpuSetSpecValid(const std::string & cpu_set)403 bool IsCpuSetSpecValid(const std::string& cpu_set) {
404   for (const std::string& str : Split(cpu_set, ",")) {
405     int id;
406     if (!ParseInt(str, &id, 0)) {
407       return false;
408     }
409   }
410   return true;
411 }
412 
AddDex2OatConcurrencyArguments(std::vector<std::string> & args,bool is_compilation_os)413 Result<void> AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>& args,
414                                             bool is_compilation_os) {
415   std::string threads;
416   if (is_compilation_os) {
417     threads = GetProperty("dalvik.vm.background-dex2oat-threads", "");
418     if (threads.empty()) {
419       threads = GetProperty("dalvik.vm.dex2oat-threads", "");
420     }
421   } else {
422     threads = GetProperty("dalvik.vm.boot-dex2oat-threads", "");
423   }
424   if (!threads.empty()) {
425     args.push_back("-j" + threads);
426   }
427 
428   std::string cpu_set;
429   if (is_compilation_os) {
430     cpu_set = GetProperty("dalvik.vm.background-dex2oat-cpu-set", "");
431     if (cpu_set.empty()) {
432       cpu_set = GetProperty("dalvik.vm.dex2oat-cpu-set", "");
433     }
434   } else {
435     cpu_set = GetProperty("dalvik.vm.boot-dex2oat-cpu-set", "");
436   }
437   if (!cpu_set.empty()) {
438     if (!IsCpuSetSpecValid(cpu_set)) {
439       return Errorf("Invalid CPU set spec '{}'", cpu_set);
440     }
441     args.push_back("--cpu-set=" + cpu_set);
442   }
443 
444   return {};
445 }
446 
AddDex2OatDebugInfo(std::vector<std::string> & args)447 void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>& args) {
448   args.emplace_back("--generate-mini-debug-info");
449   args.emplace_back("--strip");
450 }
451 
AddDex2OatInstructionSet(std::vector<std::string> & args,InstructionSet isa)452 void AddDex2OatInstructionSet(/*inout*/ std::vector<std::string>& args, InstructionSet isa) {
453   const char* isa_str = GetInstructionSetString(isa);
454   args.emplace_back(StringPrintf("--instruction-set=%s", isa_str));
455 }
456 
457 // Returns true if any profile has been added.
AddDex2OatProfile(std::vector<std::string> & args,std::vector<std::unique_ptr<File>> & output_files,const std::vector<std::string> & profile_paths)458 bool AddDex2OatProfile(
459     /*inout*/ std::vector<std::string>& args,
460     /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
461     const std::vector<std::string>& profile_paths) {
462   bool has_any_profile = false;
463   for (auto& path : profile_paths) {
464     std::unique_ptr<File> profile_file(OS::OpenFileForReading(path.c_str()));
465     if (profile_file && profile_file->IsOpened()) {
466       args.emplace_back(StringPrintf("--profile-file-fd=%d", profile_file->Fd()));
467       output_files.emplace_back(std::move(profile_file));
468       has_any_profile = true;
469     }
470   }
471   return has_any_profile;
472 }
473 
AddBootClasspathFds(std::vector<std::string> & args,std::vector<std::unique_ptr<File>> & output_files,const std::vector<std::string> & bcp_jars)474 Result<void> AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
475                                  /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
476                                  const std::vector<std::string>& bcp_jars) {
477   std::vector<std::string> bcp_fds;
478   for (const std::string& jar : bcp_jars) {
479     // Special treatment for Compilation OS. JARs in staged APEX may not be visible to Android, and
480     // may only be visible in the VM where the staged APEX is mounted. On the contrary, JARs in
481     // /system is not available by path in the VM, and can only made available via (remote) FDs.
482     if (StartsWith(jar, "/apex/")) {
483       bcp_fds.emplace_back("-1");
484     } else {
485       std::string actual_path = RewriteParentDirectoryIfNeeded(jar);
486       std::unique_ptr<File> jar_file(OS::OpenFileForReading(actual_path.c_str()));
487       if (!jar_file || !jar_file->IsValid()) {
488         return Errorf("Failed to open a BCP jar '{}'", actual_path);
489       }
490       bcp_fds.push_back(std::to_string(jar_file->Fd()));
491       output_files.push_back(std::move(jar_file));
492     }
493   }
494   args.emplace_back("--runtime-arg");
495   args.emplace_back("-Xbootclasspathfds:" + Join(bcp_fds, ':'));
496   return {};
497 }
498 
AddCacheInfoFd(std::vector<std::string> & args,std::vector<std::unique_ptr<File>> & readonly_files_raii,const std::string & cache_info_filename)499 Result<void> AddCacheInfoFd(/*inout*/ std::vector<std::string>& args,
500                             /*inout*/ std::vector<std::unique_ptr<File>>& readonly_files_raii,
501                             const std::string& cache_info_filename) {
502   std::unique_ptr<File> cache_info_file(OS::OpenFileForReading(cache_info_filename.c_str()));
503   if (cache_info_file == nullptr) {
504     return ErrnoErrorf("Failed to open a cache info file '{}'", cache_info_file);
505   }
506 
507   args.emplace_back("--cache-info-fd=" + std::to_string(cache_info_file->Fd()));
508   readonly_files_raii.push_back(std::move(cache_info_file));
509   return {};
510 }
511 
GetBootImageComponentBasename(const std::string & jar_path,bool is_first_jar)512 std::string GetBootImageComponentBasename(const std::string& jar_path, bool is_first_jar) {
513   if (is_first_jar) {
514     return kFirstBootImageBasename;
515   }
516   std::string jar_name = Basename(jar_path);
517   return "boot-" + ReplaceFileExtension(jar_name, "art");
518 }
519 
AddCompiledBootClasspathFdsIfAny(std::vector<std::string> & args,std::vector<std::unique_ptr<File>> & output_files,const std::vector<std::string> & bcp_jars,InstructionSet isa,const std::vector<std::string> & boot_image_locations)520 void AddCompiledBootClasspathFdsIfAny(
521     /*inout*/ std::vector<std::string>& args,
522     /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
523     const std::vector<std::string>& bcp_jars,
524     InstructionSet isa,
525     const std::vector<std::string>& boot_image_locations) {
526   std::vector<std::string> bcp_image_fds;
527   std::vector<std::string> bcp_oat_fds;
528   std::vector<std::string> bcp_vdex_fds;
529   std::vector<std::unique_ptr<File>> opened_files;
530   bool added_any = false;
531   std::string artifact_dir;
532   for (size_t i = 0; i < bcp_jars.size(); i++) {
533     const std::string& jar = bcp_jars[i];
534     std::string basename = GetBootImageComponentBasename(jar, /*is_first_jar=*/i == 0);
535     // If there is an entry in `boot_image_locations` for the current jar, update `artifact_dir` for
536     // the current jar and the subsequent jars.
537     for (const std::string& location : boot_image_locations) {
538       if (Basename(location) == basename) {
539         artifact_dir = Dirname(location);
540         break;
541       }
542     }
543     CHECK(!artifact_dir.empty());
544     std::string image_path = artifact_dir + "/" + basename;
545     image_path = GetSystemImageFilename(image_path.c_str(), isa);
546     std::unique_ptr<File> image_file(OS::OpenFileForReading(image_path.c_str()));
547     if (image_file && image_file->IsValid()) {
548       bcp_image_fds.push_back(std::to_string(image_file->Fd()));
549       opened_files.push_back(std::move(image_file));
550       added_any = true;
551     } else {
552       bcp_image_fds.push_back("-1");
553     }
554 
555     std::string oat_path = ReplaceFileExtension(image_path, "oat");
556     std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_path.c_str()));
557     if (oat_file && oat_file->IsValid()) {
558       bcp_oat_fds.push_back(std::to_string(oat_file->Fd()));
559       opened_files.push_back(std::move(oat_file));
560       added_any = true;
561     } else {
562       bcp_oat_fds.push_back("-1");
563     }
564 
565     std::string vdex_path = ReplaceFileExtension(image_path, "vdex");
566     std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex_path.c_str()));
567     if (vdex_file && vdex_file->IsValid()) {
568       bcp_vdex_fds.push_back(std::to_string(vdex_file->Fd()));
569       opened_files.push_back(std::move(vdex_file));
570       added_any = true;
571     } else {
572       bcp_vdex_fds.push_back("-1");
573     }
574   }
575   // Add same amount of FDs as BCP JARs, or none.
576   if (added_any) {
577     std::move(opened_files.begin(), opened_files.end(), std::back_inserter(output_files));
578 
579     args.emplace_back("--runtime-arg");
580     args.emplace_back("-Xbootclasspathimagefds:" + Join(bcp_image_fds, ':'));
581     args.emplace_back("--runtime-arg");
582     args.emplace_back("-Xbootclasspathoatfds:" + Join(bcp_oat_fds, ':'));
583     args.emplace_back("--runtime-arg");
584     args.emplace_back("-Xbootclasspathvdexfds:" + Join(bcp_vdex_fds, ':'));
585   }
586 }
587 
GetStagingLocation(const std::string & staging_dir,const std::string & path)588 std::string GetStagingLocation(const std::string& staging_dir, const std::string& path) {
589   return staging_dir + "/" + Basename(path);
590 }
591 
CheckCompilationSpace()592 WARN_UNUSED bool CheckCompilationSpace() {
593   // Check the available storage space against an arbitrary threshold because dex2oat does not
594   // report when it runs out of storage space and we do not want to completely fill
595   // the users data partition.
596   //
597   // We do not have a good way of pre-computing the required space for a compilation step, but
598   // typically observe no more than 48MiB as the largest total size of AOT artifacts for a single
599   // dex2oat invocation, which includes an image file, an executable file, and a verification data
600   // file.
601   static constexpr uint64_t kMinimumSpaceForCompilation = 48 * 1024 * 1024;
602 
603   uint64_t bytes_available;
604   const std::string& art_apex_data_path = GetArtApexData();
605   if (!GetFreeSpace(art_apex_data_path, &bytes_available)) {
606     return false;
607   }
608 
609   if (bytes_available < kMinimumSpaceForCompilation) {
610     LOG(WARNING) << "Low space for " << QuotePath(art_apex_data_path) << " (" << bytes_available
611                  << " bytes)";
612     return false;
613   }
614 
615   return true;
616 }
617 
HasVettedDeviceSystemServerProfiles()618 bool HasVettedDeviceSystemServerProfiles() {
619   // While system_server profiles were bundled on the device prior to U+, they were not used by
620   // default or rigorously tested, so we cannot vouch for their efficacy.
621   static const bool kDeviceIsAtLeastU = IsAtLeastU();
622   return kDeviceIsAtLeastU;
623 }
624 
625 }  // namespace
626 
CompileAll(const OnDeviceRefresh & odr)627 CompilationOptions CompilationOptions::CompileAll(const OnDeviceRefresh& odr) {
628   CompilationOptions options;
629   for (InstructionSet isa : odr.Config().GetBootClasspathIsas()) {
630     options.boot_images_to_generate_for_isas.emplace_back(
631         isa, BootImages{.primary_boot_image = true, .boot_image_mainline_extension = true});
632   }
633   options.system_server_jars_to_compile = odr.AllSystemServerJars();
634   return options;
635 }
636 
Count() const637 int BootImages::Count() const {
638   int count = 0;
639   if (primary_boot_image) {
640     count++;
641   }
642   if (boot_image_mainline_extension) {
643     count++;
644   }
645   return count;
646 }
647 
GetTypeForMetrics() const648 OdrMetrics::BcpCompilationType BootImages::GetTypeForMetrics() const {
649   if (primary_boot_image && boot_image_mainline_extension) {
650     return OdrMetrics::BcpCompilationType::kPrimaryAndMainline;
651   }
652   if (boot_image_mainline_extension) {
653     return OdrMetrics::BcpCompilationType::kMainline;
654   }
655   LOG(FATAL) << "Unexpected BCP compilation type";
656   UNREACHABLE();
657 }
658 
CompilationUnitCount() const659 int CompilationOptions::CompilationUnitCount() const {
660   int count = 0;
661   for (const auto& [isa, boot_images] : boot_images_to_generate_for_isas) {
662     count += boot_images.Count();
663   }
664   count += system_server_jars_to_compile.size();
665   return count;
666 }
667 
OnDeviceRefresh(const OdrConfig & config)668 OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config)
669     : OnDeviceRefresh(config,
670                       config.GetArtifactDirectory() + "/" + kCacheInfoFile,
671                       std::make_unique<ExecUtils>()) {}
672 
OnDeviceRefresh(const OdrConfig & config,const std::string & cache_info_filename,std::unique_ptr<ExecUtils> exec_utils)673 OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config,
674                                  const std::string& cache_info_filename,
675                                  std::unique_ptr<ExecUtils> exec_utils)
676     : config_{config},
677       cache_info_filename_{cache_info_filename},
678       start_time_{time(nullptr)},
679       exec_utils_{std::move(exec_utils)} {
680   // Updatable APEXes should not have DEX files in the DEX2OATBOOTCLASSPATH. At the time of
681   // writing i18n is a non-updatable APEX and so does appear in the DEX2OATBOOTCLASSPATH.
682   dex2oat_boot_classpath_jars_ = Split(config_.GetDex2oatBootClasspath(), ":");
683 
684   all_systemserver_jars_ = Split(config_.GetSystemServerClasspath(), ":");
685   systemserver_classpath_jars_ = {all_systemserver_jars_.begin(), all_systemserver_jars_.end()};
686   boot_classpath_jars_ = Split(config_.GetBootClasspath(), ":");
687   std::string standalone_system_server_jars_str = config_.GetStandaloneSystemServerJars();
688   if (!standalone_system_server_jars_str.empty()) {
689     std::vector<std::string> standalone_systemserver_jars =
690         Split(standalone_system_server_jars_str, ":");
691     std::move(standalone_systemserver_jars.begin(),
692               standalone_systemserver_jars.end(),
693               std::back_inserter(all_systemserver_jars_));
694   }
695 }
696 
GetExecutionTimeUsed() const697 time_t OnDeviceRefresh::GetExecutionTimeUsed() const { return time(nullptr) - start_time_; }
698 
GetExecutionTimeRemaining() const699 time_t OnDeviceRefresh::GetExecutionTimeRemaining() const {
700   return std::max(static_cast<time_t>(0),
701                   kMaximumExecutionSeconds - GetExecutionTimeUsed());
702 }
703 
GetSubprocessTimeout() const704 time_t OnDeviceRefresh::GetSubprocessTimeout() const {
705   return std::min(GetExecutionTimeRemaining(), kMaxChildProcessSeconds);
706 }
707 
GetApexInfoList() const708 std::optional<std::vector<apex::ApexInfo>> OnDeviceRefresh::GetApexInfoList() const {
709   std::optional<apex::ApexInfoList> info_list =
710       apex::readApexInfoList(config_.GetApexInfoListFile().c_str());
711   if (!info_list.has_value()) {
712     return std::nullopt;
713   }
714 
715   // We are only interested in active APEXes that contain compilable JARs.
716   std::unordered_set<std::string_view> relevant_apexes;
717   relevant_apexes.reserve(info_list->getApexInfo().size());
718   for (const std::vector<std::string>* jar_list :
719        {&all_systemserver_jars_, &boot_classpath_jars_}) {
720     for (const std::string& jar : *jar_list) {
721       std::string_view apex = ApexNameFromLocation(jar);
722       if (!apex.empty()) {
723         relevant_apexes.insert(apex);
724       }
725     }
726   }
727   // The ART APEX is always relevant no matter it contains any compilable JAR or not, because it
728   // contains the runtime.
729   relevant_apexes.insert("com.android.art");
730 
731   std::vector<apex::ApexInfo> filtered_info_list;
732   std::copy_if(info_list->getApexInfo().begin(),
733                info_list->getApexInfo().end(),
734                std::back_inserter(filtered_info_list),
735                [&](const apex::ApexInfo& info) {
736                  return info.getIsActive() && relevant_apexes.count(info.getModuleName()) != 0;
737                });
738   return filtered_info_list;
739 }
740 
ReadCacheInfo() const741 Result<art_apex::CacheInfo> OnDeviceRefresh::ReadCacheInfo() const {
742   std::optional<art_apex::CacheInfo> cache_info = art_apex::read(cache_info_filename_.c_str());
743   if (!cache_info.has_value()) {
744     if (errno != 0) {
745       return ErrnoErrorf("Failed to load {}", QuotePath(cache_info_filename_));
746     } else {
747       return Errorf("Failed to parse {}", QuotePath(cache_info_filename_));
748     }
749   }
750   return cache_info.value();
751 }
752 
WriteCacheInfo() const753 Result<void> OnDeviceRefresh::WriteCacheInfo() const {
754   if (OS::FileExists(cache_info_filename_.c_str())) {
755     if (unlink(cache_info_filename_.c_str()) != 0) {
756       return ErrnoErrorf("Failed to unlink() file {}", QuotePath(cache_info_filename_));
757     }
758   }
759 
760   std::string dir_name = Dirname(cache_info_filename_);
761   if (!EnsureDirectoryExists(dir_name)) {
762     return Errorf("Could not create directory {}", QuotePath(dir_name));
763   }
764 
765   std::vector<art_apex::KeyValuePair> system_properties;
766   for (const auto& [key, value] : config_.GetSystemProperties()) {
767     system_properties.emplace_back(key, value);
768   }
769 
770   std::optional<std::vector<apex::ApexInfo>> apex_info_list = GetApexInfoList();
771   if (!apex_info_list.has_value()) {
772     return Errorf("Could not update {}: no APEX info", QuotePath(cache_info_filename_));
773   }
774 
775   std::optional<apex::ApexInfo> art_apex_info = GetArtApexInfo(apex_info_list.value());
776   if (!art_apex_info.has_value()) {
777     return Errorf("Could not update {}: no ART APEX info", QuotePath(cache_info_filename_));
778   }
779 
780   art_apex::ModuleInfo art_module_info = GenerateModuleInfo(art_apex_info.value());
781   std::vector<art_apex::ModuleInfo> module_info_list =
782       GenerateModuleInfoList(apex_info_list.value());
783 
784   std::vector<art_apex::Component> bcp_components = GenerateBootClasspathComponents();
785   std::vector<art_apex::Component> dex2oat_bcp_components =
786       GenerateDex2oatBootClasspathComponents();
787   std::vector<art_apex::SystemServerComponent> system_server_components =
788       GenerateSystemServerComponents();
789 
790   std::ofstream out(cache_info_filename_.c_str());
791   if (out.fail()) {
792     return Errorf("Cannot open {} for writing.", QuotePath(cache_info_filename_));
793   }
794 
795   std::unique_ptr<art_apex::CacheInfo> info(new art_apex::CacheInfo(
796       {art_apex::KeyValuePairList(system_properties)},
797       {art_module_info},
798       {art_apex::ModuleInfoList(module_info_list)},
799       {art_apex::Classpath(bcp_components)},
800       {art_apex::Classpath(dex2oat_bcp_components)},
801       {art_apex::SystemServerComponents(system_server_components)},
802       config_.GetCompilationOsMode() ? std::make_optional(true) : std::nullopt));
803 
804   art_apex::write(out, *info);
805   out.close();
806   if (out.fail()) {
807     return Errorf("Cannot write to {}", QuotePath(cache_info_filename_));
808   }
809 
810   return {};
811 }
812 
ReportNextBootAnimationProgress(uint32_t current_compilation,uint32_t number_of_compilations)813 static void ReportNextBootAnimationProgress(uint32_t current_compilation,
814                                             uint32_t number_of_compilations) {
815   // We arbitrarily show progress until 90%, expecting that our compilations take a large chunk of
816   // boot time.
817   uint32_t value = (90 * current_compilation) / number_of_compilations;
818   SetProperty("service.bootanim.progress", std::to_string(value));
819 }
820 
GenerateBootClasspathComponents() const821 std::vector<art_apex::Component> OnDeviceRefresh::GenerateBootClasspathComponents() const {
822   return GenerateComponents(boot_classpath_jars_);
823 }
824 
GenerateDex2oatBootClasspathComponents() const825 std::vector<art_apex::Component> OnDeviceRefresh::GenerateDex2oatBootClasspathComponents() const {
826   return GenerateComponents(dex2oat_boot_classpath_jars_);
827 }
828 
GenerateSystemServerComponents() const829 std::vector<art_apex::SystemServerComponent> OnDeviceRefresh::GenerateSystemServerComponents()
830     const {
831   return GenerateComponents<art_apex::SystemServerComponent>(
832       all_systemserver_jars_,
833       [&](const std::string& path, uint64_t size, const std::string& checksum) {
834         bool isInClasspath = ContainsElement(systemserver_classpath_jars_, path);
835         return art_apex::SystemServerComponent{path, size, checksum, isInClasspath};
836       });
837 }
838 
GetArtBcpJars() const839 std::vector<std::string> OnDeviceRefresh::GetArtBcpJars() const {
840   std::string art_root = GetArtRoot() + "/";
841   std::vector<std::string> art_bcp_jars;
842   for (const std::string& jar : dex2oat_boot_classpath_jars_) {
843     if (StartsWith(jar, art_root)) {
844       art_bcp_jars.push_back(jar);
845     }
846   }
847   CHECK(!art_bcp_jars.empty());
848   return art_bcp_jars;
849 }
850 
GetFrameworkBcpJars() const851 std::vector<std::string> OnDeviceRefresh::GetFrameworkBcpJars() const {
852   std::string art_root = GetArtRoot() + "/";
853   std::vector<std::string> framework_bcp_jars;
854   for (const std::string& jar : dex2oat_boot_classpath_jars_) {
855     if (!StartsWith(jar, art_root)) {
856       framework_bcp_jars.push_back(jar);
857     }
858   }
859   CHECK(!framework_bcp_jars.empty());
860   return framework_bcp_jars;
861 }
862 
GetMainlineBcpJars() const863 std::vector<std::string> OnDeviceRefresh::GetMainlineBcpJars() const {
864   // Elements in `dex2oat_boot_classpath_jars_` should be at the beginning of
865   // `boot_classpath_jars_`, followed by mainline BCP jars.
866   CHECK_LT(dex2oat_boot_classpath_jars_.size(), boot_classpath_jars_.size());
867   CHECK(std::equal(dex2oat_boot_classpath_jars_.begin(),
868                    dex2oat_boot_classpath_jars_.end(),
869                    boot_classpath_jars_.begin(),
870                    boot_classpath_jars_.begin() + dex2oat_boot_classpath_jars_.size()));
871   return {boot_classpath_jars_.begin() + dex2oat_boot_classpath_jars_.size(),
872           boot_classpath_jars_.end()};
873 }
874 
GetPrimaryBootImage(bool on_system,bool minimal) const875 std::string OnDeviceRefresh::GetPrimaryBootImage(bool on_system, bool minimal) const {
876   DCHECK(!on_system || !minimal);
877   const char* basename = minimal ? kMinimalBootImageBasename : kFirstBootImageBasename;
878   if (on_system) {
879     // Typically "/system/framework/boot.art".
880     return GetPrebuiltPrimaryBootImageDir() + "/" + basename;
881   } else {
882     // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot.art".
883     return config_.GetArtifactDirectory() + "/" + basename;
884   }
885 }
886 
GetPrimaryBootImagePath(bool on_system,bool minimal,InstructionSet isa) const887 std::string OnDeviceRefresh::GetPrimaryBootImagePath(bool on_system,
888                                                      bool minimal,
889                                                      InstructionSet isa) const {
890   // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot.art".
891   return GetSystemImageFilename(GetPrimaryBootImage(on_system, minimal).c_str(), isa);
892 }
893 
GetSystemBootImageFrameworkExtension() const894 std::string OnDeviceRefresh::GetSystemBootImageFrameworkExtension() const {
895   std::vector<std::string> framework_bcp_jars = GetFrameworkBcpJars();
896   std::string basename =
897       GetBootImageComponentBasename(framework_bcp_jars[0], /*is_first_jar=*/false);
898   // Typically "/system/framework/boot-framework.art".
899   return "{}/framework/{}"_format(GetAndroidRoot(), basename);
900 }
901 
GetSystemBootImageFrameworkExtensionPath(InstructionSet isa) const902 std::string OnDeviceRefresh::GetSystemBootImageFrameworkExtensionPath(InstructionSet isa) const {
903   // Typically "/system/framework/<isa>/boot-framework.art".
904   return GetSystemImageFilename(GetSystemBootImageFrameworkExtension().c_str(), isa);
905 }
906 
GetBootImageMainlineExtension(bool on_system) const907 std::string OnDeviceRefresh::GetBootImageMainlineExtension(bool on_system) const {
908   std::vector<std::string> mainline_bcp_jars = GetMainlineBcpJars();
909   std::string basename =
910       GetBootImageComponentBasename(mainline_bcp_jars[0], /*is_first_jar=*/false);
911   if (on_system) {
912     // Typically "/system/framework/boot-framework-adservices.art".
913     return "{}/framework/{}"_format(GetAndroidRoot(), basename);
914   } else {
915     // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework-adservices.art".
916     return "{}/{}"_format(config_.GetArtifactDirectory(), basename);
917   }
918 }
919 
GetBootImageMainlineExtensionPath(bool on_system,InstructionSet isa) const920 std::string OnDeviceRefresh::GetBootImageMainlineExtensionPath(bool on_system,
921                                                                InstructionSet isa) const {
922   // Typically
923   // "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot-framework-adservices.art".
924   return GetSystemImageFilename(GetBootImageMainlineExtension(on_system).c_str(), isa);
925 }
926 
GetBestBootImages(InstructionSet isa,bool include_mainline_extension) const927 std::vector<std::string> OnDeviceRefresh::GetBestBootImages(InstructionSet isa,
928                                                             bool include_mainline_extension) const {
929   std::vector<std::string> locations;
930   std::string unused_error_msg;
931   bool primary_on_data = false;
932   if (PrimaryBootImageExist(
933           /*on_system=*/false, /*minimal=*/false, isa, &unused_error_msg)) {
934     primary_on_data = true;
935     locations.push_back(GetPrimaryBootImage(/*on_system=*/false, /*minimal=*/false));
936   } else {
937     locations.push_back(GetPrimaryBootImage(/*on_system=*/true, /*minimal=*/false));
938     if (!IsAtLeastU()) {
939       // Prior to U, there was a framework extension.
940       locations.push_back(GetSystemBootImageFrameworkExtension());
941     }
942   }
943   if (include_mainline_extension) {
944     if (BootImageMainlineExtensionExist(/*on_system=*/false, isa, &unused_error_msg)) {
945       locations.push_back(GetBootImageMainlineExtension(/*on_system=*/false));
946     } else {
947       // If the primary boot image is on /data, it means we have regenerated all boot images, so the
948       // mainline extension must be on /data too.
949       CHECK(!primary_on_data)
950           << "Mainline extension not found while primary boot image is on /data";
951       locations.push_back(GetBootImageMainlineExtension(/*on_system=*/true));
952     }
953   }
954   return locations;
955 }
956 
GetSystemServerImagePath(bool on_system,const std::string & jar_path) const957 std::string OnDeviceRefresh::GetSystemServerImagePath(bool on_system,
958                                                       const std::string& jar_path) const {
959   if (on_system) {
960     if (LocationIsOnApex(jar_path)) {
961       return GetSystemOdexFilenameForApex(jar_path, config_.GetSystemServerIsa());
962     }
963     std::string jar_name = Basename(jar_path);
964     std::string image_name = ReplaceFileExtension(jar_name, "art");
965     const char* isa_str = GetInstructionSetString(config_.GetSystemServerIsa());
966     // Typically "/system/framework/oat/<isa>/services.art".
967     return "{}/oat/{}/{}"_format(Dirname(jar_path), isa_str, image_name);
968   } else {
969     // Typically
970     // "/data/misc/apexdata/.../dalvik-cache/<isa>/system@framework@services.jar@classes.art".
971     const std::string image = GetApexDataImage(jar_path);
972     return GetSystemImageFilename(image.c_str(), config_.GetSystemServerIsa());
973   }
974 }
975 
RemoveArtifactsDirectory() const976 WARN_UNUSED bool OnDeviceRefresh::RemoveArtifactsDirectory() const {
977   if (config_.GetDryRun()) {
978     LOG(INFO) << "Directory " << QuotePath(config_.GetArtifactDirectory())
979               << " and contents would be removed (dry-run).";
980     return true;
981   }
982   return RemoveDirectory(config_.GetArtifactDirectory());
983 }
984 
PrimaryBootImageExist(bool on_system,bool minimal,InstructionSet isa,std::string * error_msg,std::vector<std::string> * checked_artifacts) const985 WARN_UNUSED bool OnDeviceRefresh::PrimaryBootImageExist(
986     bool on_system,
987     bool minimal,
988     InstructionSet isa,
989     /*out*/ std::string* error_msg,
990     /*out*/ std::vector<std::string>* checked_artifacts) const {
991   std::string path = GetPrimaryBootImagePath(on_system, minimal, isa);
992   OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
993   if (!ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts)) {
994     return false;
995   }
996   // Prior to U, there was a split between the primary boot image and the extension on /system, so
997   // they need to be checked separately. This does not apply to the boot image on /data.
998   if (on_system && !IsAtLeastU()) {
999     std::string extension_path = GetSystemBootImageFrameworkExtensionPath(isa);
1000     OdrArtifacts extension_artifacts = OdrArtifacts::ForBootImage(extension_path);
1001     if (!ArtifactsExist(
1002             extension_artifacts, /*check_art_file=*/true, error_msg, checked_artifacts)) {
1003       return false;
1004     }
1005   }
1006   return true;
1007 }
1008 
BootImageMainlineExtensionExist(bool on_system,InstructionSet isa,std::string * error_msg,std::vector<std::string> * checked_artifacts) const1009 WARN_UNUSED bool OnDeviceRefresh::BootImageMainlineExtensionExist(
1010     bool on_system,
1011     InstructionSet isa,
1012     /*out*/ std::string* error_msg,
1013     /*out*/ std::vector<std::string>* checked_artifacts) const {
1014   std::string path = GetBootImageMainlineExtensionPath(on_system, isa);
1015   OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
1016   return ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts);
1017 }
1018 
SystemServerArtifactsExist(bool on_system,std::string * error_msg,std::set<std::string> * jars_missing_artifacts,std::vector<std::string> * checked_artifacts) const1019 bool OnDeviceRefresh::SystemServerArtifactsExist(
1020     bool on_system,
1021     /*out*/ std::string* error_msg,
1022     /*out*/ std::set<std::string>* jars_missing_artifacts,
1023     /*out*/ std::vector<std::string>* checked_artifacts) const {
1024   for (const std::string& jar_path : all_systemserver_jars_) {
1025     const std::string image_location = GetSystemServerImagePath(on_system, jar_path);
1026     const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
1027     // .art files are optional and are not generated for all jars by the build system.
1028     const bool check_art_file = !on_system;
1029     std::string error_msg_tmp;
1030     if (!ArtifactsExist(artifacts, check_art_file, &error_msg_tmp, checked_artifacts)) {
1031       jars_missing_artifacts->insert(jar_path);
1032       *error_msg = error_msg->empty() ? error_msg_tmp : *error_msg + "\n" + error_msg_tmp;
1033     }
1034   }
1035   return jars_missing_artifacts->empty();
1036 }
1037 
CheckSystemPropertiesAreDefault() const1038 WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesAreDefault() const {
1039   // We don't have to check properties that match `kCheckedSystemPropertyPrefixes` here because none
1040   // of them is persistent. This only applies when `cache-info.xml` does not exist. When
1041   // `cache-info.xml` exists, we call `CheckSystemPropertiesHaveNotChanged` instead.
1042   DCHECK(std::none_of(std::begin(kCheckedSystemPropertyPrefixes),
1043                       std::end(kCheckedSystemPropertyPrefixes),
1044                       [](const char* prefix) { return StartsWith(prefix, "persist."); }));
1045 
1046   const std::unordered_map<std::string, std::string>& system_properties =
1047       config_.GetSystemProperties();
1048 
1049   for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) {
1050     auto property = system_properties.find(system_property_config.name);
1051     DCHECK(property != system_properties.end());
1052 
1053     if (property->second != system_property_config.default_value) {
1054       LOG(INFO) << "System property " << system_property_config.name << " has a non-default value ("
1055                 << property->second << ").";
1056       return false;
1057     }
1058   }
1059 
1060   return true;
1061 }
1062 
CheckSystemPropertiesHaveNotChanged(const art_apex::CacheInfo & cache_info) const1063 WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesHaveNotChanged(
1064     const art_apex::CacheInfo& cache_info) const {
1065   std::unordered_map<std::string, std::string> cached_system_properties;
1066   std::unordered_set<std::string> checked_properties;
1067 
1068   const art_apex::KeyValuePairList* list = cache_info.getFirstSystemProperties();
1069   if (list == nullptr) {
1070     // This should never happen. We have already checked the ART module version, and the cache
1071     // info is generated by the latest version of the ART module if it exists.
1072     LOG(ERROR) << "Missing system properties from cache-info.";
1073     return false;
1074   }
1075 
1076   for (const art_apex::KeyValuePair& pair : list->getItem()) {
1077     cached_system_properties[pair.getK()] = pair.getV();
1078     checked_properties.insert(pair.getK());
1079   }
1080 
1081   const std::unordered_map<std::string, std::string>& system_properties =
1082       config_.GetSystemProperties();
1083 
1084   for (const auto& [key, value] : system_properties) {
1085     checked_properties.insert(key);
1086   }
1087 
1088   for (const std::string& name : checked_properties) {
1089     auto property_it = system_properties.find(name);
1090     std::string property = property_it != system_properties.end() ? property_it->second : "";
1091     std::string cached_property = cached_system_properties[name];
1092 
1093     if (property != cached_property) {
1094       LOG(INFO) << "System property " << name << " value changed (before: \"" << cached_property
1095                 << "\", now: \"" << property << "\").";
1096       return false;
1097     }
1098   }
1099 
1100   return true;
1101 }
1102 
CheckBuildUserfaultFdGc() const1103 WARN_UNUSED bool OnDeviceRefresh::CheckBuildUserfaultFdGc() const {
1104   auto it = config_.GetSystemProperties().find("ro.dalvik.vm.enable_uffd_gc");
1105   bool build_enable_uffd_gc = it != config_.GetSystemProperties().end() ?
1106                                   ParseBool(it->second) == ParseBoolResult::kTrue :
1107                                   false;
1108   bool kernel_supports_uffd = KernelSupportsUffd();
1109   if (build_enable_uffd_gc && !kernel_supports_uffd) {
1110     // Normally, this should not happen. If this happens, the system image was probably built with a
1111     // wrong PRODUCT_ENABLE_UFFD_GC flag.
1112     LOG(WARNING) << "Userfaultfd GC check failed (build-time: {}, runtime: {})."_format(
1113         build_enable_uffd_gc, kernel_supports_uffd);
1114     return false;
1115   }
1116   return true;
1117 }
1118 
CheckPreconditionForSystem(const std::vector<apex::ApexInfo> & apex_info_list) const1119 WARN_UNUSED PreconditionCheckResult OnDeviceRefresh::CheckPreconditionForSystem(
1120     const std::vector<apex::ApexInfo>& apex_info_list) const {
1121   if (!CheckSystemPropertiesAreDefault()) {
1122     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1123   }
1124 
1125   if (!CheckBuildUserfaultFdGc()) {
1126     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1127   }
1128 
1129   std::optional<apex::ApexInfo> art_apex_info = GetArtApexInfo(apex_info_list);
1130   if (!art_apex_info.has_value()) {
1131     // This should never happen, further up-to-date checks are not possible if it does.
1132     LOG(ERROR) << "Could not get ART APEX info.";
1133     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kUnknown);
1134   }
1135 
1136   if (!art_apex_info->getIsFactory()) {
1137     LOG(INFO) << "Updated ART APEX mounted";
1138     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1139   }
1140 
1141   if (std::any_of(apex_info_list.begin(),
1142                   apex_info_list.end(),
1143                   [](const apex::ApexInfo& apex_info) { return !apex_info.getIsFactory(); })) {
1144     LOG(INFO) << "Updated APEXes mounted";
1145     return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
1146         OdrMetrics::Trigger::kApexVersionMismatch);
1147   }
1148 
1149   return PreconditionCheckResult::AllOk();
1150 }
1151 
CheckModuleInfo(const art_apex::ModuleInfo & cached_info,const apex::ApexInfo & current_info)1152 WARN_UNUSED static bool CheckModuleInfo(const art_apex::ModuleInfo& cached_info,
1153                                         const apex::ApexInfo& current_info) {
1154   if (cached_info.getVersionCode() != current_info.getVersionCode()) {
1155     LOG(INFO) << "APEX ({}) version code mismatch (before: {}, now: {})"_format(
1156         current_info.getModuleName(), cached_info.getVersionCode(), current_info.getVersionCode());
1157     return false;
1158   }
1159 
1160   if (cached_info.getVersionName() != current_info.getVersionName()) {
1161     LOG(INFO) << "APEX ({}) version name mismatch (before: {}, now: {})"_format(
1162         current_info.getModuleName(), cached_info.getVersionName(), current_info.getVersionName());
1163     return false;
1164   }
1165 
1166   // Check lastUpdateMillis for samegrade installs. If `cached_info` is missing the lastUpdateMillis
1167   // field then it is not current with the schema used by this binary so treat it as a samegrade
1168   // update. Otherwise check whether the lastUpdateMillis changed.
1169   const int64_t cached_last_update_millis =
1170       cached_info.hasLastUpdateMillis() ? cached_info.getLastUpdateMillis() : -1;
1171   if (cached_last_update_millis != current_info.getLastUpdateMillis()) {
1172     LOG(INFO) << "APEX ({}) last update time mismatch (before: {}, now: {})"_format(
1173         current_info.getModuleName(),
1174         cached_info.getLastUpdateMillis(),
1175         current_info.getLastUpdateMillis());
1176     return false;
1177   }
1178 
1179   return true;
1180 }
1181 
CheckPreconditionForData(const std::vector<com::android::apex::ApexInfo> & apex_info_list) const1182 WARN_UNUSED PreconditionCheckResult OnDeviceRefresh::CheckPreconditionForData(
1183     const std::vector<com::android::apex::ApexInfo>& apex_info_list) const {
1184   Result<art_apex::CacheInfo> cache_info = ReadCacheInfo();
1185   if (!cache_info.ok()) {
1186     if (cache_info.error().code() == ENOENT) {
1187       // If the cache info file does not exist, it usually means it's the first boot, or the
1188       // dalvik-cache directory is cleared by odsign due to corrupted files. Set the trigger to be
1189       // `kApexVersionMismatch` to force generate the cache info file and compile if necessary.
1190       LOG(INFO) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
1191     } else {
1192       // This should not happen unless odrefresh is updated to a new version that is not compatible
1193       // with an old cache-info file. Further up-to-date checks are not possible if it does.
1194       LOG(ERROR) << cache_info.error().message();
1195     }
1196     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1197   }
1198 
1199   if (!CheckSystemPropertiesHaveNotChanged(cache_info.value())) {
1200     // We don't have a trigger kind for system property changes. For now, we reuse
1201     // `kApexVersionMismatch` as it implies the expected behavior: re-compile regardless of the last
1202     // compilation attempt.
1203     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1204   }
1205 
1206   // Check whether the current cache ART module info differs from the current ART module info.
1207   const art_apex::ModuleInfo* cached_art_info = cache_info->getFirstArtModuleInfo();
1208   if (cached_art_info == nullptr) {
1209     LOG(ERROR) << "Missing ART APEX info from cache-info.";
1210     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1211   }
1212 
1213   std::optional<apex::ApexInfo> current_art_info = GetArtApexInfo(apex_info_list);
1214   if (!current_art_info.has_value()) {
1215     // This should never happen, further up-to-date checks are not possible if it does.
1216     LOG(ERROR) << "Could not get ART APEX info.";
1217     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kUnknown);
1218   }
1219 
1220   if (!CheckModuleInfo(*cached_art_info, *current_art_info)) {
1221     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1222   }
1223 
1224   // Check boot class components.
1225   //
1226   // This checks the size and checksums of odrefresh compilable files on the DEX2OATBOOTCLASSPATH
1227   // (the Odrefresh constructor determines which files are compilable). If the number of files
1228   // there changes, or their size or checksums change then compilation will be triggered.
1229   //
1230   // The boot class components may change unexpectedly, for example an OTA could update
1231   // framework.jar.
1232   const std::vector<art_apex::Component> current_dex2oat_bcp_components =
1233       GenerateDex2oatBootClasspathComponents();
1234 
1235   const art_apex::Classpath* cached_dex2oat_bcp_components =
1236       cache_info->getFirstDex2oatBootClasspath();
1237   if (cached_dex2oat_bcp_components == nullptr) {
1238     LOG(INFO) << "Missing Dex2oatBootClasspath components.";
1239     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
1240   }
1241 
1242   Result<void> result = CheckComponents(current_dex2oat_bcp_components,
1243                                         cached_dex2oat_bcp_components->getComponent());
1244   if (!result.ok()) {
1245     LOG(INFO) << "Dex2OatClasspath components mismatch: " << result.error();
1246     return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kDexFilesChanged);
1247   }
1248 
1249   // Check whether the current cached module info differs from the current module info.
1250   const art_apex::ModuleInfoList* cached_module_info_list = cache_info->getFirstModuleInfoList();
1251   if (cached_module_info_list == nullptr) {
1252     LOG(ERROR) << "Missing APEX info list from cache-info.";
1253     return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
1254         OdrMetrics::Trigger::kApexVersionMismatch);
1255   }
1256 
1257   std::unordered_map<std::string, const art_apex::ModuleInfo*> cached_module_info_map;
1258   for (const art_apex::ModuleInfo& module_info : cached_module_info_list->getModuleInfo()) {
1259     cached_module_info_map[module_info.getName()] = &module_info;
1260   }
1261 
1262   // Note that apex_info_list may omit APEXes that are included in cached_module_info - e.g. if an
1263   // apex used to be compilable, but now isn't. That won't be detected by this loop, but will be
1264   // detected below in CheckComponents.
1265   for (const apex::ApexInfo& current_apex_info : apex_info_list) {
1266     auto& apex_name = current_apex_info.getModuleName();
1267 
1268     auto it = cached_module_info_map.find(apex_name);
1269     if (it == cached_module_info_map.end()) {
1270       LOG(INFO) << "Missing APEX info from cache-info (" << apex_name << ").";
1271       return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
1272           OdrMetrics::Trigger::kApexVersionMismatch);
1273     }
1274 
1275     const art_apex::ModuleInfo* cached_module_info = it->second;
1276     if (!CheckModuleInfo(*cached_module_info, current_apex_info)) {
1277       return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
1278           OdrMetrics::Trigger::kApexVersionMismatch);
1279     }
1280   }
1281 
1282   const std::vector<art_apex::Component> current_bcp_components = GenerateBootClasspathComponents();
1283 
1284   const art_apex::Classpath* cached_bcp_components = cache_info->getFirstBootClasspath();
1285   if (cached_bcp_components == nullptr) {
1286     LOG(INFO) << "Missing BootClasspath components.";
1287     return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
1288         OdrMetrics::Trigger::kApexVersionMismatch);
1289   }
1290 
1291   result = CheckComponents(current_bcp_components, cached_bcp_components->getComponent());
1292   if (!result.ok()) {
1293     LOG(INFO) << "BootClasspath components mismatch: " << result.error();
1294     // Boot classpath components can be dependencies of system_server components, so system_server
1295     // components need to be recompiled if boot classpath components are changed.
1296     return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
1297         OdrMetrics::Trigger::kDexFilesChanged);
1298   }
1299 
1300   // Check system server components.
1301   //
1302   // This checks the size and checksums of odrefresh compilable files on the
1303   // SYSTEMSERVERCLASSPATH (the Odrefresh constructor determines which files are compilable). If
1304   // the number of files there changes, or their size or checksums change then compilation will be
1305   // triggered.
1306   //
1307   // The system_server components may change unexpectedly, for example an OTA could update
1308   // services.jar.
1309   const std::vector<art_apex::SystemServerComponent> current_system_server_components =
1310       GenerateSystemServerComponents();
1311 
1312   const art_apex::SystemServerComponents* cached_system_server_components =
1313       cache_info->getFirstSystemServerComponents();
1314   if (cached_system_server_components == nullptr) {
1315     LOG(INFO) << "Missing SystemServerComponents.";
1316     return PreconditionCheckResult::SystemServerNotOk(OdrMetrics::Trigger::kApexVersionMismatch);
1317   }
1318 
1319   result = CheckSystemServerComponents(current_system_server_components,
1320                                        cached_system_server_components->getComponent());
1321   if (!result.ok()) {
1322     LOG(INFO) << "SystemServerComponents mismatch: " << result.error();
1323     return PreconditionCheckResult::SystemServerNotOk(OdrMetrics::Trigger::kDexFilesChanged);
1324   }
1325 
1326   return PreconditionCheckResult::AllOk();
1327 }
1328 
CheckBootClasspathArtifactsAreUpToDate(OdrMetrics & metrics,InstructionSet isa,const PreconditionCheckResult & system_result,const PreconditionCheckResult & data_result,std::vector<std::string> * checked_artifacts) const1329 WARN_UNUSED BootImages OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate(
1330     OdrMetrics& metrics,
1331     InstructionSet isa,
1332     const PreconditionCheckResult& system_result,
1333     const PreconditionCheckResult& data_result,
1334     /*out*/ std::vector<std::string>* checked_artifacts) const {
1335   const char* isa_str = GetInstructionSetString(isa);
1336 
1337   BootImages boot_images_on_system{.primary_boot_image = false,
1338                                    .boot_image_mainline_extension = false};
1339   if (system_result.IsPrimaryBootImageOk()) {
1340     // We can use the artifacts on /system. Check if they exist.
1341     std::string error_msg;
1342     if (PrimaryBootImageExist(/*on_system=*/true, /*minimal=*/false, isa, &error_msg)) {
1343       boot_images_on_system.primary_boot_image = true;
1344     } else {
1345       LOG(INFO) << "Incomplete primary boot image or framework extension on /system: " << error_msg;
1346     }
1347   }
1348 
1349   if (boot_images_on_system.primary_boot_image && system_result.IsBootImageMainlineExtensionOk()) {
1350     std::string error_msg;
1351     if (BootImageMainlineExtensionExist(/*on_system=*/true, isa, &error_msg)) {
1352       boot_images_on_system.boot_image_mainline_extension = true;
1353     } else {
1354       LOG(INFO) << "Incomplete boot image mainline extension on /system: " << error_msg;
1355     }
1356   }
1357 
1358   if (boot_images_on_system.Count() == BootImages::kMaxCount) {
1359     LOG(INFO) << "Boot images on /system OK ({})"_format(isa_str);
1360     // Nothing to compile.
1361     return BootImages{.primary_boot_image = false, .boot_image_mainline_extension = false};
1362   }
1363 
1364   LOG(INFO) << "Checking boot images /data ({})"_format(isa_str);
1365   BootImages boot_images_on_data{.primary_boot_image = false,
1366                                  .boot_image_mainline_extension = false};
1367 
1368   if (data_result.IsPrimaryBootImageOk()) {
1369     std::string error_msg;
1370     if (PrimaryBootImageExist(
1371             /*on_system=*/false, /*minimal=*/false, isa, &error_msg, checked_artifacts)) {
1372       boot_images_on_data.primary_boot_image = true;
1373     } else {
1374       LOG(INFO) << "Incomplete primary boot image on /data: " << error_msg;
1375       metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
1376       // Add the minimal boot image to `checked_artifacts` if exists. This is to prevent the minimal
1377       // boot image from being deleted. It does not affect the return value because we should still
1378       // attempt to generate a full boot image even if the minimal one exists.
1379       if (PrimaryBootImageExist(
1380               /*on_system=*/false, /*minimal=*/true, isa, &error_msg, checked_artifacts)) {
1381         LOG(INFO) << "Found minimal primary boot image ({})"_format(isa_str);
1382       }
1383     }
1384   } else {
1385     metrics.SetTrigger(data_result.GetTrigger());
1386   }
1387 
1388   if (boot_images_on_data.primary_boot_image) {
1389     if (data_result.IsBootImageMainlineExtensionOk()) {
1390       std::string error_msg;
1391       if (BootImageMainlineExtensionExist(
1392               /*on_system=*/false, isa, &error_msg, checked_artifacts)) {
1393         boot_images_on_data.boot_image_mainline_extension = true;
1394       } else {
1395         LOG(INFO) << "Incomplete boot image mainline extension on /data: " << error_msg;
1396         metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
1397       }
1398     } else {
1399       metrics.SetTrigger(data_result.GetTrigger());
1400     }
1401   }
1402 
1403   BootImages boot_images_to_generate{
1404       .primary_boot_image =
1405           !boot_images_on_system.primary_boot_image && !boot_images_on_data.primary_boot_image,
1406       .boot_image_mainline_extension = !boot_images_on_system.boot_image_mainline_extension &&
1407                                        !boot_images_on_data.boot_image_mainline_extension,
1408   };
1409 
1410   if (boot_images_to_generate.Count() == 0) {
1411     LOG(INFO) << "Boot images on /data OK ({})"_format(isa_str);
1412   }
1413 
1414   return boot_images_to_generate;
1415 }
1416 
CheckSystemServerArtifactsAreUpToDate(OdrMetrics & metrics,const PreconditionCheckResult & system_result,const PreconditionCheckResult & data_result,std::vector<std::string> * checked_artifacts) const1417 std::set<std::string> OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate(
1418     OdrMetrics& metrics,
1419     const PreconditionCheckResult& system_result,
1420     const PreconditionCheckResult& data_result,
1421     /*out*/ std::vector<std::string>* checked_artifacts) const {
1422   std::set<std::string> jars_to_compile;
1423   std::set<std::string> jars_missing_artifacts_on_system;
1424   if (system_result.IsSystemServerOk()) {
1425     // We can use the artifacts on /system. Check if they exist.
1426     std::string error_msg;
1427     if (SystemServerArtifactsExist(
1428             /*on_system=*/true, &error_msg, &jars_missing_artifacts_on_system)) {
1429       LOG(INFO) << "system_server artifacts on /system OK";
1430       return {};
1431     }
1432 
1433     LOG(INFO) << "Incomplete system server artifacts on /system: " << error_msg;
1434     LOG(INFO) << "Checking system server artifacts /data";
1435   } else {
1436     jars_missing_artifacts_on_system = AllSystemServerJars();
1437   }
1438 
1439   std::set<std::string> jars_missing_artifacts_on_data;
1440   std::string error_msg;
1441   if (data_result.IsSystemServerOk()) {
1442     SystemServerArtifactsExist(
1443         /*on_system=*/false, &error_msg, &jars_missing_artifacts_on_data, checked_artifacts);
1444   } else {
1445     jars_missing_artifacts_on_data = AllSystemServerJars();
1446   }
1447 
1448   std::set_intersection(jars_missing_artifacts_on_system.begin(),
1449                         jars_missing_artifacts_on_system.end(),
1450                         jars_missing_artifacts_on_data.begin(),
1451                         jars_missing_artifacts_on_data.end(),
1452                         std::inserter(jars_to_compile, jars_to_compile.end()));
1453   if (!jars_to_compile.empty()) {
1454     if (data_result.IsSystemServerOk()) {
1455       LOG(INFO) << "Incomplete system_server artifacts on /data: " << error_msg;
1456       metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
1457     } else {
1458       metrics.SetTrigger(data_result.GetTrigger());
1459     }
1460     return jars_to_compile;
1461   }
1462 
1463   LOG(INFO) << "system_server artifacts on /data OK";
1464   return {};
1465 }
1466 
CleanupArtifactDirectory(OdrMetrics & metrics,const std::vector<std::string> & artifacts_to_keep) const1467 Result<void> OnDeviceRefresh::CleanupArtifactDirectory(
1468     OdrMetrics& metrics, const std::vector<std::string>& artifacts_to_keep) const {
1469   const std::string& artifact_dir = config_.GetArtifactDirectory();
1470   std::unordered_set<std::string> artifact_set{artifacts_to_keep.begin(), artifacts_to_keep.end()};
1471 
1472   // When anything unexpected happens, remove all artifacts.
1473   auto remove_artifact_dir = android::base::make_scope_guard([&]() {
1474     if (!RemoveDirectory(artifact_dir)) {
1475       LOG(ERROR) << "Failed to remove the artifact directory";
1476     }
1477   });
1478 
1479   std::vector<std::filesystem::directory_entry> entries;
1480   std::error_code ec;
1481   for (const auto& entry : std::filesystem::recursive_directory_iterator(artifact_dir, ec)) {
1482     // Save the entries and use them later because modifications during the iteration will result in
1483     // undefined behavior;
1484     entries.push_back(entry);
1485   }
1486   if (ec && ec.value() != ENOENT) {
1487     metrics.SetStatus(ec.value() == EPERM ? OdrMetrics::Status::kDalvikCachePermissionDenied :
1488                                             OdrMetrics::Status::kIoError);
1489     return Errorf("Failed to iterate over entries in the artifact directory: {}", ec.message());
1490   }
1491 
1492   for (const std::filesystem::directory_entry& entry : entries) {
1493     std::string path = entry.path().string();
1494     if (entry.is_regular_file()) {
1495       if (!ContainsElement(artifact_set, path)) {
1496         LOG(INFO) << "Removing " << path;
1497         if (unlink(path.c_str()) != 0) {
1498           metrics.SetStatus(OdrMetrics::Status::kIoError);
1499           return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
1500         }
1501       }
1502     } else if (!entry.is_directory()) {
1503       // Neither a regular file nor a directory. Unexpected file type.
1504       LOG(INFO) << "Removing " << path;
1505       if (unlink(path.c_str()) != 0) {
1506         metrics.SetStatus(OdrMetrics::Status::kIoError);
1507         return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
1508       }
1509     }
1510   }
1511 
1512   remove_artifact_dir.Disable();
1513   return {};
1514 }
1515 
RefreshExistingArtifacts() const1516 Result<void> OnDeviceRefresh::RefreshExistingArtifacts() const {
1517   const std::string& artifact_dir = config_.GetArtifactDirectory();
1518   if (!OS::DirectoryExists(artifact_dir.c_str())) {
1519     return {};
1520   }
1521 
1522   std::vector<std::filesystem::directory_entry> entries;
1523   std::error_code ec;
1524   for (const auto& entry : std::filesystem::recursive_directory_iterator(artifact_dir, ec)) {
1525     // Save the entries and use them later because modifications during the iteration will result in
1526     // undefined behavior;
1527     entries.push_back(entry);
1528   }
1529   if (ec) {
1530     return Errorf("Failed to iterate over entries in the artifact directory: {}", ec.message());
1531   }
1532 
1533   for (const std::filesystem::directory_entry& entry : entries) {
1534     std::string path = entry.path().string();
1535     if (entry.is_regular_file()) {
1536       // Unexpected files are already removed by `CleanupArtifactDirectory`. We can safely assume
1537       // that all the remaining files are good.
1538       LOG(INFO) << "Refreshing " << path;
1539       std::string content;
1540       if (!android::base::ReadFileToString(path, &content)) {
1541         return Errorf("Failed to read file {}", QuotePath(path));
1542       }
1543       if (unlink(path.c_str()) != 0) {
1544         return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
1545       }
1546       if (!android::base::WriteStringToFile(content, path)) {
1547         return Errorf("Failed to write file {}", QuotePath(path));
1548       }
1549       if (chmod(path.c_str(), kFileMode) != 0) {
1550         return ErrnoErrorf("Failed to chmod file {}", QuotePath(path));
1551       }
1552     }
1553   }
1554 
1555   return {};
1556 }
1557 
1558 WARN_UNUSED ExitCode
CheckArtifactsAreUpToDate(OdrMetrics & metrics,CompilationOptions * compilation_options) const1559 OnDeviceRefresh::CheckArtifactsAreUpToDate(OdrMetrics& metrics,
1560                                            /*out*/ CompilationOptions* compilation_options) const {
1561   metrics.SetStage(OdrMetrics::Stage::kCheck);
1562 
1563   // Clean-up helper used to simplify clean-ups and handling failures there.
1564   auto cleanup_and_compile_all = [&, this]() {
1565     *compilation_options = CompilationOptions::CompileAll(*this);
1566     if (!RemoveArtifactsDirectory()) {
1567       metrics.SetStatus(OdrMetrics::Status::kIoError);
1568       return ExitCode::kCleanupFailed;
1569     }
1570     return ExitCode::kCompilationRequired;
1571   };
1572 
1573   std::optional<std::vector<apex::ApexInfo>> apex_info_list = GetApexInfoList();
1574   if (!apex_info_list.has_value()) {
1575     // This should never happen, further up-to-date checks are not possible if it does.
1576     LOG(ERROR) << "Could not get APEX info.";
1577     metrics.SetTrigger(OdrMetrics::Trigger::kUnknown);
1578     return cleanup_and_compile_all();
1579   }
1580 
1581   std::optional<apex::ApexInfo> art_apex_info = GetArtApexInfo(apex_info_list.value());
1582   if (!art_apex_info.has_value()) {
1583     // This should never happen, further up-to-date checks are not possible if it does.
1584     LOG(ERROR) << "Could not get ART APEX info.";
1585     metrics.SetTrigger(OdrMetrics::Trigger::kUnknown);
1586     return cleanup_and_compile_all();
1587   }
1588 
1589   // Record ART APEX version for metrics reporting.
1590   metrics.SetArtApexVersion(art_apex_info->getVersionCode());
1591 
1592   // Log the version so there's a starting point for any issues reported (b/197489543).
1593   LOG(INFO) << "ART APEX version " << art_apex_info->getVersionCode();
1594 
1595   // Record ART APEX last update milliseconds (used in compilation log).
1596   metrics.SetArtApexLastUpdateMillis(art_apex_info->getLastUpdateMillis());
1597 
1598   InstructionSet system_server_isa = config_.GetSystemServerIsa();
1599   std::vector<std::string> checked_artifacts;
1600 
1601   PreconditionCheckResult system_result = CheckPreconditionForSystem(apex_info_list.value());
1602   PreconditionCheckResult data_result = CheckPreconditionForData(apex_info_list.value());
1603 
1604   for (InstructionSet isa : config_.GetBootClasspathIsas()) {
1605     BootImages boot_images_to_generate = CheckBootClasspathArtifactsAreUpToDate(
1606         metrics, isa, system_result, data_result, &checked_artifacts);
1607     if (boot_images_to_generate.Count() > 0) {
1608       compilation_options->boot_images_to_generate_for_isas.emplace_back(isa,
1609                                                                          boot_images_to_generate);
1610       // system_server artifacts are invalid without valid boot classpath artifacts.
1611       if (isa == system_server_isa) {
1612         compilation_options->system_server_jars_to_compile = AllSystemServerJars();
1613       }
1614     }
1615   }
1616 
1617   if (compilation_options->system_server_jars_to_compile.empty()) {
1618     compilation_options->system_server_jars_to_compile = CheckSystemServerArtifactsAreUpToDate(
1619         metrics, system_result, data_result, &checked_artifacts);
1620   }
1621 
1622   bool compilation_required = compilation_options->CompilationUnitCount() > 0;
1623 
1624   if (!compilation_required && !data_result.IsAllOk()) {
1625     // Return kCompilationRequired to generate the cache info even if there's nothing to compile.
1626     compilation_required = true;
1627     metrics.SetTrigger(data_result.GetTrigger());
1628   }
1629 
1630   // If partial compilation is disabled, we should compile everything regardless of what's in
1631   // `compilation_options`.
1632   if (compilation_required && !config_.GetPartialCompilation()) {
1633     return cleanup_and_compile_all();
1634   }
1635 
1636   // Always keep the cache info.
1637   checked_artifacts.push_back(cache_info_filename_);
1638 
1639   Result<void> result = CleanupArtifactDirectory(metrics, checked_artifacts);
1640   if (!result.ok()) {
1641     LOG(ERROR) << result.error();
1642     return ExitCode::kCleanupFailed;
1643   }
1644 
1645   return compilation_required ? ExitCode::kCompilationRequired : ExitCode::kOkay;
1646 }
1647 
RunDex2oat(const std::string & staging_dir,const std::string & debug_message,InstructionSet isa,const std::vector<std::string> & dex_files,const std::vector<std::string> & boot_classpath,const std::vector<std::string> & input_boot_images,const OdrArtifacts & artifacts,const std::vector<std::string> & extra_args,std::vector<std::unique_ptr<File>> & readonly_files_raii) const1648 WARN_UNUSED CompilationResult OnDeviceRefresh::RunDex2oat(
1649     const std::string& staging_dir,
1650     const std::string& debug_message,
1651     InstructionSet isa,
1652     const std::vector<std::string>& dex_files,
1653     const std::vector<std::string>& boot_classpath,
1654     const std::vector<std::string>& input_boot_images,
1655     const OdrArtifacts& artifacts,
1656     const std::vector<std::string>& extra_args,
1657     /*inout*/ std::vector<std::unique_ptr<File>>& readonly_files_raii) const {
1658   std::vector<std::string> args;
1659   args.push_back(config_.GetDex2Oat());
1660 
1661   AddDex2OatCommonOptions(args);
1662   AddDex2OatDebugInfo(args);
1663   AddDex2OatInstructionSet(args, isa);
1664   Result<void> result = AddDex2OatConcurrencyArguments(args, config_.GetCompilationOsMode());
1665   if (!result.ok()) {
1666     return CompilationResult::Error(OdrMetrics::Status::kUnknown, result.error().message());
1667   }
1668 
1669   // dex2oat reads some system properties from cache-info.xml generated by odrefresh.
1670   result = AddCacheInfoFd(args, readonly_files_raii, cache_info_filename_);
1671   if (!result.ok()) {
1672     return CompilationResult::Error(OdrMetrics::Status::kUnknown, result.error().message());
1673   }
1674 
1675   for (const std::string& dex_file : dex_files) {
1676     std::string actual_path = RewriteParentDirectoryIfNeeded(dex_file);
1677     args.emplace_back("--dex-file=" + dex_file);
1678     std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
1679     args.emplace_back(StringPrintf("--dex-fd=%d", file->Fd()));
1680     readonly_files_raii.push_back(std::move(file));
1681   }
1682 
1683   args.emplace_back("--runtime-arg");
1684   args.emplace_back("-Xbootclasspath:" + Join(boot_classpath, ":"));
1685   result = AddBootClasspathFds(args, readonly_files_raii, boot_classpath);
1686   if (!result.ok()) {
1687     return CompilationResult::Error(OdrMetrics::Status::kIoError, result.error().message());
1688   }
1689 
1690   if (!input_boot_images.empty()) {
1691     args.emplace_back("--boot-image=" + Join(input_boot_images, ':'));
1692     AddCompiledBootClasspathFdsIfAny(
1693         args, readonly_files_raii, boot_classpath, isa, input_boot_images);
1694   }
1695 
1696   args.emplace_back("--oat-location=" + artifacts.OatPath());
1697   std::pair<std::string, const char*> location_kind_pairs[] = {
1698       std::make_pair(artifacts.ImagePath(), artifacts.ImageKind()),
1699       std::make_pair(artifacts.OatPath(), "oat"),
1700       std::make_pair(artifacts.VdexPath(), "output-vdex")};
1701   std::vector<std::unique_ptr<File>> staging_files;
1702   for (const auto& [location, kind] : location_kind_pairs) {
1703     std::string staging_location = GetStagingLocation(staging_dir, location);
1704     std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
1705     if (staging_file == nullptr) {
1706       return CompilationResult::Error(
1707           OdrMetrics::Status::kIoError,
1708           "Failed to create {} file '{}'"_format(kind, staging_location));
1709     }
1710     // Don't check the state of the staging file. It doesn't need to be flushed because it's removed
1711     // after the compilation regardless of success or failure.
1712     staging_file->MarkUnchecked();
1713     args.emplace_back(StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
1714     staging_files.emplace_back(std::move(staging_file));
1715   }
1716 
1717   std::string install_location = Dirname(artifacts.OatPath());
1718   if (!EnsureDirectoryExists(install_location)) {
1719     return CompilationResult::Error(
1720         OdrMetrics::Status::kIoError,
1721         "Error encountered when preparing directory '{}'"_format(install_location));
1722   }
1723 
1724   std::copy(extra_args.begin(), extra_args.end(), std::back_inserter(args));
1725 
1726   Timer timer;
1727   time_t timeout = GetSubprocessTimeout();
1728   std::string cmd_line = Join(args, ' ');
1729   LOG(INFO) << "{}: {} [timeout {}s]"_format(debug_message, cmd_line, timeout);
1730   if (config_.GetDryRun()) {
1731     LOG(INFO) << "Compilation skipped (dry-run).";
1732     return CompilationResult::Ok();
1733   }
1734 
1735   std::string error_msg;
1736   ExecResult dex2oat_result = exec_utils_->ExecAndReturnResult(args, timeout, &error_msg);
1737 
1738   if (dex2oat_result.exit_code != 0) {
1739     return CompilationResult::Dex2oatError(
1740         dex2oat_result.exit_code < 0 ?
1741             error_msg :
1742             "dex2oat returned an unexpected code: {}"_format(dex2oat_result.exit_code),
1743         timer.duration().count(),
1744         dex2oat_result);
1745   }
1746 
1747   if (!MoveOrEraseFiles(staging_files, install_location)) {
1748     return CompilationResult::Error(OdrMetrics::Status::kIoError,
1749                                     "Failed to commit artifacts to '{}'"_format(install_location));
1750   }
1751 
1752   return CompilationResult::Dex2oatOk(timer.duration().count(), dex2oat_result);
1753 }
1754 
1755 WARN_UNUSED CompilationResult
RunDex2oatForBootClasspath(const std::string & staging_dir,const std::string & debug_name,InstructionSet isa,const std::vector<std::string> & dex_files,const std::vector<std::string> & boot_classpath,const std::vector<std::string> & input_boot_images,const std::string & output_path) const1756 OnDeviceRefresh::RunDex2oatForBootClasspath(const std::string& staging_dir,
1757                                             const std::string& debug_name,
1758                                             InstructionSet isa,
1759                                             const std::vector<std::string>& dex_files,
1760                                             const std::vector<std::string>& boot_classpath,
1761                                             const std::vector<std::string>& input_boot_images,
1762                                             const std::string& output_path) const {
1763   std::vector<std::string> args;
1764   std::vector<std::unique_ptr<File>> readonly_files_raii;
1765 
1766   // Compile as a single image for fewer files and slightly less memory overhead.
1767   args.emplace_back("--single-image");
1768 
1769   if (input_boot_images.empty()) {
1770     // Primary boot image.
1771     std::string art_boot_profile_file = GetArtRoot() + "/etc/boot-image.prof";
1772     std::string framework_boot_profile_file = GetAndroidRoot() + "/etc/boot-image.prof";
1773     bool has_any_profile = AddDex2OatProfile(
1774         args, readonly_files_raii, {art_boot_profile_file, framework_boot_profile_file});
1775     if (!has_any_profile) {
1776       return CompilationResult::Error(OdrMetrics::Status::kIoError, "Missing boot image profile");
1777     }
1778     const std::string& compiler_filter = config_.GetBootImageCompilerFilter();
1779     if (!compiler_filter.empty()) {
1780       args.emplace_back("--compiler-filter=" + compiler_filter);
1781     } else {
1782       args.emplace_back(StringPrintf("--compiler-filter=%s", kPrimaryCompilerFilter));
1783     }
1784 
1785     args.emplace_back(StringPrintf("--base=0x%08x", ART_BASE_ADDRESS));
1786 
1787     std::string dirty_image_objects_file(GetAndroidRoot() + "/etc/dirty-image-objects");
1788     if (OS::FileExists(dirty_image_objects_file.c_str())) {
1789       std::unique_ptr<File> file(OS::OpenFileForReading(dirty_image_objects_file.c_str()));
1790       args.emplace_back(StringPrintf("--dirty-image-objects-fd=%d", file->Fd()));
1791       readonly_files_raii.push_back(std::move(file));
1792     } else {
1793       LOG(WARNING) << "Missing dirty objects file: '{}'"_format(dirty_image_objects_file);
1794     }
1795 
1796     std::string preloaded_classes_file(GetAndroidRoot() + "/etc/preloaded-classes");
1797     if (OS::FileExists(preloaded_classes_file.c_str())) {
1798       std::unique_ptr<File> file(OS::OpenFileForReading(preloaded_classes_file.c_str()));
1799       args.emplace_back(StringPrintf("--preloaded-classes-fds=%d", file->Fd()));
1800       readonly_files_raii.push_back(std::move(file));
1801     } else {
1802       LOG(WARNING) << "Missing preloaded classes file: '{}'"_format(preloaded_classes_file);
1803     }
1804   } else {
1805     // Mainline extension.
1806     args.emplace_back(StringPrintf("--compiler-filter=%s", kMainlineCompilerFilter));
1807   }
1808 
1809   return RunDex2oat(
1810       staging_dir,
1811       "Compiling boot classpath ({}, {})"_format(GetInstructionSetString(isa), debug_name),
1812       isa,
1813       dex_files,
1814       boot_classpath,
1815       input_boot_images,
1816       OdrArtifacts::ForBootImage(output_path),
1817       args,
1818       readonly_files_raii);
1819 }
1820 
1821 WARN_UNUSED CompilationResult
CompileBootClasspath(const std::string & staging_dir,InstructionSet isa,BootImages boot_images,const std::function<void ()> & on_dex2oat_success) const1822 OnDeviceRefresh::CompileBootClasspath(const std::string& staging_dir,
1823                                       InstructionSet isa,
1824                                       BootImages boot_images,
1825                                       const std::function<void()>& on_dex2oat_success) const {
1826   DCHECK_GT(boot_images.Count(), 0);
1827   DCHECK_IMPLIES(boot_images.primary_boot_image, boot_images.boot_image_mainline_extension);
1828 
1829   CompilationResult result = CompilationResult::Ok();
1830 
1831   if (config_.GetMinimal()) {
1832     result.Merge(
1833         CompilationResult::Error(OdrMetrics::Status::kUnknown, "Minimal boot image requested"));
1834   }
1835 
1836   if (!CheckCompilationSpace()) {
1837     result.Merge(CompilationResult::Error(OdrMetrics::Status::kNoSpace, "Insufficient space"));
1838   }
1839 
1840   if (result.IsOk() && boot_images.primary_boot_image) {
1841     CompilationResult primary_result = RunDex2oatForBootClasspath(
1842         staging_dir,
1843         "primary",
1844         isa,
1845         dex2oat_boot_classpath_jars_,
1846         dex2oat_boot_classpath_jars_,
1847         /*input_boot_images=*/{},
1848         GetPrimaryBootImagePath(/*on_system=*/false, /*minimal=*/false, isa));
1849     result.Merge(primary_result);
1850 
1851     if (primary_result.IsOk()) {
1852       on_dex2oat_success();
1853 
1854       // Remove the minimal boot image only if the full boot image is successfully generated.
1855       std::string path = GetPrimaryBootImagePath(/*on_system=*/false, /*minimal=*/true, isa);
1856       OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
1857       unlink(artifacts.ImagePath().c_str());
1858       unlink(artifacts.OatPath().c_str());
1859       unlink(artifacts.VdexPath().c_str());
1860     }
1861   }
1862 
1863   if (!result.IsOk() && boot_images.primary_boot_image) {
1864     LOG(ERROR) << "Compilation of primary BCP failed: " << result.error_msg;
1865 
1866     // Fall back to generating a minimal boot image.
1867     // The compilation of the full boot image will be retried on later reboots with a backoff
1868     // time, and the minimal boot image will be removed once the compilation of the full boot
1869     // image succeeds.
1870     std::string ignored_error_msg;
1871     if (PrimaryBootImageExist(
1872             /*on_system=*/false, /*minimal=*/true, isa, &ignored_error_msg)) {
1873       LOG(INFO) << "Minimal boot image already up-to-date";
1874       return result;
1875     }
1876     std::vector<std::string> art_bcp_jars = GetArtBcpJars();
1877     CompilationResult minimal_result = RunDex2oatForBootClasspath(
1878         staging_dir,
1879         "minimal",
1880         isa,
1881         art_bcp_jars,
1882         art_bcp_jars,
1883         /*input_boot_images=*/{},
1884         GetPrimaryBootImagePath(/*on_system=*/false, /*minimal=*/true, isa));
1885     result.Merge(minimal_result);
1886 
1887     if (!minimal_result.IsOk()) {
1888       LOG(ERROR) << "Compilation of minimal BCP failed: " << result.error_msg;
1889     }
1890 
1891     return result;
1892   }
1893 
1894   if (result.IsOk() && boot_images.boot_image_mainline_extension) {
1895     CompilationResult mainline_result =
1896         RunDex2oatForBootClasspath(staging_dir,
1897                                    "mainline",
1898                                    isa,
1899                                    GetMainlineBcpJars(),
1900                                    boot_classpath_jars_,
1901                                    GetBestBootImages(isa, /*include_mainline_extension=*/false),
1902                                    GetBootImageMainlineExtensionPath(/*on_system=*/false, isa));
1903     result.Merge(mainline_result);
1904 
1905     if (mainline_result.IsOk()) {
1906       on_dex2oat_success();
1907     }
1908   }
1909 
1910   if (!result.IsOk() && boot_images.boot_image_mainline_extension) {
1911     LOG(ERROR) << "Compilation of mainline BCP failed: " << result.error_msg;
1912   }
1913 
1914   return result;
1915 }
1916 
RunDex2oatForSystemServer(const std::string & staging_dir,const std::string & dex_file,const std::vector<std::string> & classloader_context) const1917 WARN_UNUSED CompilationResult OnDeviceRefresh::RunDex2oatForSystemServer(
1918     const std::string& staging_dir,
1919     const std::string& dex_file,
1920     const std::vector<std::string>& classloader_context) const {
1921   std::vector<std::string> args;
1922   std::vector<std::unique_ptr<File>> readonly_files_raii;
1923   InstructionSet isa = config_.GetSystemServerIsa();
1924   std::string output_path = GetSystemServerImagePath(/*on_system=*/false, dex_file);
1925 
1926   std::string actual_jar_path = RewriteParentDirectoryIfNeeded(dex_file);
1927   std::string profile = actual_jar_path + ".prof";
1928   const std::string& compiler_filter = config_.GetSystemServerCompilerFilter();
1929   bool maybe_add_profile = !compiler_filter.empty() || HasVettedDeviceSystemServerProfiles();
1930   bool has_added_profile =
1931       maybe_add_profile && AddDex2OatProfile(args, readonly_files_raii, {profile});
1932   if (!compiler_filter.empty()) {
1933     args.emplace_back("--compiler-filter=" + compiler_filter);
1934   } else if (has_added_profile) {
1935     args.emplace_back("--compiler-filter=speed-profile");
1936   } else {
1937     args.emplace_back("--compiler-filter=speed");
1938   }
1939 
1940   std::string context_path = Join(classloader_context, ':');
1941   if (art::ContainsElement(systemserver_classpath_jars_, dex_file)) {
1942     args.emplace_back("--class-loader-context=PCL[" + context_path + "]");
1943   } else {
1944     args.emplace_back("--class-loader-context=PCL[];PCL[" + context_path + "]");
1945   }
1946   if (!classloader_context.empty()) {
1947     std::vector<int> fds;
1948     for (const std::string& path : classloader_context) {
1949       std::string actual_path = RewriteParentDirectoryIfNeeded(path);
1950       std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
1951       if (!file->IsValid()) {
1952         return CompilationResult::Error(
1953             OdrMetrics::Status::kIoError,
1954             "Failed to open classloader context '{}': {}"_format(actual_path, strerror(errno)));
1955       }
1956       fds.emplace_back(file->Fd());
1957       readonly_files_raii.emplace_back(std::move(file));
1958     }
1959     args.emplace_back("--class-loader-context-fds=" + Join(fds, ':'));
1960   }
1961 
1962   return RunDex2oat(staging_dir,
1963                     "Compiling {}"_format(Basename(dex_file)),
1964                     isa,
1965                     {dex_file},
1966                     boot_classpath_jars_,
1967                     GetBestBootImages(isa, /*include_mainline_extension=*/true),
1968                     OdrArtifacts::ForSystemServer(output_path),
1969                     args,
1970                     readonly_files_raii);
1971 }
1972 
1973 WARN_UNUSED CompilationResult
CompileSystemServer(const std::string & staging_dir,const std::set<std::string> & system_server_jars_to_compile,const std::function<void ()> & on_dex2oat_success) const1974 OnDeviceRefresh::CompileSystemServer(const std::string& staging_dir,
1975                                      const std::set<std::string>& system_server_jars_to_compile,
1976                                      const std::function<void()>& on_dex2oat_success) const {
1977   DCHECK(!system_server_jars_to_compile.empty());
1978 
1979   CompilationResult result = CompilationResult::Ok();
1980   std::vector<std::string> classloader_context;
1981 
1982   if (!CheckCompilationSpace()) {
1983     LOG(ERROR) << "Compilation of system_server failed: Insufficient space";
1984     return CompilationResult::Error(OdrMetrics::Status::kNoSpace, "Insufficient space");
1985   }
1986 
1987   for (const std::string& jar : all_systemserver_jars_) {
1988     if (ContainsElement(system_server_jars_to_compile, jar)) {
1989       CompilationResult current_result =
1990           RunDex2oatForSystemServer(staging_dir, jar, classloader_context);
1991       result.Merge(current_result);
1992 
1993       if (current_result.IsOk()) {
1994         on_dex2oat_success();
1995       } else {
1996         LOG(ERROR) << "Compilation of {} failed: {}"_format(Basename(jar), result.error_msg);
1997       }
1998     }
1999 
2000     if (ContainsElement(systemserver_classpath_jars_, jar)) {
2001       classloader_context.emplace_back(jar);
2002     }
2003   }
2004 
2005   return result;
2006 }
2007 
Compile(OdrMetrics & metrics,const CompilationOptions & compilation_options) const2008 WARN_UNUSED ExitCode OnDeviceRefresh::Compile(OdrMetrics& metrics,
2009                                               const CompilationOptions& compilation_options) const {
2010   const char* staging_dir = nullptr;
2011   metrics.SetStage(OdrMetrics::Stage::kPreparation);
2012 
2013   if (!EnsureDirectoryExists(config_.GetArtifactDirectory())) {
2014     LOG(ERROR) << "Failed to prepare artifact directory";
2015     metrics.SetStatus(errno == EPERM ? OdrMetrics::Status::kDalvikCachePermissionDenied :
2016                                        OdrMetrics::Status::kIoError);
2017     return ExitCode::kCleanupFailed;
2018   }
2019 
2020   if (config_.GetRefresh()) {
2021     Result<void> result = RefreshExistingArtifacts();
2022     if (!result.ok()) {
2023       LOG(ERROR) << "Failed to refresh existing artifacts: " << result.error();
2024       metrics.SetStatus(OdrMetrics::Status::kIoError);
2025       return ExitCode::kCleanupFailed;
2026     }
2027   }
2028 
2029   // Emit cache info before compiling. This can be used to throttle compilation attempts later.
2030   Result<void> result = WriteCacheInfo();
2031   if (!result.ok()) {
2032     LOG(ERROR) << result.error();
2033     metrics.SetStatus(OdrMetrics::Status::kIoError);
2034     return ExitCode::kCleanupFailed;
2035   }
2036 
2037   if (!config_.GetStagingDir().empty()) {
2038     staging_dir = config_.GetStagingDir().c_str();
2039   } else {
2040     // Create staging area and assign label for generating compilation artifacts.
2041     if (PaletteCreateOdrefreshStagingDirectory(&staging_dir) != PALETTE_STATUS_OK) {
2042       metrics.SetStatus(OdrMetrics::Status::kStagingFailed);
2043       return ExitCode::kCleanupFailed;
2044     }
2045   }
2046 
2047   std::string error_msg;
2048 
2049   uint32_t dex2oat_invocation_count = 0;
2050   uint32_t total_dex2oat_invocation_count = compilation_options.CompilationUnitCount();
2051   ReportNextBootAnimationProgress(dex2oat_invocation_count, total_dex2oat_invocation_count);
2052   auto advance_animation_progress = [&]() {
2053     ReportNextBootAnimationProgress(++dex2oat_invocation_count, total_dex2oat_invocation_count);
2054   };
2055 
2056   const std::vector<InstructionSet>& bcp_instruction_sets = config_.GetBootClasspathIsas();
2057   DCHECK(!bcp_instruction_sets.empty() && bcp_instruction_sets.size() <= 2);
2058   InstructionSet system_server_isa = config_.GetSystemServerIsa();
2059 
2060   bool system_server_isa_failed = false;
2061   std::optional<std::pair<OdrMetrics::Stage, OdrMetrics::Status>> first_failure;
2062 
2063   for (const auto& [isa, boot_images_to_generate] :
2064        compilation_options.boot_images_to_generate_for_isas) {
2065     OdrMetrics::Stage stage = (isa == bcp_instruction_sets.front()) ?
2066                                   OdrMetrics::Stage::kPrimaryBootClasspath :
2067                                   OdrMetrics::Stage::kSecondaryBootClasspath;
2068     CompilationResult bcp_result =
2069         CompileBootClasspath(staging_dir, isa, boot_images_to_generate, advance_animation_progress);
2070     metrics.SetDex2OatResult(stage, bcp_result.elapsed_time_ms, bcp_result.dex2oat_result);
2071     metrics.SetBcpCompilationType(stage, boot_images_to_generate.GetTypeForMetrics());
2072     if (!bcp_result.IsOk()) {
2073       if (isa == system_server_isa) {
2074         system_server_isa_failed = true;
2075       }
2076       first_failure = first_failure.value_or(std::make_pair(stage, bcp_result.status));
2077     }
2078   }
2079 
2080   // Don't compile system server if the compilation of BCP failed.
2081   if (!system_server_isa_failed && !compilation_options.system_server_jars_to_compile.empty()) {
2082     OdrMetrics::Stage stage = OdrMetrics::Stage::kSystemServerClasspath;
2083     CompilationResult ss_result = CompileSystemServer(
2084         staging_dir, compilation_options.system_server_jars_to_compile, advance_animation_progress);
2085     metrics.SetDex2OatResult(stage, ss_result.elapsed_time_ms, ss_result.dex2oat_result);
2086     if (!ss_result.IsOk()) {
2087       first_failure = first_failure.value_or(std::make_pair(stage, ss_result.status));
2088     }
2089   }
2090 
2091   if (first_failure.has_value()) {
2092     metrics.SetStage(first_failure->first);
2093     metrics.SetStatus(first_failure->second);
2094 
2095     if (!config_.GetDryRun() && !RemoveDirectory(staging_dir)) {
2096       return ExitCode::kCleanupFailed;
2097     }
2098     return ExitCode::kCompilationFailed;
2099   }
2100 
2101   metrics.SetStage(OdrMetrics::Stage::kComplete);
2102   metrics.SetStatus(OdrMetrics::Status::kOK);
2103   return ExitCode::kCompilationSuccess;
2104 }
2105 
2106 }  // namespace odrefresh
2107 }  // namespace art
2108