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