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