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