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