• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "odrefresh/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 <cstdlib>
34 #include <fstream>
35 #include <initializer_list>
36 #include <iosfwd>
37 #include <iostream>
38 #include <memory>
39 #include <optional>
40 #include <ostream>
41 #include <sstream>
42 #include <string>
43 #include <string_view>
44 #include <type_traits>
45 #include <utility>
46 #include <vector>
47 
48 #include "android-base/file.h"
49 #include "android-base/logging.h"
50 #include "android-base/macros.h"
51 #include "android-base/properties.h"
52 #include "android-base/stringprintf.h"
53 #include "android-base/strings.h"
54 #include "android/log.h"
55 #include "arch/instruction_set.h"
56 #include "base/file_utils.h"
57 #include "base/globals.h"
58 #include "base/macros.h"
59 #include "base/os.h"
60 #include "base/string_view_cpp20.h"
61 #include "base/unix_file/fd_file.h"
62 #include "com_android_apex.h"
63 #include "com_android_art.h"
64 #include "dex/art_dex_file_loader.h"
65 #include "dexoptanalyzer.h"
66 #include "exec_utils.h"
67 #include "log/log.h"
68 #include "palette/palette.h"
69 #include "palette/palette_types.h"
70 
71 #include "odr_artifacts.h"
72 #include "odr_compilation_log.h"
73 #include "odr_config.h"
74 #include "odr_fs_utils.h"
75 #include "odr_metrics.h"
76 
77 namespace art {
78 namespace odrefresh {
79 
80 namespace apex = com::android::apex;
81 namespace art_apex = com::android::art;
82 
83 namespace {
84 
85 // Name of cache info file in the ART Apex artifact cache.
86 static constexpr const char* kCacheInfoFile = "cache-info.xml";
87 
UsageErrorV(const char * fmt,va_list ap)88 static void UsageErrorV(const char* fmt, va_list ap) {
89   std::string error;
90   android::base::StringAppendV(&error, fmt, ap);
91   if (isatty(fileno(stderr))) {
92     std::cerr << error << std::endl;
93   } else {
94     LOG(ERROR) << error;
95   }
96 }
97 
UsageError(const char * fmt,...)98 static void UsageError(const char* fmt, ...) {
99   va_list ap;
100   va_start(ap, fmt);
101   UsageErrorV(fmt, ap);
102   va_end(ap);
103 }
104 
ArgumentError(const char * fmt,...)105 NO_RETURN static void ArgumentError(const char* fmt, ...) {
106   va_list ap;
107   va_start(ap, fmt);
108   UsageErrorV(fmt, ap);
109   va_end(ap);
110   UsageError("Try '--help' for more information.");
111   exit(EX_USAGE);
112 }
113 
UsageHelp(const char * argv0)114 NO_RETURN static void UsageHelp(const char* argv0) {
115   std::string name(android::base::Basename(argv0));
116   UsageError("Usage: %s ACTION", name.c_str());
117   UsageError("On-device refresh tool for boot class path extensions and system server");
118   UsageError("following an update of the ART APEX.");
119   UsageError("");
120   UsageError("Valid ACTION choices are:");
121   UsageError("");
122   UsageError(
123       "--check          Check compilation artifacts are up-to-date based on metadata (fast).");
124   UsageError("--compile        Compile boot class path extensions and system_server jars");
125   UsageError("                 when necessary.");
126   UsageError("--force-compile  Unconditionally compile the boot class path extensions and");
127   UsageError("                 system_server jars.");
128   UsageError("--verify         Verify artifacts are up-to-date with dexoptanalyzer (slow).");
129   UsageError("--help           Display this help information.");
130   exit(EX_USAGE);
131 }
132 
Concatenate(std::initializer_list<std::string_view> args)133 static std::string Concatenate(std::initializer_list<std::string_view> args) {
134   std::stringstream ss;
135   for (auto arg : args) {
136     ss << arg;
137   }
138   return ss.str();
139 }
140 
GetEnvironmentVariableOrDie(const char * name)141 static std::string GetEnvironmentVariableOrDie(const char* name) {
142   const char* value = getenv(name);
143   LOG_ALWAYS_FATAL_IF(value == nullptr, "%s is not defined.", name);
144   return value;
145 }
146 
QuotePath(std::string_view path)147 static std::string QuotePath(std::string_view path) {
148   return Concatenate({"'", path, "'"});
149 }
150 
EraseFiles(const std::vector<std::unique_ptr<File>> & files)151 static void EraseFiles(const std::vector<std::unique_ptr<File>>& files) {
152   for (auto& file : files) {
153     file->Erase(/*unlink=*/true);
154   }
155 }
156 
157 // Moves `files` to the directory `output_directory_path`.
158 //
159 // If any of the files cannot be moved, then all copies of the files are removed from both
160 // the original location and the output location.
161 //
162 // Returns true if all files are moved, false otherwise.
MoveOrEraseFiles(const std::vector<std::unique_ptr<File>> & files,std::string_view output_directory_path)163 static bool MoveOrEraseFiles(const std::vector<std::unique_ptr<File>>& files,
164                              std::string_view output_directory_path) {
165   std::vector<std::unique_ptr<File>> output_files;
166   for (auto& file : files) {
167     const std::string file_basename(android::base::Basename(file->GetPath()));
168     const std::string output_file_path = Concatenate({output_directory_path, "/", file_basename});
169     const std::string input_file_path = file->GetPath();
170 
171     output_files.emplace_back(OS::CreateEmptyFileWriteOnly(output_file_path.c_str()));
172     if (output_files.back() == nullptr) {
173       PLOG(ERROR) << "Failed to open " << QuotePath(output_file_path);
174       output_files.pop_back();
175       EraseFiles(output_files);
176       EraseFiles(files);
177       return false;
178     }
179 
180     static constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
181     if (fchmod(output_files.back()->Fd(), kFileMode) != 0) {
182       PLOG(ERROR) << "Could not set file mode on " << QuotePath(output_file_path);
183       EraseFiles(output_files);
184       EraseFiles(files);
185       return false;
186     }
187 
188     const size_t file_bytes = file->GetLength();
189     if (!output_files.back()->Copy(file.get(), /*offset=*/0, file_bytes)) {
190       PLOG(ERROR) << "Failed to copy " << QuotePath(file->GetPath())
191                   << " to " << QuotePath(output_file_path);
192       EraseFiles(output_files);
193       EraseFiles(files);
194       return false;
195     }
196 
197     if (!file->Erase(/*unlink=*/true)) {
198       PLOG(ERROR) << "Failed to erase " << QuotePath(file->GetPath());
199       EraseFiles(output_files);
200       EraseFiles(files);
201       return false;
202     }
203 
204     if (output_files.back()->FlushCloseOrErase() != 0) {
205       PLOG(ERROR) << "Failed to flush and close file " << QuotePath(output_file_path);
206       EraseFiles(output_files);
207       EraseFiles(files);
208       return false;
209     }
210   }
211   return true;
212 }
213 
214 }  // namespace
215 
ParseZygoteKind(const char * input,ZygoteKind * zygote_kind)216 bool ParseZygoteKind(const char* input, ZygoteKind* zygote_kind) {
217   std::string_view z(input);
218   if (z == "zygote32") {
219     *zygote_kind = ZygoteKind::kZygote32;
220     return true;
221   } else if (z == "zygote32_64") {
222     *zygote_kind = ZygoteKind::kZygote32_64;
223     return true;
224   } else if (z == "zygote64_32") {
225     *zygote_kind = ZygoteKind::kZygote64_32;
226     return true;
227   } else if (z == "zygote64") {
228     *zygote_kind = ZygoteKind::kZygote64;
229     return true;
230   }
231   return false;
232 }
233 
234 class OnDeviceRefresh final {
235  private:
236   // Maximum execution time for odrefresh from start to end.
237   static constexpr time_t kMaximumExecutionSeconds = 300;
238 
239   // Maximum execution time for any child process spawned.
240   static constexpr time_t kMaxChildProcessSeconds = 90;
241 
242   // Configuration to use.
243   const OdrConfig& config_;
244 
245   // Path to cache information file that is used to speed up artifact checking.
246   const std::string cache_info_filename_;
247 
248   // List of boot extension components that should be compiled.
249   std::vector<std::string> boot_extension_compilable_jars_;
250 
251   // List of system_server components that should be compiled.
252   std::vector<std::string> systemserver_compilable_jars_;
253 
254   const time_t start_time_;
255 
256  public:
OnDeviceRefresh(const OdrConfig & config)257   explicit OnDeviceRefresh(const OdrConfig& config)
258       : config_{config},
259         cache_info_filename_{Concatenate({kOdrefreshArtifactDirectory, "/", kCacheInfoFile})},
260         start_time_{time(nullptr)} {
261     for (const std::string& jar : android::base::Split(config_.GetDex2oatBootClasspath(), ":")) {
262       // Boot class path extensions are those not in the ART APEX. Updatable APEXes should not
263       // have DEX files in the DEX2OATBOOTCLASSPATH. At the time of writing i18n is a non-updatable
264       // APEX and so does appear in the DEX2OATBOOTCLASSPATH.
265       if (!LocationIsOnArtModule(jar)) {
266         boot_extension_compilable_jars_.emplace_back(jar);
267       }
268     }
269 
270     for (const std::string& jar : android::base::Split(config_.GetSystemServerClasspath(), ":")) {
271       // Only consider DEX files on the SYSTEMSERVERCLASSPATH for compilation that do not reside
272       // in APEX modules. Otherwise, we'll recompile on boot any time one of these APEXes updates.
273       if (!LocationIsOnApex(jar)) {
274         systemserver_compilable_jars_.emplace_back(jar);
275       }
276     }
277   }
278 
GetExecutionTimeUsed() const279   time_t GetExecutionTimeUsed() const { return time(nullptr) - start_time_; }
280 
GetExecutionTimeRemaining() const281   time_t GetExecutionTimeRemaining() const {
282     return kMaximumExecutionSeconds - GetExecutionTimeUsed();
283   }
284 
GetSubprocessTimeout() const285   time_t GetSubprocessTimeout() const {
286     return std::max(GetExecutionTimeRemaining(), kMaxChildProcessSeconds);
287   }
288 
289   // Gets the `ApexInfo` associated with the currently active ART APEX.
GetArtApexInfo() const290   std::optional<apex::ApexInfo> GetArtApexInfo() const {
291     auto info_list = apex::readApexInfoList(config_.GetApexInfoListFile().c_str());
292     if (!info_list.has_value()) {
293       return {};
294     }
295 
296     for (const apex::ApexInfo& info : info_list->getApexInfo()) {
297       if (info.getIsActive() && info.getModuleName() == "com.android.art") {
298         return info;
299       }
300     }
301     return {};
302   }
303 
304   // Reads the ART APEX cache information (if any) found in `kOdrefreshArtifactDirectory`.
ReadCacheInfo()305   std::optional<art_apex::CacheInfo> ReadCacheInfo() {
306     return art_apex::read(cache_info_filename_.c_str());
307   }
308 
309   // Write ART APEX cache information to `kOdrefreshArtifactDirectory`.
WriteCacheInfo() const310   void WriteCacheInfo() const {
311     if (OS::FileExists(cache_info_filename_.c_str())) {
312       if (unlink(cache_info_filename_.c_str()) != 0) {
313         PLOG(ERROR) << "Failed to unlink() file " << QuotePath(cache_info_filename_);
314       }
315     }
316 
317     const std::string dir_name = android::base::Dirname(cache_info_filename_);
318     if (!EnsureDirectoryExists(dir_name)) {
319       LOG(ERROR) << "Could not create directory: " << QuotePath(dir_name);
320       return;
321     }
322 
323     std::optional<art_apex::ArtModuleInfo> art_module_info = GenerateArtModuleInfo();
324     if (!art_module_info.has_value()) {
325       LOG(ERROR) << "Unable to generate cache provenance";
326       return;
327     }
328 
329     // There can be only one CacheProvence in the XML file, but `xsdc` does not have
330     // minOccurs/maxOccurs in the xsd schema.
331     const std::vector<art_apex::ArtModuleInfo> art_module_infos { art_module_info.value() };
332 
333     std::optional<std::vector<art_apex::Component>> bcp_components =
334         GenerateBootExtensionComponents();
335     if (!bcp_components.has_value()) {
336       LOG(ERROR) << "No boot classpath extension components.";
337       return;
338     }
339 
340     std::optional<std::vector<art_apex::Component>> system_server_components =
341         GenerateSystemServerComponents();
342     if (!system_server_components.has_value()) {
343       LOG(ERROR) << "No system_server extension components.";
344       return;
345     }
346 
347     std::ofstream out(cache_info_filename_.c_str());
348     art_apex::CacheInfo info{art_module_infos,
349                              {{art_apex::Dex2oatBootClasspath{bcp_components.value()}}},
350                              {{art_apex::SystemServerClasspath{system_server_components.value()}}}};
351 
352     art_apex::write(out, info);
353   }
354 
355   // Returns cache provenance information based on the current ART APEX version and filesystem
356   // information.
GenerateArtModuleInfo() const357   std::optional<art_apex::ArtModuleInfo> GenerateArtModuleInfo() const {
358     auto info = GetArtApexInfo();
359     if (!info.has_value()) {
360       LOG(ERROR) << "Could not update " << QuotePath(cache_info_filename_) << " : no ART Apex info";
361       return {};
362     }
363     // The lastUpdateMillis is an addition to ApexInfoList.xsd to support samegrade installs.
364     int64_t last_update_millis = info->hasLastUpdateMillis() ? info->getLastUpdateMillis() : 0;
365     return art_apex::ArtModuleInfo{
366         info->getVersionCode(), info->getVersionName(), last_update_millis};
367   }
368 
CheckComponents(const std::vector<art_apex::Component> & expected_components,const std::vector<art_apex::Component> & actual_components,std::string * error_msg) const369   bool CheckComponents(const std::vector<art_apex::Component>& expected_components,
370                        const std::vector<art_apex::Component>& actual_components,
371                        std::string* error_msg) const {
372     if (expected_components.size() != actual_components.size()) {
373       return false;
374     }
375 
376     for (size_t i = 0; i < expected_components.size(); ++i) {
377       const art_apex::Component& expected = expected_components[i];
378       const art_apex::Component& actual = actual_components[i];
379 
380       if (expected.getFile() != actual.getFile()) {
381         *error_msg = android::base::StringPrintf("Component %zu file differs ('%s' != '%s')",
382                                                  i,
383                                                  expected.getFile().c_str(),
384                                                  actual.getFile().c_str());
385         return false;
386       }
387       if (expected.getSize() != actual.getSize()) {
388         *error_msg = android::base::StringPrintf("Component %zu size differs (%" PRIu64
389                                                  " != %" PRIu64 ")",
390                                                  i,
391                                                  expected.getSize(),
392                                                  actual.getSize());
393         return false;
394       }
395       if (expected.getChecksums() != actual.getChecksums()) {
396         *error_msg = android::base::StringPrintf("Component %zu checksums differ ('%s' != '%s')",
397                                                  i,
398                                                  expected.getChecksums().c_str(),
399                                                  actual.getChecksums().c_str());
400         return false;
401       }
402     }
403 
404     return true;
405   }
406 
GenerateComponents(const std::vector<std::string> & jars) const407   std::vector<art_apex::Component> GenerateComponents(const std::vector<std::string>& jars) const {
408     std::vector<art_apex::Component> components;
409 
410     ArtDexFileLoader loader;
411     for (const std::string& path : jars) {
412       struct stat sb;
413       if (stat(path.c_str(), &sb) == -1) {
414         PLOG(ERROR) << "Failed to get component: " << QuotePath(path);
415         return {};
416       }
417 
418       std::vector<uint32_t> checksums;
419       std::vector<std::string> dex_locations;
420       std::string error_msg;
421       if (!loader.GetMultiDexChecksums(path.c_str(), &checksums, &dex_locations, &error_msg)) {
422         LOG(ERROR) << "Failed to get components: " << error_msg;
423         return {};
424       }
425 
426       std::ostringstream oss;
427       for (size_t i = 0; i < checksums.size(); ++i) {
428         if (i != 0) {
429           oss << ';';
430         }
431         oss << android::base::StringPrintf("%08x", checksums[i]);
432       }
433       const std::string checksum = oss.str();
434 
435       components.emplace_back(
436           art_apex::Component{path, static_cast<uint64_t>(sb.st_size), checksum});
437     }
438 
439     return components;
440   }
441 
GenerateBootExtensionComponents() const442   std::vector<art_apex::Component> GenerateBootExtensionComponents() const {
443     return GenerateComponents(boot_extension_compilable_jars_);
444   }
445 
GenerateSystemServerComponents() const446   std::vector<art_apex::Component> GenerateSystemServerComponents() const {
447     return GenerateComponents(systemserver_compilable_jars_);
448   }
449 
450   // Checks whether a group of artifacts exists. Returns true if all are present, false otherwise.
ArtifactsExist(const OdrArtifacts & artifacts,std::string * error_msg)451   static bool ArtifactsExist(const OdrArtifacts& artifacts, /*out*/ std::string* error_msg) {
452     const auto paths = {
453         artifacts.ImagePath().c_str(), artifacts.OatPath().c_str(), artifacts.VdexPath().c_str()};
454     for (const char* path : paths) {
455       if (!OS::FileExists(path)) {
456         if (errno == EACCES) {
457           PLOG(ERROR) << "Failed to stat() " << path;
458         }
459         *error_msg = "Missing file: " + QuotePath(path);
460         return false;
461       }
462     }
463     return true;
464   }
465 
466   // Checks whether all boot extension artifacts are present on /data. Returns true if all are
467   // present, false otherwise.
BootExtensionArtifactsExistOnData(const InstructionSet isa,std::string * error_msg) const468   WARN_UNUSED bool BootExtensionArtifactsExistOnData(const InstructionSet isa,
469                                                      /*out*/ std::string* error_msg) const {
470     const std::string apexdata_image_location = GetBootImageExtensionImagePath(isa);
471     const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(apexdata_image_location);
472     return ArtifactsExist(artifacts, error_msg);
473   }
474 
475   // Checks whether all system_server artifacts are present on /data. The artifacts are checked in
476   // their order of compilation. Returns true if all are present, false otherwise.
SystemServerArtifactsExistOnData(std::string * error_msg) const477   WARN_UNUSED bool SystemServerArtifactsExistOnData(/*out*/ std::string* error_msg) const {
478     for (const std::string& jar_path : systemserver_compilable_jars_) {
479       const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar_path);
480       const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
481       if (!ArtifactsExist(artifacts, error_msg)) {
482         return false;
483       }
484     }
485     return true;
486   }
487 
CheckArtifactsAreUpToDate(OdrMetrics & metrics)488   WARN_UNUSED ExitCode CheckArtifactsAreUpToDate(OdrMetrics& metrics) {
489     metrics.SetStage(OdrMetrics::Stage::kCheck);
490 
491     // Clean-up helper used to simplify clean-ups and handling failures there.
492     auto cleanup_return = [this](ExitCode exit_code) {
493       return CleanApexdataDirectory() ? exit_code : ExitCode::kCleanupFailed;
494     };
495 
496     const auto apex_info = GetArtApexInfo();
497     if (!apex_info.has_value()) {
498       // This should never happen, further up-to-date checks are not possible if it does.
499       LOG(ERROR) << "Could not get ART APEX info.";
500       metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
501       return cleanup_return(ExitCode::kCompilationRequired);
502     }
503 
504     // Generate current module info for the current ART APEX.
505     const auto current_info = GenerateArtModuleInfo();
506     if (!current_info.has_value()) {
507       // This should never happen, further up-to-date checks are not possible if it does.
508       LOG(ERROR) << "Failed to generate cache provenance.";
509       metrics.SetTrigger(OdrMetrics::Trigger::kUnknown);
510       return cleanup_return(ExitCode::kCompilationRequired);
511     }
512 
513     // Record ART APEX version for metrics reporting.
514     metrics.SetArtApexVersion(current_info->getVersionCode());
515 
516     // Record ART APEX last update milliseconds (used in compilation log).
517     metrics.SetArtApexLastUpdateMillis(current_info->getLastUpdateMillis());
518 
519     if (apex_info->getIsFactory()) {
520       // Remove any artifacts on /data as they are not necessary and no compilation is necessary.
521       LOG(INFO) << "Factory APEX mounted.";
522       return cleanup_return(ExitCode::kOkay);
523     }
524 
525     if (!OS::FileExists(cache_info_filename_.c_str())) {
526       // If the cache info file does not exist, assume compilation is required because the
527       // file is missing and because the current ART APEX is not factory installed.
528       PLOG(ERROR) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
529       metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
530       return cleanup_return(ExitCode::kCompilationRequired);
531     }
532 
533     // Get and parse the ART APEX cache info file.
534     std::optional<art_apex::CacheInfo> cache_info = ReadCacheInfo();
535     if (!cache_info.has_value()) {
536       // This should never happen, further up-to-date checks are not possible if it does.
537       PLOG(ERROR) << "Failed to read cache-info file: " << QuotePath(cache_info_filename_);
538       metrics.SetTrigger(OdrMetrics::Trigger::kUnknown);
539       return cleanup_return(ExitCode::kCompilationRequired);
540     }
541 
542     // Check whether the current cache ART module info differs from the current ART module info.
543     // Always check APEX version.
544     const auto cached_info = cache_info->getFirstArtModuleInfo();
545 
546     if (cached_info->getVersionCode() != current_info->getVersionCode()) {
547       LOG(INFO) << "ART APEX version code mismatch ("
548                 << cached_info->getVersionCode()
549                 << " != " << current_info->getVersionCode() << ").";
550       metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
551       return cleanup_return(ExitCode::kCompilationRequired);
552     }
553 
554     if (cached_info->getVersionName() != current_info->getVersionName()) {
555       LOG(INFO) << "ART APEX version name mismatch ("
556                 << cached_info->getVersionName()
557                 << " != " << current_info->getVersionName() << ").";
558       metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
559       return cleanup_return(ExitCode::kCompilationRequired);
560     }
561 
562     // Check lastUpdateMillis for samegrade installs. If `cached_info` is missing lastUpdateMillis
563     // then it is not current with the schema used by this binary so treat it as a samegrade
564     // update. Otherwise check whether the lastUpdateMillis changed.
565     if (!cached_info->hasLastUpdateMillis() ||
566         cached_info->getLastUpdateMillis() != current_info->getLastUpdateMillis()) {
567       LOG(INFO) << "ART APEX last update time mismatch ("
568                 << cached_info->getLastUpdateMillis()
569                 << " != " << current_info->getLastUpdateMillis() << ").";
570       metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
571       return cleanup_return(ExitCode::kCompilationRequired);
572     }
573 
574     // Check boot class components.
575     //
576     // This checks the size and checksums of odrefresh compilable files on the DEX2OATBOOTCLASSPATH
577     // (the Odrefresh constructor determines which files are compilable). If the number of files
578     // there changes, or their size or checksums change then compilation will be triggered.
579     //
580     // The boot class components may change unexpectedly, for example an OTA could update
581     // framework.jar.
582     const std::vector<art_apex::Component> expected_bcp_components =
583         GenerateBootExtensionComponents();
584     if (expected_bcp_components.size() != 0 &&
585         (!cache_info->hasDex2oatBootClasspath() ||
586          !cache_info->getFirstDex2oatBootClasspath()->hasComponent())) {
587       LOG(INFO) << "Missing Dex2oatBootClasspath components.";
588       metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
589       return cleanup_return(ExitCode::kCompilationRequired);
590     }
591 
592     std::string error_msg;
593     const std::vector<art_apex::Component>& bcp_components =
594         cache_info->getFirstDex2oatBootClasspath()->getComponent();
595     if (!CheckComponents(expected_bcp_components, bcp_components, &error_msg)) {
596       LOG(INFO) << "Dex2OatClasspath components mismatch: " << error_msg;
597       metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
598       return cleanup_return(ExitCode::kCompilationRequired);
599     }
600 
601     // Check system server components.
602     //
603     // This checks the size and checksums of odrefresh compilable files on the
604     // SYSTEMSERVERCLASSPATH (the Odrefresh constructor determines which files are compilable). If
605     // the number of files there changes, or their size or checksums change then compilation will be
606     // triggered.
607     //
608     // The system_server components may change unexpectedly, for example an OTA could update
609     // services.jar.
610     auto cleanup_system_server_return = [this](ExitCode exit_code) {
611       return RemoveSystemServerArtifactsFromData() ? exit_code : ExitCode::kCleanupFailed;
612     };
613 
614     const std::vector<art_apex::Component> expected_system_server_components =
615         GenerateSystemServerComponents();
616     if (expected_system_server_components.size() != 0 &&
617         (!cache_info->hasSystemServerClasspath() ||
618          !cache_info->getFirstSystemServerClasspath()->hasComponent())) {
619       LOG(INFO) << "Missing SystemServerClasspath components.";
620       metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
621       return cleanup_system_server_return(ExitCode::kCompilationRequired);
622     }
623 
624     const std::vector<art_apex::Component>& system_server_components =
625         cache_info->getFirstSystemServerClasspath()->getComponent();
626     if (!CheckComponents(expected_system_server_components, system_server_components, &error_msg)) {
627       LOG(INFO) << "SystemServerClasspath components mismatch: " << error_msg;
628       metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
629       return cleanup_system_server_return(ExitCode::kCompilationRequired);
630     }
631 
632     // Cache info looks good, check all compilation artifacts exist.
633     auto cleanup_boot_extensions_return = [this](ExitCode exit_code, InstructionSet isa) {
634       return RemoveBootExtensionArtifactsFromData(isa) ? exit_code : ExitCode::kCleanupFailed;
635     };
636 
637     for (const InstructionSet isa : config_.GetBootExtensionIsas()) {
638       if (!BootExtensionArtifactsExistOnData(isa, &error_msg)) {
639         LOG(INFO) << "Incomplete boot extension artifacts. " << error_msg;
640         metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
641         return cleanup_boot_extensions_return(ExitCode::kCompilationRequired, isa);
642       }
643     }
644 
645     if (!SystemServerArtifactsExistOnData(&error_msg)) {
646       LOG(INFO) << "Incomplete system_server artifacts. " << error_msg;
647       // No clean-up is required here: we have boot extension artifacts. The method
648       // `SystemServerArtifactsExistOnData()` checks in compilation order so it is possible some of
649       // the artifacts are here. We likely ran out of space compiling the system_server artifacts.
650       // Any artifacts present are usable.
651       metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
652       return ExitCode::kCompilationRequired;
653     }
654 
655     return ExitCode::kOkay;
656   }
657 
AddDex2OatCommonOptions(std::vector<std::string> * args)658   static void AddDex2OatCommonOptions(/*inout*/ std::vector<std::string>* args) {
659     args->emplace_back("--android-root=out/empty");
660     args->emplace_back("--abort-on-hard-verifier-error");
661     args->emplace_back("--no-abort-on-soft-verifier-error");
662     args->emplace_back("--compilation-reason=boot");
663     args->emplace_back("--image-format=lz4");
664     args->emplace_back("--force-determinism");
665     args->emplace_back("--resolve-startup-const-strings=true");
666   }
667 
AddDex2OatConcurrencyArguments(std::vector<std::string> * args)668   static void AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>* args) {
669     static constexpr std::pair<const char*, const char*> kPropertyArgPairs[] = {
670         std::make_pair("dalvik.vm.boot-dex2oat-cpu-set", "--cpu-set="),
671         std::make_pair("dalvik.vm.boot-dex2oat-threads", "-j"),
672     };
673     for (auto property_arg_pair : kPropertyArgPairs) {
674       auto [property, arg] = property_arg_pair;
675       std::string value = android::base::GetProperty(property, {});
676       if (!value.empty()) {
677         args->push_back(arg + value);
678       }
679     }
680   }
681 
AddDex2OatDebugInfo(std::vector<std::string> * args)682   static void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>* args) {
683     args->emplace_back("--generate-mini-debug-info");
684     args->emplace_back("--strip");
685   }
686 
AddDex2OatInstructionSet(std::vector<std::string> * args,InstructionSet isa)687   static void AddDex2OatInstructionSet(/*inout*/ std::vector<std::string>* args,
688                                        InstructionSet isa) {
689     const char* isa_str = GetInstructionSetString(isa);
690     args->emplace_back(Concatenate({"--instruction-set=", isa_str}));
691   }
692 
693 
AddDex2OatProfileAndCompilerFilter(std::vector<std::string> * args,const std::string & profile_file)694   static void AddDex2OatProfileAndCompilerFilter(/*inout*/ std::vector<std::string>* args,
695                                                  const std::string& profile_file) {
696     if (OS::FileExists(profile_file.c_str(), /*check_file_type=*/true)) {
697       args->emplace_back(Concatenate({"--profile-file=", profile_file}));
698       args->emplace_back("--compiler-filter=speed-profile");
699     } else {
700       args->emplace_back("--compiler-filter=speed");
701     }
702   }
703 
VerifySystemServerArtifactsAreUpToDate(bool on_system) const704   WARN_UNUSED bool VerifySystemServerArtifactsAreUpToDate(bool on_system) const {
705     std::vector<std::string> classloader_context;
706     for (const std::string& jar_path : systemserver_compilable_jars_) {
707       std::vector<std::string> args;
708       args.emplace_back(config_.GetDexOptAnalyzer());
709       args.emplace_back("--dex-file=" + jar_path);
710 
711       const std::string image_location = GetSystemServerImagePath(on_system, jar_path);
712 
713       // odrefresh produces app-image files, but these are not guaranteed for those pre-installed
714       // on /system.
715       if (!on_system && !OS::FileExists(image_location.c_str(), true)) {
716         LOG(INFO) << "Missing image file: " << QuotePath(image_location);
717         return false;
718       }
719 
720       // Generate set of artifacts that are output by compilation.
721       OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
722       if (!on_system) {
723         CHECK_EQ(artifacts.OatPath(),
724                  GetApexDataOdexFilename(jar_path, config_.GetSystemServerIsa()));
725         CHECK_EQ(artifacts.ImagePath(),
726                  GetApexDataDalvikCacheFilename(jar_path, config_.GetSystemServerIsa(), "art"));
727         CHECK_EQ(artifacts.OatPath(),
728                  GetApexDataDalvikCacheFilename(jar_path, config_.GetSystemServerIsa(), "odex"));
729         CHECK_EQ(artifacts.VdexPath(),
730                  GetApexDataDalvikCacheFilename(jar_path, config_.GetSystemServerIsa(), "vdex"));
731       }
732 
733       // Associate inputs and outputs with dexoptanalyzer arguments.
734       std::pair<const std::string, const char*> location_args[] = {
735           std::make_pair(artifacts.OatPath(), "--oat-fd="),
736           std::make_pair(artifacts.VdexPath(), "--vdex-fd="),
737           std::make_pair(jar_path, "--zip-fd=")
738       };
739 
740       // Open file descriptors for dexoptanalyzer file inputs and add to the command-line.
741       std::vector<std::unique_ptr<File>> files;
742       for (const auto& location_arg : location_args) {
743         auto& [location, arg] = location_arg;
744         std::unique_ptr<File> file(OS::OpenFileForReading(location.c_str()));
745         if (file == nullptr) {
746           PLOG(ERROR) << "Failed to open \"" << location << "\"";
747           return false;
748         }
749         args.emplace_back(android::base::StringPrintf("%s%d", arg, file->Fd()));
750         files.emplace_back(file.release());
751       }
752 
753       const std::string basename(android::base::Basename(jar_path));
754       const std::string root = GetAndroidRoot();
755       const std::string profile_file = Concatenate({root, "/framework/", basename, ".prof"});
756       if (OS::FileExists(profile_file.c_str())) {
757         args.emplace_back("--compiler-filter=speed-profile");
758       } else {
759         args.emplace_back("--compiler-filter=speed");
760       }
761 
762       args.emplace_back(
763           Concatenate({"--image=", GetBootImage(), ":", GetBootImageExtensionImage(on_system)}));
764       args.emplace_back(
765           Concatenate({"--isa=", GetInstructionSetString(config_.GetSystemServerIsa())}));
766       args.emplace_back("--runtime-arg");
767       args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
768       args.emplace_back(Concatenate(
769           {"--class-loader-context=PCL[", android::base::Join(classloader_context, ':'), "]"}));
770 
771       classloader_context.emplace_back(jar_path);
772 
773       LOG(INFO) << "Checking " << jar_path << ": " << android::base::Join(args, ' ');
774       std::string error_msg;
775       bool timed_out = false;
776       const time_t timeout = GetSubprocessTimeout();
777       const int dexoptanalyzer_result = ExecAndReturnCode(args, timeout, &timed_out, &error_msg);
778       if (dexoptanalyzer_result == -1) {
779         LOG(ERROR) << "Unexpected exit from dexoptanalyzer: " << error_msg;
780         if (timed_out) {
781           // TODO(oth): record metric for timeout.
782         }
783         return false;
784       }
785       LOG(INFO) << "dexoptanalyzer returned " << dexoptanalyzer_result;
786 
787       bool unexpected_result = true;
788       switch (static_cast<dexoptanalyzer::ReturnCode>(dexoptanalyzer_result)) {
789         case art::dexoptanalyzer::ReturnCode::kNoDexOptNeeded:
790           unexpected_result = false;
791           break;
792 
793         // Recompile needed
794         case art::dexoptanalyzer::ReturnCode::kDex2OatFromScratch:
795         case art::dexoptanalyzer::ReturnCode::kDex2OatForBootImageOat:
796         case art::dexoptanalyzer::ReturnCode::kDex2OatForFilterOat:
797         case art::dexoptanalyzer::ReturnCode::kDex2OatForBootImageOdex:
798         case art::dexoptanalyzer::ReturnCode::kDex2OatForFilterOdex:
799           return false;
800 
801         // Unexpected issues (note no default-case here to catch missing enum values, but the
802         // return code from dexoptanalyzer may also be outside expected values, such as a
803         // process crash.
804         case art::dexoptanalyzer::ReturnCode::kFlattenClassLoaderContextSuccess:
805         case art::dexoptanalyzer::ReturnCode::kErrorInvalidArguments:
806         case art::dexoptanalyzer::ReturnCode::kErrorCannotCreateRuntime:
807         case art::dexoptanalyzer::ReturnCode::kErrorUnknownDexOptNeeded:
808           break;
809       }
810 
811       if (unexpected_result) {
812         LOG(ERROR) << "Unexpected result from dexoptanalyzer: " << dexoptanalyzer_result;
813         return false;
814       }
815     }
816     return true;
817   }
818 
RemoveSystemServerArtifactsFromData() const819   WARN_UNUSED bool RemoveSystemServerArtifactsFromData() const {
820     if (config_.GetDryRun()) {
821       LOG(INFO) << "Removal of system_server artifacts on /data skipped (dry-run).";
822       return true;
823     }
824 
825     bool success = true;
826     for (const std::string& jar_path : systemserver_compilable_jars_) {
827       const std::string image_location =
828           GetSystemServerImagePath(/*on_system=*/false, jar_path);
829       const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
830       LOG(INFO) << "Removing system_server artifacts on /data for " << QuotePath(jar_path);
831       success &= RemoveArtifacts(artifacts);
832     }
833     return success;
834   }
835 
836   // Verify the validity of system server artifacts on both /system and /data.
837   // This method has the side-effect of removing system server artifacts on /data, if there are
838   // valid artifacts on /system, or if the artifacts on /data are not valid.
839   // Returns true if valid artifacts are found.
VerifySystemServerArtifactsAreUpToDate() const840   WARN_UNUSED bool VerifySystemServerArtifactsAreUpToDate() const {
841     bool system_ok = VerifySystemServerArtifactsAreUpToDate(/*on_system=*/true);
842     LOG(INFO) << "system_server artifacts on /system are " << (system_ok ? "ok" : "stale");
843     bool data_ok = VerifySystemServerArtifactsAreUpToDate(/*on_system=*/false);
844     LOG(INFO) << "system_server artifacts on /data are " << (data_ok ? "ok" : "stale");
845     return system_ok || data_ok;
846   }
847 
848   // Check the validity of boot class path extension artifacts.
849   //
850   // Returns true if artifacts exist and are valid according to dexoptanalyzer.
VerifyBootExtensionArtifactsAreUpToDate(const InstructionSet isa,bool on_system) const851   WARN_UNUSED bool VerifyBootExtensionArtifactsAreUpToDate(const InstructionSet isa,
852                                                            bool on_system) const {
853     const std::string dex_file = boot_extension_compilable_jars_.front();
854     const std::string image_location = GetBootImageExtensionImage(on_system);
855 
856     std::vector<std::string> args;
857     args.emplace_back(config_.GetDexOptAnalyzer());
858     args.emplace_back("--validate-bcp");
859     args.emplace_back(Concatenate({"--image=", GetBootImage(), ":", image_location}));
860     args.emplace_back(Concatenate({"--isa=", GetInstructionSetString(isa)}));
861     args.emplace_back("--runtime-arg");
862     args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
863 
864     LOG(INFO) << "Checking " << dex_file << ": " << android::base::Join(args, ' ');
865 
866     std::string error_msg;
867     bool timed_out = false;
868     const time_t timeout = GetSubprocessTimeout();
869     const int dexoptanalyzer_result = ExecAndReturnCode(args, timeout, &timed_out, &error_msg);
870     if (dexoptanalyzer_result == -1) {
871       LOG(ERROR) << "Unexpected exit from dexoptanalyzer: " << error_msg;
872       if (timed_out) {
873         // TODO(oth): record metric for timeout.
874       }
875       return false;
876     }
877     auto rc = static_cast<dexoptanalyzer::ReturnCode>(dexoptanalyzer_result);
878     if (rc == dexoptanalyzer::ReturnCode::kNoDexOptNeeded) {
879       return true;
880     }
881     return false;
882   }
883 
884   // Remove boot extension artifacts from /data.
RemoveBootExtensionArtifactsFromData(InstructionSet isa) const885   WARN_UNUSED bool RemoveBootExtensionArtifactsFromData(InstructionSet isa) const {
886     if (config_.GetDryRun()) {
887       LOG(INFO) << "Removal of bcp extension artifacts on /data skipped (dry-run).";
888       return true;
889     }
890 
891     bool success = true;
892     if (isa == config_.GetSystemServerIsa()) {
893       // system_server artifacts are invalid without boot extension artifacts.
894       success &= RemoveSystemServerArtifactsFromData();
895     }
896 
897     const std::string apexdata_image_location = GetBootImageExtensionImagePath(isa);
898     LOG(INFO) << "Removing boot class path artifacts on /data for "
899               << QuotePath(apexdata_image_location);
900     success &= RemoveArtifacts(OdrArtifacts::ForBootImageExtension(apexdata_image_location));
901     return success;
902   }
903 
904   // Verify whether boot extension artifacts for `isa` are valid on system partition or in apexdata.
905   // This method has the side-effect of removing boot classpath extension artifacts on /data,
906   // if there are valid artifacts on /system, or if the artifacts on /data are not valid.
907   // Returns true if valid boot externsion artifacts are valid.
VerifyBootExtensionArtifactsAreUpToDate(InstructionSet isa) const908   WARN_UNUSED bool VerifyBootExtensionArtifactsAreUpToDate(InstructionSet isa) const {
909     bool system_ok = VerifyBootExtensionArtifactsAreUpToDate(isa, /*on_system=*/true);
910     LOG(INFO) << "Boot extension artifacts on /system are " << (system_ok ? "ok" : "stale");
911     bool data_ok = VerifyBootExtensionArtifactsAreUpToDate(isa, /*on_system=*/false);
912     LOG(INFO) << "Boot extension artifacts on /data are " << (data_ok ? "ok" : "stale");
913     return system_ok || data_ok;
914   }
915 
916   // Verify all artifacts are up-to-date.
917   //
918   // This method checks artifacts can be loaded by the runtime.
919   //
920   // Returns ExitCode::kOkay if artifacts are up-to-date, ExitCode::kCompilationRequired otherwise.
921   //
922   // NB This is the main function used by the --check command-line option. When invoked with
923   // --compile, we only recompile the out-of-date artifacts, not all (see `Odrefresh::Compile`).
VerifyArtifactsAreUpToDate()924   WARN_UNUSED ExitCode VerifyArtifactsAreUpToDate() {
925     ExitCode exit_code = ExitCode::kOkay;
926     for (const InstructionSet isa : config_.GetBootExtensionIsas()) {
927       if (!VerifyBootExtensionArtifactsAreUpToDate(isa)) {
928         if (!RemoveBootExtensionArtifactsFromData(isa)) {
929           return ExitCode::kCleanupFailed;
930         }
931         exit_code = ExitCode::kCompilationRequired;
932       }
933     }
934     if (!VerifySystemServerArtifactsAreUpToDate()) {
935       if (!RemoveSystemServerArtifactsFromData()) {
936         return ExitCode::kCleanupFailed;
937       }
938       exit_code = ExitCode::kCompilationRequired;
939     }
940     return exit_code;
941   }
942 
CleanApexdataDirectory() const943   WARN_UNUSED bool CleanApexdataDirectory() const {
944     const std::string& apex_data_path = GetArtApexData();
945     if (config_.GetDryRun()) {
946       LOG(INFO) << "Files under `" << QuotePath(apex_data_path) << " would be removed (dry-run).";
947       return true;
948     }
949     return CleanDirectory(apex_data_path);
950   }
951 
RemoveArtifacts(const OdrArtifacts & artifacts) const952   WARN_UNUSED bool RemoveArtifacts(const OdrArtifacts& artifacts) const {
953     bool success = true;
954     for (const auto& location :
955          {artifacts.ImagePath(), artifacts.OatPath(), artifacts.VdexPath()}) {
956       if (config_.GetDryRun()) {
957         LOG(INFO) << "Removing " << QuotePath(location) << " (dry-run).";
958         continue;
959       }
960 
961       if (OS::FileExists(location.c_str()) && unlink(location.c_str()) != 0) {
962         PLOG(ERROR) << "Failed to remove: " << QuotePath(location);
963         success = false;
964       }
965     }
966     return success;
967   }
968 
GetBootImage()969   static std::string GetBootImage() {
970     // Typically "/apex/com.android.art/javalib/boot.art".
971     return GetArtRoot() + "/javalib/boot.art";
972   }
973 
GetBootImageExtensionImage(bool on_system) const974   std::string GetBootImageExtensionImage(bool on_system) const {
975     CHECK(!boot_extension_compilable_jars_.empty());
976     const std::string leading_jar = boot_extension_compilable_jars_[0];
977     if (on_system) {
978       const std::string jar_name = android::base::Basename(leading_jar);
979       const std::string image_name = ReplaceFileExtension(jar_name, "art");
980       // Typically "/system/framework/boot-framework.art".
981       return Concatenate({GetAndroidRoot(), "/framework/boot-", image_name});
982     } else {
983       // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework.art".
984       return GetApexDataBootImage(leading_jar);
985     }
986   }
987 
GetBootImageExtensionImagePath(const InstructionSet isa) const988   std::string GetBootImageExtensionImagePath(const InstructionSet isa) const {
989     // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot-framework.art".
990     return GetSystemImageFilename(GetBootImageExtensionImage(/*on_system=*/false).c_str(), isa);
991   }
992 
GetSystemServerImagePath(bool on_system,const std::string & jar_path) const993   std::string GetSystemServerImagePath(bool on_system, const std::string& jar_path) const {
994     if (on_system) {
995       const std::string jar_name = android::base::Basename(jar_path);
996       const std::string image_name = ReplaceFileExtension(jar_name, "art");
997       const char* isa_str = GetInstructionSetString(config_.GetSystemServerIsa());
998       // Typically "/system/framework/oat/<isa>/services.art".
999       return Concatenate({GetAndroidRoot(), "/framework/oat/", isa_str, "/", image_name});
1000     } else {
1001       // Typically
1002       // "/data/misc/apexdata/.../dalvik-cache/<isa>/system@framework@services.jar@classes.art".
1003       const std::string image = GetApexDataImage(jar_path.c_str());
1004       return GetSystemImageFilename(image.c_str(), config_.GetSystemServerIsa());
1005     }
1006   }
1007 
GetStagingLocation(const std::string & staging_dir,const std::string & path) const1008   std::string GetStagingLocation(const std::string& staging_dir, const std::string& path) const {
1009     return Concatenate({staging_dir, "/", android::base::Basename(path)});
1010   }
1011 
CompileBootExtensionArtifacts(const InstructionSet isa,const std::string & staging_dir,OdrMetrics & metrics,uint32_t * dex2oat_invocation_count,std::string * error_msg) const1012   WARN_UNUSED bool CompileBootExtensionArtifacts(const InstructionSet isa,
1013                                                  const std::string& staging_dir,
1014                                                  OdrMetrics& metrics,
1015                                                  uint32_t* dex2oat_invocation_count,
1016                                                  std::string* error_msg) const {
1017     ScopedOdrCompilationTimer compilation_timer(metrics);
1018     std::vector<std::string> args;
1019     args.push_back(config_.GetDex2Oat());
1020 
1021     AddDex2OatCommonOptions(&args);
1022     AddDex2OatConcurrencyArguments(&args);
1023     AddDex2OatDebugInfo(&args);
1024     AddDex2OatInstructionSet(&args, isa);
1025     const std::string boot_profile_file(GetAndroidRoot() + "/etc/boot-image.prof");
1026     AddDex2OatProfileAndCompilerFilter(&args, boot_profile_file);
1027 
1028     // Compile as a single image for fewer files and slightly less memory overhead.
1029     args.emplace_back("--single-image");
1030 
1031     // Set boot-image and expectation of compiling boot classpath extensions.
1032     args.emplace_back("--boot-image=" + GetBootImage());
1033 
1034     const std::string dirty_image_objects_file(GetAndroidRoot() + "/etc/dirty-image-objects");
1035     if (OS::FileExists(dirty_image_objects_file.c_str())) {
1036       args.emplace_back(Concatenate({"--dirty-image-objects=", dirty_image_objects_file}));
1037     } else {
1038       LOG(WARNING) << "Missing dirty objects file : " << QuotePath(dirty_image_objects_file);
1039     }
1040 
1041     // Add boot extensions to compile.
1042     for (const std::string& component : boot_extension_compilable_jars_) {
1043       args.emplace_back("--dex-file=" + component);
1044     }
1045 
1046     args.emplace_back("--runtime-arg");
1047     args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
1048 
1049     const std::string image_location = GetBootImageExtensionImagePath(isa);
1050     const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(image_location);
1051     CHECK_EQ(GetApexDataOatFilename(boot_extension_compilable_jars_.front().c_str(), isa),
1052              artifacts.OatPath());
1053 
1054     args.emplace_back("--oat-location=" + artifacts.OatPath());
1055     const std::pair<const std::string, const char*> location_kind_pairs[] = {
1056         std::make_pair(artifacts.ImagePath(), "image"),
1057         std::make_pair(artifacts.OatPath(), "oat"),
1058         std::make_pair(artifacts.VdexPath(), "output-vdex")
1059     };
1060 
1061     std::vector<std::unique_ptr<File>> staging_files;
1062     for (const auto& location_kind_pair : location_kind_pairs) {
1063       auto& [location, kind] = location_kind_pair;
1064       const std::string staging_location = GetStagingLocation(staging_dir, location);
1065       std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
1066       if (staging_file == nullptr) {
1067         PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
1068         metrics.SetStatus(OdrMetrics::Status::kIoError);
1069         EraseFiles(staging_files);
1070         return false;
1071       }
1072 
1073       if (fchmod(staging_file->Fd(), S_IRUSR | S_IWUSR) != 0) {
1074         PLOG(ERROR) << "Could not set file mode on " << QuotePath(staging_location);
1075         metrics.SetStatus(OdrMetrics::Status::kIoError);
1076         EraseFiles(staging_files);
1077         return false;
1078       }
1079 
1080       args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
1081       staging_files.emplace_back(std::move(staging_file));
1082     }
1083 
1084     const std::string install_location = android::base::Dirname(image_location);
1085     if (!EnsureDirectoryExists(install_location)) {
1086       metrics.SetStatus(OdrMetrics::Status::kIoError);
1087       return false;
1088     }
1089 
1090     const time_t timeout = GetSubprocessTimeout();
1091     const std::string cmd_line = android::base::Join(args, ' ');
1092     LOG(INFO) << "Compiling boot extensions (" << isa << "): " << cmd_line
1093               << " [timeout " << timeout << "s]";
1094     if (config_.GetDryRun()) {
1095       LOG(INFO) << "Compilation skipped (dry-run).";
1096       return true;
1097     }
1098 
1099     bool timed_out = false;
1100     int dex2oat_exit_code = ExecAndReturnCode(args, timeout, &timed_out, error_msg);
1101     if (dex2oat_exit_code != 0) {
1102       if (timed_out) {
1103         metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
1104       } else {
1105         metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
1106       }
1107       EraseFiles(staging_files);
1108       return false;
1109     }
1110 
1111     if (!MoveOrEraseFiles(staging_files, install_location)) {
1112       metrics.SetStatus(OdrMetrics::Status::kInstallFailed);
1113       return false;
1114     }
1115 
1116     *dex2oat_invocation_count = *dex2oat_invocation_count + 1;
1117     ReportNextBootAnimationProgress(*dex2oat_invocation_count);
1118 
1119     return true;
1120   }
1121 
CompileSystemServerArtifacts(const std::string & staging_dir,OdrMetrics & metrics,uint32_t * dex2oat_invocation_count,std::string * error_msg) const1122   WARN_UNUSED bool CompileSystemServerArtifacts(const std::string& staging_dir,
1123                                                 OdrMetrics& metrics,
1124                                                 uint32_t* dex2oat_invocation_count,
1125                                                 std::string* error_msg) const {
1126     ScopedOdrCompilationTimer compilation_timer(metrics);
1127     std::vector<std::string> classloader_context;
1128 
1129     const std::string dex2oat = config_.GetDex2Oat();
1130     const InstructionSet isa = config_.GetSystemServerIsa();
1131     for (const std::string& jar : systemserver_compilable_jars_) {
1132       std::vector<std::string> args;
1133       args.emplace_back(dex2oat);
1134       args.emplace_back("--dex-file=" + jar);
1135 
1136       AddDex2OatCommonOptions(&args);
1137       AddDex2OatConcurrencyArguments(&args);
1138       AddDex2OatDebugInfo(&args);
1139       AddDex2OatInstructionSet(&args, isa);
1140       const std::string jar_name(android::base::Basename(jar));
1141       const std::string profile = Concatenate({GetAndroidRoot(), "/framework/", jar_name, ".prof"});
1142       std::string compiler_filter =
1143           android::base::GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
1144       if (compiler_filter == "speed-profile") {
1145         AddDex2OatProfileAndCompilerFilter(&args, profile);
1146       } else {
1147         args.emplace_back("--compiler-filter=" + compiler_filter);
1148       }
1149 
1150       const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar);
1151       const std::string install_location = android::base::Dirname(image_location);
1152       if (classloader_context.empty()) {
1153         // All images are in the same directory, we only need to check on the first iteration.
1154         if (!EnsureDirectoryExists(install_location)) {
1155           metrics.SetStatus(OdrMetrics::Status::kIoError);
1156           return false;
1157         }
1158       }
1159 
1160       OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
1161       CHECK_EQ(artifacts.OatPath(), GetApexDataOdexFilename(jar.c_str(), isa));
1162 
1163       const std::pair<const std::string, const char*> location_kind_pairs[] = {
1164           std::make_pair(artifacts.ImagePath(), "app-image"),
1165           std::make_pair(artifacts.OatPath(), "oat"),
1166           std::make_pair(artifacts.VdexPath(), "output-vdex")
1167       };
1168 
1169       std::vector<std::unique_ptr<File>> staging_files;
1170       for (const auto& location_kind_pair : location_kind_pairs) {
1171         auto& [location, kind] = location_kind_pair;
1172         const std::string staging_location = GetStagingLocation(staging_dir, location);
1173         std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
1174         if (staging_file == nullptr) {
1175           PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
1176           metrics.SetStatus(OdrMetrics::Status::kIoError);
1177           EraseFiles(staging_files);
1178           return false;
1179         }
1180         args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
1181         staging_files.emplace_back(std::move(staging_file));
1182       }
1183       args.emplace_back("--oat-location=" + artifacts.OatPath());
1184 
1185       if (!config_.GetUpdatableBcpPackagesFile().empty()) {
1186         const std::string& bcp_packages = config_.GetUpdatableBcpPackagesFile();
1187         if (!OS::FileExists(bcp_packages.c_str())) {
1188           *error_msg = "Cannot compile system_server JARs: missing " + QuotePath(bcp_packages);
1189           metrics.SetStatus(OdrMetrics::Status::kIoError);
1190           EraseFiles(staging_files);
1191           return false;
1192         }
1193         args.emplace_back("--updatable-bcp-packages-file=" + bcp_packages);
1194       }
1195 
1196       args.emplace_back("--runtime-arg");
1197       args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
1198       const std::string context_path = android::base::Join(classloader_context, ':');
1199       args.emplace_back(Concatenate({"--class-loader-context=PCL[", context_path, "]"}));
1200       const std::string extension_image = GetBootImageExtensionImage(/*on_system=*/false);
1201       args.emplace_back(Concatenate({"--boot-image=", GetBootImage(), ":", extension_image}));
1202 
1203       const time_t timeout = GetSubprocessTimeout();
1204       const std::string cmd_line = android::base::Join(args, ' ');
1205       LOG(INFO) << "Compiling " << jar << ": " << cmd_line << " [timeout " << timeout << "s]";
1206       if (config_.GetDryRun()) {
1207         LOG(INFO) << "Compilation skipped (dry-run).";
1208         return true;
1209       }
1210 
1211       bool timed_out = false;
1212       int dex2oat_exit_code = ExecAndReturnCode(args, timeout, &timed_out, error_msg);
1213       if (dex2oat_exit_code != 0) {
1214         if (timed_out) {
1215           metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
1216         } else {
1217           metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
1218         }
1219         EraseFiles(staging_files);
1220         return false;
1221       }
1222 
1223       if (!MoveOrEraseFiles(staging_files, install_location)) {
1224         metrics.SetStatus(OdrMetrics::Status::kInstallFailed);
1225         return false;
1226       }
1227 
1228       *dex2oat_invocation_count = *dex2oat_invocation_count + 1;
1229       ReportNextBootAnimationProgress(*dex2oat_invocation_count);
1230       classloader_context.emplace_back(jar);
1231     }
1232 
1233     return true;
1234   }
1235 
ReportNextBootAnimationProgress(uint32_t current_compilation) const1236   void ReportNextBootAnimationProgress(uint32_t current_compilation) const {
1237     uint32_t number_of_compilations =
1238         config_.GetBootExtensionIsas().size() + systemserver_compilable_jars_.size();
1239     // We arbitrarily show progress until 90%, expecting that our compilations
1240     // take a large chunk of boot time.
1241     uint32_t value = (90 * current_compilation) / number_of_compilations;
1242     android::base::SetProperty("service.bootanim.progress", std::to_string(value));
1243   }
1244 
CheckCompilationSpace() const1245   WARN_UNUSED bool CheckCompilationSpace() const {
1246     // Check the available storage space against an arbitrary threshold because dex2oat does not
1247     // report when it runs out of storage space and we do not want to completely fill
1248     // the users data partition.
1249     //
1250     // We do not have a good way of pre-computing the required space for a compilation step, but
1251     // typically observe 16MB as the largest size of an AOT artifact. Since there are three
1252     // AOT artifacts per compilation step - an image file, executable file, and a verification
1253     // data file - the threshold is three times 16MB.
1254     static constexpr uint64_t kMinimumSpaceForCompilation = 3 * 16 * 1024 * 1024;
1255 
1256     uint64_t bytes_available;
1257     const std::string& art_apex_data_path = GetArtApexData();
1258     if (!GetFreeSpace(art_apex_data_path, &bytes_available)) {
1259       return false;
1260     }
1261 
1262     if (bytes_available < kMinimumSpaceForCompilation) {
1263       LOG(WARNING) << "Low space for " << QuotePath(art_apex_data_path) << " (" << bytes_available
1264                    << " bytes)";
1265       return false;
1266     }
1267 
1268     return true;
1269   }
1270 
Compile(OdrMetrics & metrics,bool force_compile) const1271   WARN_UNUSED ExitCode Compile(OdrMetrics& metrics, bool force_compile) const {
1272     const char* staging_dir = nullptr;
1273     metrics.SetStage(OdrMetrics::Stage::kPreparation);
1274     // Clean-up existing files.
1275     if (force_compile && !CleanApexdataDirectory()) {
1276       metrics.SetStatus(OdrMetrics::Status::kIoError);
1277       return ExitCode::kCleanupFailed;
1278     }
1279 
1280     // Create staging area and assign label for generating compilation artifacts.
1281     if (PaletteCreateOdrefreshStagingDirectory(&staging_dir) != PALETTE_STATUS_OK) {
1282       metrics.SetStatus(OdrMetrics::Status::kStagingFailed);
1283       return ExitCode::kCleanupFailed;
1284     }
1285 
1286     // Emit cache info before compiling. This can be used to throttle compilation attempts later.
1287     WriteCacheInfo();
1288 
1289     std::string error_msg;
1290 
1291     uint32_t dex2oat_invocation_count = 0;
1292     ReportNextBootAnimationProgress(dex2oat_invocation_count);
1293 
1294     const auto& bcp_instruction_sets = config_.GetBootExtensionIsas();
1295     DCHECK(!bcp_instruction_sets.empty() && bcp_instruction_sets.size() <= 2);
1296     for (const InstructionSet isa : bcp_instruction_sets) {
1297       auto stage = (isa == bcp_instruction_sets.front()) ?
1298                        OdrMetrics::Stage::kPrimaryBootClasspath :
1299                        OdrMetrics::Stage::kSecondaryBootClasspath;
1300       metrics.SetStage(stage);
1301       if (force_compile || !BootExtensionArtifactsExistOnData(isa, &error_msg)) {
1302         // Remove artifacts we are about to generate. Ordinarily these are removed in the checking
1303         // step, but this is not always run (e.g. during manual testing).
1304         if (!RemoveBootExtensionArtifactsFromData(isa)) {
1305             return ExitCode::kCleanupFailed;
1306         }
1307 
1308         if (!CheckCompilationSpace()) {
1309           metrics.SetStatus(OdrMetrics::Status::kNoSpace);
1310           // Return kOkay so odsign will keep and sign whatever we have been able to compile.
1311           return ExitCode::kOkay;
1312         }
1313 
1314         if (!CompileBootExtensionArtifacts(
1315                 isa, staging_dir, metrics, &dex2oat_invocation_count, &error_msg)) {
1316           LOG(ERROR) << "Compilation of BCP failed: " << error_msg;
1317           if (!config_.GetDryRun() && !CleanDirectory(staging_dir)) {
1318             return ExitCode::kCleanupFailed;
1319           }
1320           return ExitCode::kCompilationFailed;
1321         }
1322       }
1323     }
1324 
1325     if (force_compile || !SystemServerArtifactsExistOnData(&error_msg)) {
1326       metrics.SetStage(OdrMetrics::Stage::kSystemServerClasspath);
1327 
1328       if (!CheckCompilationSpace()) {
1329         metrics.SetStatus(OdrMetrics::Status::kNoSpace);
1330         // Return kOkay so odsign will keep and sign whatever we have been able to compile.
1331         return ExitCode::kOkay;
1332       }
1333 
1334       if (!CompileSystemServerArtifacts(
1335               staging_dir, metrics, &dex2oat_invocation_count, &error_msg)) {
1336         LOG(ERROR) << "Compilation of system_server failed: " << error_msg;
1337         if (!config_.GetDryRun() && !CleanDirectory(staging_dir)) {
1338           return ExitCode::kCleanupFailed;
1339         }
1340         return ExitCode::kCompilationFailed;
1341       }
1342     }
1343 
1344     metrics.SetStage(OdrMetrics::Stage::kComplete);
1345     return ExitCode::kCompilationSuccess;
1346   }
1347 
ArgumentMatches(std::string_view argument,std::string_view prefix,std::string * value)1348   static bool ArgumentMatches(std::string_view argument,
1349                               std::string_view prefix,
1350                               std::string* value) {
1351     if (StartsWith(argument, prefix)) {
1352       *value = std::string(argument.substr(prefix.size()));
1353       return true;
1354     }
1355     return false;
1356   }
1357 
ArgumentEquals(std::string_view argument,std::string_view expected)1358   static bool ArgumentEquals(std::string_view argument, std::string_view expected) {
1359     return argument == expected;
1360   }
1361 
InitializeCommonConfig(std::string_view argument,OdrConfig * config)1362   static bool InitializeCommonConfig(std::string_view argument, OdrConfig* config) {
1363     static constexpr std::string_view kDryRunArgument{"--dry-run"};
1364     if (ArgumentEquals(argument, kDryRunArgument)) {
1365       config->SetDryRun();
1366       return true;
1367     }
1368     return false;
1369   }
1370 
InitializeHostConfig(int argc,const char ** argv,OdrConfig * config)1371   static int InitializeHostConfig(int argc, const char** argv, OdrConfig* config) {
1372     __android_log_set_logger(__android_log_stderr_logger);
1373 
1374     std::string current_binary;
1375     if (argv[0][0] == '/') {
1376       current_binary = argv[0];
1377     } else {
1378       std::vector<char> buf(PATH_MAX);
1379       if (getcwd(buf.data(), buf.size()) == nullptr) {
1380         PLOG(FATAL) << "Failed getwd()";
1381       }
1382       current_binary = Concatenate({buf.data(), "/", argv[0]});
1383     }
1384     config->SetArtBinDir(android::base::Dirname(current_binary));
1385 
1386     int n = 1;
1387     for (; n < argc - 1; ++n) {
1388       const char* arg = argv[n];
1389       std::string value;
1390       if (ArgumentMatches(arg, "--android-root=", &value)) {
1391         setenv("ANDROID_ROOT", value.c_str(), 1);
1392       } else if (ArgumentMatches(arg, "--android-art-root=", &value)) {
1393         setenv("ANDROID_ART_ROOT", value.c_str(), 1);
1394       } else if (ArgumentMatches(arg, "--apex-info-list=", &value)) {
1395         config->SetApexInfoListFile(value);
1396       } else if (ArgumentMatches(arg, "--art-apex-data=", &value)) {
1397         setenv("ART_APEX_DATA", value.c_str(), 1);
1398       } else if (ArgumentMatches(arg, "--dex2oat-bootclasspath=", &value)) {
1399         config->SetDex2oatBootclasspath(value);
1400       } else if (ArgumentMatches(arg, "--isa=", &value)) {
1401         config->SetIsa(GetInstructionSetFromString(value.c_str()));
1402       } else if (ArgumentMatches(arg, "--system-server-classpath=", &value)) {
1403         config->SetSystemServerClasspath(arg);
1404       } else if (ArgumentMatches(arg, "--updatable-bcp-packages-file=", &value)) {
1405         config->SetUpdatableBcpPackagesFile(value);
1406       } else if (ArgumentMatches(arg, "--zygote-arch=", &value)) {
1407         ZygoteKind zygote_kind;
1408         if (!ParseZygoteKind(value.c_str(), &zygote_kind)) {
1409           ArgumentError("Unrecognized zygote kind: '%s'", value.c_str());
1410         }
1411         config->SetZygoteKind(zygote_kind);
1412       } else if (!InitializeCommonConfig(arg, config)) {
1413         UsageError("Unrecognized argument: '%s'", arg);
1414       }
1415     }
1416     return n;
1417   }
1418 
InitializeTargetConfig(int argc,const char ** argv,OdrConfig * config)1419   static int InitializeTargetConfig(int argc, const char** argv, OdrConfig* config) {
1420     config->SetApexInfoListFile("/apex/apex-info-list.xml");
1421     config->SetArtBinDir(GetArtBinDir());
1422     config->SetDex2oatBootclasspath(GetEnvironmentVariableOrDie("DEX2OATBOOTCLASSPATH"));
1423     config->SetSystemServerClasspath(GetEnvironmentVariableOrDie("SYSTEMSERVERCLASSPATH"));
1424     config->SetIsa(kRuntimeISA);
1425 
1426     const std::string zygote = android::base::GetProperty("ro.zygote", {});
1427     ZygoteKind zygote_kind;
1428     if (!ParseZygoteKind(zygote.c_str(), &zygote_kind)) {
1429       LOG(FATAL) << "Unknown zygote: " << QuotePath(zygote);
1430     }
1431     config->SetZygoteKind(zygote_kind);
1432 
1433     const std::string updatable_packages =
1434         android::base::GetProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", {});
1435     config->SetUpdatableBcpPackagesFile(updatable_packages);
1436 
1437     int n = 1;
1438     for (; n < argc - 1; ++n) {
1439       if (!InitializeCommonConfig(argv[n], config)) {
1440         UsageError("Unrecognized argument: '%s'", argv[n]);
1441       }
1442     }
1443     return n;
1444   }
1445 
InitializeConfig(int argc,const char ** argv,OdrConfig * config)1446   static int InitializeConfig(int argc, const char** argv, OdrConfig* config) {
1447     if (kIsTargetBuild) {
1448       return InitializeTargetConfig(argc, argv, config);
1449     } else {
1450       return InitializeHostConfig(argc, argv, config);
1451     }
1452   }
1453 
main(int argc,const char ** argv)1454   static int main(int argc, const char** argv) {
1455     OdrConfig config(argv[0]);
1456     int n = InitializeConfig(argc, argv, &config);
1457     argv += n;
1458     argc -= n;
1459     if (argc != 1) {
1460       UsageError("Expected 1 argument, but have %d.", argc);
1461     }
1462 
1463     OdrMetrics metrics(kOdrefreshArtifactDirectory);
1464     OnDeviceRefresh odr(config);
1465     for (int i = 0; i < argc; ++i) {
1466       std::string_view action(argv[i]);
1467       if (action == "--check") {
1468         // Fast determination of whether artifacts are up to date.
1469         return odr.CheckArtifactsAreUpToDate(metrics);
1470       } else if (action == "--compile") {
1471         const ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics);
1472         if (exit_code != ExitCode::kCompilationRequired) {
1473           return exit_code;
1474         }
1475         OdrCompilationLog compilation_log;
1476         if (!compilation_log.ShouldAttemptCompile(metrics.GetArtApexVersion(),
1477                                                   metrics.GetArtApexLastUpdateMillis(),
1478                                                   metrics.GetTrigger())) {
1479           return ExitCode::kOkay;
1480         }
1481         ExitCode compile_result = odr.Compile(metrics, /*force_compile=*/false);
1482         compilation_log.Log(metrics.GetArtApexVersion(),
1483                             metrics.GetArtApexLastUpdateMillis(),
1484                             metrics.GetTrigger(),
1485                             compile_result);
1486         return compile_result;
1487       } else if (action == "--force-compile") {
1488         return odr.Compile(metrics, /*force_compile=*/true);
1489       } else if (action == "--verify") {
1490         // Slow determination of whether artifacts are up to date. These are too slow for checking
1491         // during boot (b/181689036).
1492         return odr.VerifyArtifactsAreUpToDate();
1493       } else if (action == "--help") {
1494         UsageHelp(argv[0]);
1495       } else {
1496         UsageError("Unknown argument: ", argv[i]);
1497       }
1498     }
1499     return ExitCode::kOkay;
1500   }
1501 
1502   DISALLOW_COPY_AND_ASSIGN(OnDeviceRefresh);
1503 };
1504 
1505 }  // namespace odrefresh
1506 }  // namespace art
1507 
main(int argc,const char ** argv)1508 int main(int argc, const char** argv) {
1509   // odrefresh is launched by `init` which sets the umask of forked processed to
1510   // 077 (S_IRWXG | S_IRWXO). This blocks the ability to make files and directories readable
1511   // by others and prevents system_server from loading generated artifacts.
1512   umask(S_IWGRP | S_IWOTH);
1513   return art::odrefresh::OnDeviceRefresh::main(argc, argv);
1514 }
1515