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