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