1 /*
2 * Copyright (C) 2021 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 "artd.h"
18
19 #include <fcntl.h>
20 #include <stdlib.h>
21 #include <sys/stat.h>
22 #include <sys/statfs.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <climits>
27 #include <csignal>
28 #include <cstdint>
29 #include <cstring>
30 #include <filesystem>
31 #include <functional>
32 #include <map>
33 #include <memory>
34 #include <mutex>
35 #include <optional>
36 #include <ostream>
37 #include <string>
38 #include <string_view>
39 #include <system_error>
40 #include <type_traits>
41 #include <unordered_set>
42 #include <utility>
43 #include <vector>
44
45 #include "aidl/com/android/server/art/BnArtd.h"
46 #include "aidl/com/android/server/art/DexoptTrigger.h"
47 #include "aidl/com/android/server/art/IArtdCancellationSignal.h"
48 #include "android-base/errors.h"
49 #include "android-base/file.h"
50 #include "android-base/logging.h"
51 #include "android-base/result.h"
52 #include "android-base/scopeguard.h"
53 #include "android-base/strings.h"
54 #include "android/binder_auto_utils.h"
55 #include "android/binder_interface_utils.h"
56 #include "android/binder_manager.h"
57 #include "android/binder_process.h"
58 #include "base/compiler_filter.h"
59 #include "base/file_utils.h"
60 #include "base/globals.h"
61 #include "base/logging.h"
62 #include "base/os.h"
63 #include "cmdline_types.h"
64 #include "exec_utils.h"
65 #include "file_utils.h"
66 #include "fmt/format.h"
67 #include "oat_file_assistant.h"
68 #include "oat_file_assistant_context.h"
69 #include "path_utils.h"
70 #include "profman/profman_result.h"
71 #include "selinux/android.h"
72 #include "tools/cmdline_builder.h"
73 #include "tools/tools.h"
74
75 #ifdef __BIONIC__
76 #include <linux/incrementalfs.h>
77 #endif
78
79 namespace art {
80 namespace artd {
81
82 namespace {
83
84 using ::aidl::com::android::server::art::ArtdDexoptResult;
85 using ::aidl::com::android::server::art::ArtifactsPath;
86 using ::aidl::com::android::server::art::DexMetadataPath;
87 using ::aidl::com::android::server::art::DexoptOptions;
88 using ::aidl::com::android::server::art::DexoptTrigger;
89 using ::aidl::com::android::server::art::FileVisibility;
90 using ::aidl::com::android::server::art::FsPermission;
91 using ::aidl::com::android::server::art::GetDexoptNeededResult;
92 using ::aidl::com::android::server::art::GetDexoptStatusResult;
93 using ::aidl::com::android::server::art::IArtdCancellationSignal;
94 using ::aidl::com::android::server::art::MergeProfileOptions;
95 using ::aidl::com::android::server::art::OutputArtifacts;
96 using ::aidl::com::android::server::art::OutputProfile;
97 using ::aidl::com::android::server::art::PriorityClass;
98 using ::aidl::com::android::server::art::ProfilePath;
99 using ::aidl::com::android::server::art::VdexPath;
100 using ::android::base::Dirname;
101 using ::android::base::Error;
102 using ::android::base::Join;
103 using ::android::base::make_scope_guard;
104 using ::android::base::ReadFileToString;
105 using ::android::base::Result;
106 using ::android::base::Split;
107 using ::android::base::StringReplace;
108 using ::android::base::WriteStringToFd;
109 using ::art::tools::CmdlineBuilder;
110 using ::ndk::ScopedAStatus;
111
112 using ::fmt::literals::operator""_format; // NOLINT
113
114 using ArtifactsLocation = GetDexoptNeededResult::ArtifactsLocation;
115 using TmpProfilePath = ProfilePath::TmpProfilePath;
116
117 constexpr const char* kServiceName = "artd";
118 constexpr const char* kArtdCancellationSignalType = "ArtdCancellationSignal";
119
120 // Timeout for short operations, such as merging profiles.
121 constexpr int kShortTimeoutSec = 60; // 1 minute.
122
123 // Timeout for long operations, such as compilation. We set it to be smaller than the Package
124 // Manager watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that if the operation
125 // is called from the Package Manager's thread handler, it will be aborted before that watchdog
126 // would take down the system server.
127 constexpr int kLongTimeoutSec = 570; // 9.5 minutes.
128
GetSize(std::string_view path)129 std::optional<int64_t> GetSize(std::string_view path) {
130 std::error_code ec;
131 int64_t size = std::filesystem::file_size(path, ec);
132 if (ec) {
133 // It is okay if the file does not exist. We don't have to log it.
134 if (ec.value() != ENOENT) {
135 LOG(ERROR) << "Failed to get the file size of '{}': {}"_format(path, ec.message());
136 }
137 return std::nullopt;
138 }
139 return size;
140 }
141
142 // Deletes a file. Returns the size of the deleted file, or 0 if the deleted file is empty or an
143 // error occurs.
GetSizeAndDeleteFile(const std::string & path)144 int64_t GetSizeAndDeleteFile(const std::string& path) {
145 std::optional<int64_t> size = GetSize(path);
146 if (!size.has_value()) {
147 return 0;
148 }
149
150 std::error_code ec;
151 if (!std::filesystem::remove(path, ec)) {
152 LOG(ERROR) << "Failed to remove '{}': {}"_format(path, ec.message());
153 return 0;
154 }
155
156 return size.value();
157 }
158
EscapeErrorMessage(const std::string & message)159 std::string EscapeErrorMessage(const std::string& message) {
160 return StringReplace(message, std::string("\0", /*n=*/1), "\\0", /*all=*/true);
161 }
162
163 // Indicates an error that should never happen (e.g., illegal arguments passed by service-art
164 // internally). System server should crash if this kind of error happens.
Fatal(const std::string & message)165 ScopedAStatus Fatal(const std::string& message) {
166 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
167 EscapeErrorMessage(message).c_str());
168 }
169
170 // Indicates an error that service-art should handle (e.g., I/O errors, sub-process crashes).
171 // The scope of the error depends on the function that throws it, so service-art should catch the
172 // error at every call site and take different actions.
173 // Ideally, this should be a checked exception or an additional return value that forces service-art
174 // to handle it, but `ServiceSpecificException` (a separate runtime exception type) is the best
175 // approximate we have given the limitation of Java and Binder.
NonFatal(const std::string & message)176 ScopedAStatus NonFatal(const std::string& message) {
177 constexpr int32_t kArtdNonFatalErrorCode = 1;
178 return ScopedAStatus::fromServiceSpecificErrorWithMessage(kArtdNonFatalErrorCode,
179 EscapeErrorMessage(message).c_str());
180 }
181
ParseCompilerFilter(const std::string & compiler_filter_str)182 Result<CompilerFilter::Filter> ParseCompilerFilter(const std::string& compiler_filter_str) {
183 CompilerFilter::Filter compiler_filter;
184 if (!CompilerFilter::ParseCompilerFilter(compiler_filter_str.c_str(), &compiler_filter)) {
185 return Errorf("Failed to parse compiler filter '{}'", compiler_filter_str);
186 }
187 return compiler_filter;
188 }
189
DexOptTriggerFromAidl(int32_t aidl_value)190 OatFileAssistant::DexOptTrigger DexOptTriggerFromAidl(int32_t aidl_value) {
191 OatFileAssistant::DexOptTrigger trigger{};
192 if ((aidl_value & static_cast<int32_t>(DexoptTrigger::COMPILER_FILTER_IS_BETTER)) != 0) {
193 trigger.targetFilterIsBetter = true;
194 }
195 if ((aidl_value & static_cast<int32_t>(DexoptTrigger::COMPILER_FILTER_IS_SAME)) != 0) {
196 trigger.targetFilterIsSame = true;
197 }
198 if ((aidl_value & static_cast<int32_t>(DexoptTrigger::COMPILER_FILTER_IS_WORSE)) != 0) {
199 trigger.targetFilterIsWorse = true;
200 }
201 if ((aidl_value & static_cast<int32_t>(DexoptTrigger::PRIMARY_BOOT_IMAGE_BECOMES_USABLE)) != 0) {
202 trigger.primaryBootImageBecomesUsable = true;
203 }
204 if ((aidl_value & static_cast<int32_t>(DexoptTrigger::NEED_EXTRACTION)) != 0) {
205 trigger.needExtraction = true;
206 }
207 return trigger;
208 }
209
ArtifactsLocationToAidl(OatFileAssistant::Location location)210 ArtifactsLocation ArtifactsLocationToAidl(OatFileAssistant::Location location) {
211 switch (location) {
212 case OatFileAssistant::Location::kLocationNoneOrError:
213 return ArtifactsLocation::NONE_OR_ERROR;
214 case OatFileAssistant::Location::kLocationOat:
215 return ArtifactsLocation::DALVIK_CACHE;
216 case OatFileAssistant::Location::kLocationOdex:
217 return ArtifactsLocation::NEXT_TO_DEX;
218 case OatFileAssistant::Location::kLocationDm:
219 return ArtifactsLocation::DM;
220 // No default. All cases should be explicitly handled, or the compilation will fail.
221 }
222 // This should never happen. Just in case we get a non-enumerator value.
223 LOG(FATAL) << "Unexpected Location " << location;
224 }
225
PrepareArtifactsDir(const std::string & path,const FsPermission & fs_permission)226 Result<void> PrepareArtifactsDir(const std::string& path, const FsPermission& fs_permission) {
227 std::error_code ec;
228 bool created = std::filesystem::create_directory(path, ec);
229 if (ec) {
230 return Errorf("Failed to create directory '{}': {}", path, ec.message());
231 }
232
233 auto cleanup = make_scope_guard([&] {
234 if (created) {
235 std::filesystem::remove(path, ec);
236 }
237 });
238
239 if (chmod(path.c_str(), DirFsPermissionToMode(fs_permission)) != 0) {
240 return ErrnoErrorf("Failed to chmod directory '{}'", path);
241 }
242 OR_RETURN(Chown(path, fs_permission));
243
244 cleanup.Disable();
245 return {};
246 }
247
PrepareArtifactsDirs(const OutputArtifacts & output_artifacts,std::string * oat_dir_path)248 Result<void> PrepareArtifactsDirs(const OutputArtifacts& output_artifacts,
249 /*out*/ std::string* oat_dir_path) {
250 if (output_artifacts.artifactsPath.isInDalvikCache) {
251 return {};
252 }
253
254 std::filesystem::path oat_path(OR_RETURN(BuildOatPath(output_artifacts.artifactsPath)));
255 std::filesystem::path isa_dir = oat_path.parent_path();
256 std::filesystem::path oat_dir = isa_dir.parent_path();
257 DCHECK_EQ(oat_dir.filename(), "oat");
258
259 OR_RETURN(PrepareArtifactsDir(oat_dir, output_artifacts.permissionSettings.dirFsPermission));
260 OR_RETURN(PrepareArtifactsDir(isa_dir, output_artifacts.permissionSettings.dirFsPermission));
261 *oat_dir_path = oat_dir;
262 return {};
263 }
264
Restorecon(const std::string & path,const std::optional<OutputArtifacts::PermissionSettings::SeContext> & se_context)265 Result<void> Restorecon(
266 const std::string& path,
267 const std::optional<OutputArtifacts::PermissionSettings::SeContext>& se_context) {
268 if (!kIsTargetAndroid) {
269 return {};
270 }
271
272 int res = 0;
273 if (se_context.has_value()) {
274 res = selinux_android_restorecon_pkgdir(path.c_str(),
275 se_context->seInfo.c_str(),
276 se_context->uid,
277 SELINUX_ANDROID_RESTORECON_RECURSE);
278 } else {
279 res = selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
280 }
281 if (res != 0) {
282 return ErrnoErrorf("Failed to restorecon directory '{}'", path);
283 }
284 return {};
285 }
286
GetFileVisibility(const std::string & file)287 Result<FileVisibility> GetFileVisibility(const std::string& file) {
288 std::error_code ec;
289 std::filesystem::file_status status = std::filesystem::status(file, ec);
290 if (!std::filesystem::status_known(status)) {
291 return Errorf("Failed to get status of '{}': {}", file, ec.message());
292 }
293 if (!std::filesystem::exists(status)) {
294 return FileVisibility::NOT_FOUND;
295 }
296
297 return (status.permissions() & std::filesystem::perms::others_read) !=
298 std::filesystem::perms::none ?
299 FileVisibility::OTHER_READABLE :
300 FileVisibility::NOT_OTHER_READABLE;
301 }
302
ToArtdCancellationSignal(IArtdCancellationSignal * input)303 Result<ArtdCancellationSignal*> ToArtdCancellationSignal(IArtdCancellationSignal* input) {
304 if (input == nullptr) {
305 return Error() << "Cancellation signal must not be nullptr";
306 }
307 // We cannot use `dynamic_cast` because ART code is compiled with `-fno-rtti`, so we have to check
308 // the magic number.
309 int64_t type;
310 if (!input->getType(&type).isOk() ||
311 type != reinterpret_cast<intptr_t>(kArtdCancellationSignalType)) {
312 // The cancellation signal must be created by `Artd::createCancellationSignal`.
313 return Error() << "Invalid cancellation signal type";
314 }
315 return static_cast<ArtdCancellationSignal*>(input);
316 }
317
CopyFile(const std::string & src_path,const NewFile & dst_file)318 Result<void> CopyFile(const std::string& src_path, const NewFile& dst_file) {
319 std::string content;
320 if (!ReadFileToString(src_path, &content)) {
321 return Errorf("Failed to read file '{}': {}", src_path, strerror(errno));
322 }
323 if (!WriteStringToFd(content, dst_file.Fd())) {
324 return Errorf("Failed to write file '{}': {}", dst_file.TempPath(), strerror(errno));
325 }
326 if (fsync(dst_file.Fd()) != 0) {
327 return Errorf("Failed to flush file '{}': {}", dst_file.TempPath(), strerror(errno));
328 }
329 if (lseek(dst_file.Fd(), /*offset=*/0, SEEK_SET) != 0) {
330 return Errorf(
331 "Failed to reset the offset for file '{}': {}", dst_file.TempPath(), strerror(errno));
332 }
333 return {};
334 }
335
SetLogVerbosity()336 Result<void> SetLogVerbosity() {
337 std::string options = android::base::GetProperty("dalvik.vm.artd-verbose", /*default_value=*/"");
338 if (options.empty()) {
339 return {};
340 }
341
342 CmdlineType<LogVerbosity> parser;
343 CmdlineParseResult<LogVerbosity> result = parser.Parse(options);
344 if (!result.IsSuccess()) {
345 return Error() << result.GetMessage();
346 }
347
348 gLogVerbosity = result.ReleaseValue();
349 return {};
350 }
351
352 class FdLogger {
353 public:
Add(const NewFile & file)354 void Add(const NewFile& file) { fd_mapping_.emplace_back(file.Fd(), file.TempPath()); }
Add(const File & file)355 void Add(const File& file) { fd_mapping_.emplace_back(file.Fd(), file.GetPath()); }
356
GetFds()357 std::string GetFds() {
358 std::vector<int> fds;
359 fds.reserve(fd_mapping_.size());
360 for (const auto& [fd, path] : fd_mapping_) {
361 fds.push_back(fd);
362 }
363 return Join(fds, ':');
364 }
365
366 private:
367 std::vector<std::pair<int, std::string>> fd_mapping_;
368
369 friend std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger);
370 };
371
operator <<(std::ostream & os,const FdLogger & fd_logger)372 std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger) {
373 for (const auto& [fd, path] : fd_logger.fd_mapping_) {
374 os << fd << ":" << path << ' ';
375 }
376 return os;
377 }
378
379 } // namespace
380
381 #define OR_RETURN_ERROR(func, expr) \
382 ({ \
383 decltype(expr)&& tmp = (expr); \
384 if (!tmp.ok()) { \
385 return (func)(tmp.error().message()); \
386 } \
387 std::move(tmp).value(); \
388 })
389
390 #define OR_RETURN_FATAL(expr) OR_RETURN_ERROR(Fatal, expr)
391 #define OR_RETURN_NON_FATAL(expr) OR_RETURN_ERROR(NonFatal, expr)
392
isAlive(bool * _aidl_return)393 ScopedAStatus Artd::isAlive(bool* _aidl_return) {
394 *_aidl_return = true;
395 return ScopedAStatus::ok();
396 }
397
deleteArtifacts(const ArtifactsPath & in_artifactsPath,int64_t * _aidl_return)398 ScopedAStatus Artd::deleteArtifacts(const ArtifactsPath& in_artifactsPath, int64_t* _aidl_return) {
399 std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath));
400
401 *_aidl_return = 0;
402 *_aidl_return += GetSizeAndDeleteFile(oat_path);
403 *_aidl_return += GetSizeAndDeleteFile(OatPathToVdexPath(oat_path));
404 *_aidl_return += GetSizeAndDeleteFile(OatPathToArtPath(oat_path));
405
406 return ScopedAStatus::ok();
407 }
408
getDexoptStatus(const std::string & in_dexFile,const std::string & in_instructionSet,const std::optional<std::string> & in_classLoaderContext,GetDexoptStatusResult * _aidl_return)409 ScopedAStatus Artd::getDexoptStatus(const std::string& in_dexFile,
410 const std::string& in_instructionSet,
411 const std::optional<std::string>& in_classLoaderContext,
412 GetDexoptStatusResult* _aidl_return) {
413 Result<OatFileAssistantContext*> ofa_context = GetOatFileAssistantContext();
414 if (!ofa_context.ok()) {
415 return NonFatal("Failed to get runtime options: " + ofa_context.error().message());
416 }
417
418 std::unique_ptr<ClassLoaderContext> context;
419 std::string error_msg;
420 auto oat_file_assistant = OatFileAssistant::Create(in_dexFile,
421 in_instructionSet,
422 in_classLoaderContext,
423 /*load_executable=*/false,
424 /*only_load_trusted_executable=*/true,
425 ofa_context.value(),
426 &context,
427 &error_msg);
428 if (oat_file_assistant == nullptr) {
429 return NonFatal("Failed to create OatFileAssistant: " + error_msg);
430 }
431
432 std::string ignored_odex_status;
433 oat_file_assistant->GetOptimizationStatus(&_aidl_return->locationDebugString,
434 &_aidl_return->compilerFilter,
435 &_aidl_return->compilationReason,
436 &ignored_odex_status);
437
438 // We ignore odex_status because it is not meaningful. It can only be either "up-to-date",
439 // "apk-more-recent", or "io-error-no-oat", which means it doesn't give us information in addition
440 // to what we can learn from compiler_filter because compiler_filter will be the actual compiler
441 // filter, "run-from-apk-fallback", and "run-from-apk" in those three cases respectively.
442 DCHECK(ignored_odex_status == "up-to-date" || ignored_odex_status == "apk-more-recent" ||
443 ignored_odex_status == "io-error-no-oat");
444
445 return ScopedAStatus::ok();
446 }
447
isProfileUsable(const ProfilePath & in_profile,const std::string & in_dexFile,bool * _aidl_return)448 ndk::ScopedAStatus Artd::isProfileUsable(const ProfilePath& in_profile,
449 const std::string& in_dexFile,
450 bool* _aidl_return) {
451 std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
452 OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
453
454 FdLogger fd_logger;
455
456 CmdlineBuilder art_exec_args;
457 art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
458
459 CmdlineBuilder args;
460 args.Add(OR_RETURN_FATAL(GetProfman()));
461
462 Result<std::unique_ptr<File>> profile = OpenFileForReading(profile_path);
463 if (!profile.ok()) {
464 if (profile.error().code() == ENOENT) {
465 *_aidl_return = false;
466 return ScopedAStatus::ok();
467 }
468 return NonFatal(
469 "Failed to open profile '{}': {}"_format(profile_path, profile.error().message()));
470 }
471 args.Add("--reference-profile-file-fd=%d", profile.value()->Fd());
472 fd_logger.Add(*profile.value());
473
474 std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
475 args.Add("--apk-fd=%d", dex_file->Fd());
476 fd_logger.Add(*dex_file);
477
478 art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
479
480 LOG(INFO) << "Running profman: " << Join(art_exec_args.Get(), /*separator=*/" ")
481 << "\nOpened FDs: " << fd_logger;
482
483 Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec);
484 if (!result.ok()) {
485 return NonFatal("Failed to run profman: " + result.error().message());
486 }
487
488 LOG(INFO) << "profman returned code {}"_format(result.value());
489
490 if (result.value() != ProfmanResult::kSkipCompilationSmallDelta &&
491 result.value() != ProfmanResult::kSkipCompilationEmptyProfiles) {
492 return NonFatal("profman returned an unexpected code: {}"_format(result.value()));
493 }
494
495 *_aidl_return = result.value() == ProfmanResult::kSkipCompilationSmallDelta;
496 return ScopedAStatus::ok();
497 }
498
copyAndRewriteProfile(const ProfilePath & in_src,OutputProfile * in_dst,const std::string & in_dexFile,bool * _aidl_return)499 ndk::ScopedAStatus Artd::copyAndRewriteProfile(const ProfilePath& in_src,
500 OutputProfile* in_dst,
501 const std::string& in_dexFile,
502 bool* _aidl_return) {
503 std::string src_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_src));
504 std::string dst_path = OR_RETURN_FATAL(BuildFinalProfilePath(in_dst->profilePath));
505 OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
506
507 FdLogger fd_logger;
508
509 CmdlineBuilder art_exec_args;
510 art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
511
512 CmdlineBuilder args;
513 args.Add(OR_RETURN_FATAL(GetProfman())).Add("--copy-and-update-profile-key");
514
515 Result<std::unique_ptr<File>> src = OpenFileForReading(src_path);
516 if (!src.ok()) {
517 if (src.error().code() == ENOENT) {
518 *_aidl_return = false;
519 return ScopedAStatus::ok();
520 }
521 return NonFatal("Failed to open src profile '{}': {}"_format(src_path, src.error().message()));
522 }
523 args.Add("--profile-file-fd=%d", src.value()->Fd());
524 fd_logger.Add(*src.value());
525
526 std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
527 args.Add("--apk-fd=%d", dex_file->Fd());
528 fd_logger.Add(*dex_file);
529
530 std::unique_ptr<NewFile> dst =
531 OR_RETURN_NON_FATAL(NewFile::Create(dst_path, in_dst->fsPermission));
532 args.Add("--reference-profile-file-fd=%d", dst->Fd());
533 fd_logger.Add(*dst);
534
535 art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
536
537 LOG(INFO) << "Running profman: " << Join(art_exec_args.Get(), /*separator=*/" ")
538 << "\nOpened FDs: " << fd_logger;
539
540 Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec);
541 if (!result.ok()) {
542 return NonFatal("Failed to run profman: " + result.error().message());
543 }
544
545 LOG(INFO) << "profman returned code {}"_format(result.value());
546
547 if (result.value() == ProfmanResult::kCopyAndUpdateNoMatch) {
548 *_aidl_return = false;
549 return ScopedAStatus::ok();
550 }
551
552 if (result.value() != ProfmanResult::kCopyAndUpdateSuccess) {
553 return NonFatal("profman returned an unexpected code: {}"_format(result.value()));
554 }
555
556 OR_RETURN_NON_FATAL(dst->Keep());
557 *_aidl_return = true;
558 in_dst->profilePath.id = dst->TempId();
559 in_dst->profilePath.tmpPath = dst->TempPath();
560 return ScopedAStatus::ok();
561 }
562
commitTmpProfile(const TmpProfilePath & in_profile)563 ndk::ScopedAStatus Artd::commitTmpProfile(const TmpProfilePath& in_profile) {
564 std::string tmp_profile_path = OR_RETURN_FATAL(BuildTmpProfilePath(in_profile));
565 std::string ref_profile_path = OR_RETURN_FATAL(BuildFinalProfilePath(in_profile));
566
567 std::error_code ec;
568 std::filesystem::rename(tmp_profile_path, ref_profile_path, ec);
569 if (ec) {
570 return NonFatal(
571 "Failed to move '{}' to '{}': {}"_format(tmp_profile_path, ref_profile_path, ec.message()));
572 }
573
574 return ScopedAStatus::ok();
575 }
576
deleteProfile(const ProfilePath & in_profile)577 ndk::ScopedAStatus Artd::deleteProfile(const ProfilePath& in_profile) {
578 std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
579
580 std::error_code ec;
581 std::filesystem::remove(profile_path, ec);
582 if (ec) {
583 LOG(ERROR) << "Failed to remove '{}': {}"_format(profile_path, ec.message());
584 }
585
586 return ScopedAStatus::ok();
587 }
588
getProfileVisibility(const ProfilePath & in_profile,FileVisibility * _aidl_return)589 ndk::ScopedAStatus Artd::getProfileVisibility(const ProfilePath& in_profile,
590 FileVisibility* _aidl_return) {
591 std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
592 *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(profile_path));
593 return ScopedAStatus::ok();
594 }
595
getArtifactsVisibility(const ArtifactsPath & in_artifactsPath,FileVisibility * _aidl_return)596 ndk::ScopedAStatus Artd::getArtifactsVisibility(const ArtifactsPath& in_artifactsPath,
597 FileVisibility* _aidl_return) {
598 std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath));
599 *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(oat_path));
600 return ScopedAStatus::ok();
601 }
602
getDexFileVisibility(const std::string & in_dexFile,FileVisibility * _aidl_return)603 ndk::ScopedAStatus Artd::getDexFileVisibility(const std::string& in_dexFile,
604 FileVisibility* _aidl_return) {
605 OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
606 *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(in_dexFile));
607 return ScopedAStatus::ok();
608 }
609
getDmFileVisibility(const DexMetadataPath & in_dmFile,FileVisibility * _aidl_return)610 ndk::ScopedAStatus Artd::getDmFileVisibility(const DexMetadataPath& in_dmFile,
611 FileVisibility* _aidl_return) {
612 std::string dm_path = OR_RETURN_FATAL(BuildDexMetadataPath(in_dmFile));
613 *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(dm_path));
614 return ScopedAStatus::ok();
615 }
616
mergeProfiles(const std::vector<ProfilePath> & in_profiles,const std::optional<ProfilePath> & in_referenceProfile,OutputProfile * in_outputProfile,const std::vector<std::string> & in_dexFiles,const MergeProfileOptions & in_options,bool * _aidl_return)617 ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profiles,
618 const std::optional<ProfilePath>& in_referenceProfile,
619 OutputProfile* in_outputProfile,
620 const std::vector<std::string>& in_dexFiles,
621 const MergeProfileOptions& in_options,
622 bool* _aidl_return) {
623 std::vector<std::string> profile_paths;
624 for (const ProfilePath& profile : in_profiles) {
625 std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(profile));
626 if (profile.getTag() == ProfilePath::dexMetadataPath) {
627 return Fatal("Does not support DM file, got '{}'"_format(profile_path));
628 }
629 profile_paths.push_back(std::move(profile_path));
630 }
631 std::string output_profile_path =
632 OR_RETURN_FATAL(BuildFinalProfilePath(in_outputProfile->profilePath));
633 for (const std::string& dex_file : in_dexFiles) {
634 OR_RETURN_FATAL(ValidateDexPath(dex_file));
635 }
636 if (in_options.forceMerge + in_options.dumpOnly + in_options.dumpClassesAndMethods > 1) {
637 return Fatal("Only one of 'forceMerge', 'dumpOnly', and 'dumpClassesAndMethods' can be set");
638 }
639
640 FdLogger fd_logger;
641
642 CmdlineBuilder art_exec_args;
643 art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
644
645 CmdlineBuilder args;
646 args.Add(OR_RETURN_FATAL(GetProfman()));
647
648 std::vector<std::unique_ptr<File>> profile_files;
649 for (const std::string& profile_path : profile_paths) {
650 Result<std::unique_ptr<File>> profile_file = OpenFileForReading(profile_path);
651 if (!profile_file.ok()) {
652 if (profile_file.error().code() == ENOENT) {
653 // Skip non-existing file.
654 continue;
655 }
656 return NonFatal(
657 "Failed to open profile '{}': {}"_format(profile_path, profile_file.error().message()));
658 }
659 args.Add("--profile-file-fd=%d", profile_file.value()->Fd());
660 fd_logger.Add(*profile_file.value());
661 profile_files.push_back(std::move(profile_file.value()));
662 }
663
664 if (profile_files.empty()) {
665 LOG(INFO) << "Merge skipped because there are no existing profiles";
666 *_aidl_return = false;
667 return ScopedAStatus::ok();
668 }
669
670 std::unique_ptr<NewFile> output_profile_file =
671 OR_RETURN_NON_FATAL(NewFile::Create(output_profile_path, in_outputProfile->fsPermission));
672
673 if (in_referenceProfile.has_value()) {
674 if (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) {
675 return Fatal(
676 "Reference profile must not be set when 'forceMerge', 'dumpOnly', or "
677 "'dumpClassesAndMethods' is set");
678 }
679 std::string reference_profile_path =
680 OR_RETURN_FATAL(BuildProfileOrDmPath(*in_referenceProfile));
681 if (in_referenceProfile->getTag() == ProfilePath::dexMetadataPath) {
682 return Fatal("Does not support DM file, got '{}'"_format(reference_profile_path));
683 }
684 OR_RETURN_NON_FATAL(CopyFile(reference_profile_path, *output_profile_file));
685 }
686
687 if (in_options.dumpOnly || in_options.dumpClassesAndMethods) {
688 args.Add("--dump-output-to-fd=%d", output_profile_file->Fd());
689 } else {
690 // profman is ok with this being an empty file when in_referenceProfile isn't set.
691 args.Add("--reference-profile-file-fd=%d", output_profile_file->Fd());
692 }
693 fd_logger.Add(*output_profile_file);
694
695 std::vector<std::unique_ptr<File>> dex_files;
696 for (const std::string& dex_path : in_dexFiles) {
697 std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(dex_path));
698 args.Add("--apk-fd=%d", dex_file->Fd());
699 fd_logger.Add(*dex_file);
700 dex_files.push_back(std::move(dex_file));
701 }
702
703 if (in_options.dumpOnly || in_options.dumpClassesAndMethods) {
704 args.Add(in_options.dumpOnly ? "--dump-only" : "--dump-classes-and-methods");
705 } else {
706 args.AddIfNonEmpty("--min-new-classes-percent-change=%s",
707 props_->GetOrEmpty("dalvik.vm.bgdexopt.new-classes-percent"))
708 .AddIfNonEmpty("--min-new-methods-percent-change=%s",
709 props_->GetOrEmpty("dalvik.vm.bgdexopt.new-methods-percent"))
710 .AddIf(in_options.forceMerge, "--force-merge")
711 .AddIf(in_options.forBootImage, "--boot-image-merge");
712 }
713
714 art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
715
716 LOG(INFO) << "Running profman: " << Join(art_exec_args.Get(), /*separator=*/" ")
717 << "\nOpened FDs: " << fd_logger;
718
719 Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec);
720 if (!result.ok()) {
721 return NonFatal("Failed to run profman: " + result.error().message());
722 }
723
724 LOG(INFO) << "profman returned code {}"_format(result.value());
725
726 if (result.value() == ProfmanResult::kSkipCompilationSmallDelta ||
727 result.value() == ProfmanResult::kSkipCompilationEmptyProfiles) {
728 *_aidl_return = false;
729 return ScopedAStatus::ok();
730 }
731
732 ProfmanResult::ProcessingResult expected_result =
733 (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) ?
734 ProfmanResult::kSuccess :
735 ProfmanResult::kCompile;
736 if (result.value() != expected_result) {
737 return NonFatal("profman returned an unexpected code: {}"_format(result.value()));
738 }
739
740 OR_RETURN_NON_FATAL(output_profile_file->Keep());
741 *_aidl_return = true;
742 in_outputProfile->profilePath.id = output_profile_file->TempId();
743 in_outputProfile->profilePath.tmpPath = output_profile_file->TempPath();
744 return ScopedAStatus::ok();
745 }
746
getDexoptNeeded(const std::string & in_dexFile,const std::string & in_instructionSet,const std::optional<std::string> & in_classLoaderContext,const std::string & in_compilerFilter,int32_t in_dexoptTrigger,GetDexoptNeededResult * _aidl_return)747 ndk::ScopedAStatus Artd::getDexoptNeeded(const std::string& in_dexFile,
748 const std::string& in_instructionSet,
749 const std::optional<std::string>& in_classLoaderContext,
750 const std::string& in_compilerFilter,
751 int32_t in_dexoptTrigger,
752 GetDexoptNeededResult* _aidl_return) {
753 Result<OatFileAssistantContext*> ofa_context = GetOatFileAssistantContext();
754 if (!ofa_context.ok()) {
755 return NonFatal("Failed to get runtime options: " + ofa_context.error().message());
756 }
757
758 std::unique_ptr<ClassLoaderContext> context;
759 std::string error_msg;
760 auto oat_file_assistant = OatFileAssistant::Create(in_dexFile,
761 in_instructionSet,
762 in_classLoaderContext,
763 /*load_executable=*/false,
764 /*only_load_trusted_executable=*/true,
765 ofa_context.value(),
766 &context,
767 &error_msg);
768 if (oat_file_assistant == nullptr) {
769 return NonFatal("Failed to create OatFileAssistant: " + error_msg);
770 }
771
772 OatFileAssistant::DexOptStatus status;
773 _aidl_return->isDexoptNeeded =
774 oat_file_assistant->GetDexOptNeeded(OR_RETURN_FATAL(ParseCompilerFilter(in_compilerFilter)),
775 DexOptTriggerFromAidl(in_dexoptTrigger),
776 &status);
777 _aidl_return->isVdexUsable = status.IsVdexUsable();
778 _aidl_return->artifactsLocation = ArtifactsLocationToAidl(status.GetLocation());
779
780 return ScopedAStatus::ok();
781 }
782
dexopt(const OutputArtifacts & in_outputArtifacts,const std::string & in_dexFile,const std::string & in_instructionSet,const std::optional<std::string> & in_classLoaderContext,const std::string & in_compilerFilter,const std::optional<ProfilePath> & in_profile,const std::optional<VdexPath> & in_inputVdex,const std::optional<DexMetadataPath> & in_dmFile,PriorityClass in_priorityClass,const DexoptOptions & in_dexoptOptions,const std::shared_ptr<IArtdCancellationSignal> & in_cancellationSignal,ArtdDexoptResult * _aidl_return)783 ndk::ScopedAStatus Artd::dexopt(
784 const OutputArtifacts& in_outputArtifacts,
785 const std::string& in_dexFile,
786 const std::string& in_instructionSet,
787 const std::optional<std::string>& in_classLoaderContext,
788 const std::string& in_compilerFilter,
789 const std::optional<ProfilePath>& in_profile,
790 const std::optional<VdexPath>& in_inputVdex,
791 const std::optional<DexMetadataPath>& in_dmFile,
792 PriorityClass in_priorityClass,
793 const DexoptOptions& in_dexoptOptions,
794 const std::shared_ptr<IArtdCancellationSignal>& in_cancellationSignal,
795 ArtdDexoptResult* _aidl_return) {
796 _aidl_return->cancelled = false;
797
798 std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_outputArtifacts.artifactsPath));
799 std::string vdex_path = OatPathToVdexPath(oat_path);
800 std::string art_path = OatPathToArtPath(oat_path);
801 OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
802 std::optional<std::string> profile_path =
803 in_profile.has_value() ?
804 std::make_optional(OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile.value()))) :
805 std::nullopt;
806 ArtdCancellationSignal* cancellation_signal =
807 OR_RETURN_FATAL(ToArtdCancellationSignal(in_cancellationSignal.get()));
808
809 std::unique_ptr<ClassLoaderContext> context = nullptr;
810 if (in_classLoaderContext.has_value()) {
811 context = ClassLoaderContext::Create(in_classLoaderContext->c_str());
812 if (context == nullptr) {
813 return Fatal("Class loader context '{}' is invalid"_format(in_classLoaderContext.value()));
814 }
815 }
816
817 std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache.
818 OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts, &oat_dir_path));
819
820 // First-round restorecon. artd doesn't have the permission to create files with the
821 // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will
822 // inherit `dalvikcache_data_file` rather than `apk_data_file`.
823 if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
824 OR_RETURN_NON_FATAL(Restorecon(oat_dir_path, in_outputArtifacts.permissionSettings.seContext));
825 }
826
827 FdLogger fd_logger;
828
829 CmdlineBuilder art_exec_args;
830 art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
831
832 CmdlineBuilder args;
833 args.Add(OR_RETURN_FATAL(GetDex2Oat()));
834
835 const FsPermission& fs_permission = in_outputArtifacts.permissionSettings.fileFsPermission;
836
837 std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
838 args.Add("--zip-fd=%d", dex_file->Fd()).Add("--zip-location=%s", in_dexFile);
839 fd_logger.Add(*dex_file);
840 struct stat dex_st = OR_RETURN_NON_FATAL(Fstat(*dex_file));
841 if ((dex_st.st_mode & S_IROTH) == 0) {
842 if (fs_permission.isOtherReadable) {
843 return NonFatal(
844 "Outputs cannot be other-readable because the dex file '{}' is not other-readable"_format(
845 dex_file->GetPath()));
846 }
847 // Negative numbers mean no `chown`. 0 means root.
848 // Note: this check is more strict than it needs to be. For example, it doesn't allow the
849 // outputs to belong to a group that is a subset of the dex file's group. This is for
850 // simplicity, and it's okay as we don't have to handle such complicated cases in practice.
851 if ((fs_permission.uid > 0 && static_cast<uid_t>(fs_permission.uid) != dex_st.st_uid) ||
852 (fs_permission.gid > 0 && static_cast<gid_t>(fs_permission.gid) != dex_st.st_uid &&
853 static_cast<gid_t>(fs_permission.gid) != dex_st.st_gid)) {
854 return NonFatal(
855 "Outputs' owner doesn't match the dex file '{}' (outputs: {}:{}, dex file: {}:{})"_format(
856 dex_file->GetPath(),
857 fs_permission.uid,
858 fs_permission.gid,
859 dex_st.st_uid,
860 dex_st.st_gid));
861 }
862 }
863
864 std::unique_ptr<NewFile> oat_file = OR_RETURN_NON_FATAL(NewFile::Create(oat_path, fs_permission));
865 args.Add("--oat-fd=%d", oat_file->Fd()).Add("--oat-location=%s", oat_path);
866 fd_logger.Add(*oat_file);
867
868 std::unique_ptr<NewFile> vdex_file =
869 OR_RETURN_NON_FATAL(NewFile::Create(vdex_path, fs_permission));
870 args.Add("--output-vdex-fd=%d", vdex_file->Fd());
871 fd_logger.Add(*vdex_file);
872
873 std::vector<NewFile*> files_to_commit{oat_file.get(), vdex_file.get()};
874 std::vector<std::string_view> files_to_delete;
875
876 std::unique_ptr<NewFile> art_file = nullptr;
877 if (in_dexoptOptions.generateAppImage) {
878 art_file = OR_RETURN_NON_FATAL(NewFile::Create(art_path, fs_permission));
879 args.Add("--app-image-fd=%d", art_file->Fd());
880 args.AddIfNonEmpty("--image-format=%s", props_->GetOrEmpty("dalvik.vm.appimageformat"));
881 fd_logger.Add(*art_file);
882 files_to_commit.push_back(art_file.get());
883 } else {
884 files_to_delete.push_back(art_path);
885 }
886
887 std::unique_ptr<NewFile> swap_file = nullptr;
888 if (ShouldCreateSwapFileForDexopt()) {
889 swap_file = OR_RETURN_NON_FATAL(
890 NewFile::Create("{}.swap"_format(oat_path), FsPermission{.uid = -1, .gid = -1}));
891 args.Add("--swap-fd=%d", swap_file->Fd());
892 fd_logger.Add(*swap_file);
893 }
894
895 std::vector<std::unique_ptr<File>> context_files;
896 if (context != nullptr) {
897 std::vector<std::string> flattened_context = context->FlattenDexPaths();
898 std::string dex_dir = Dirname(in_dexFile.c_str());
899 std::vector<int> context_fds;
900 for (const std::string& context_element : flattened_context) {
901 std::string context_path = std::filesystem::path(dex_dir).append(context_element);
902 OR_RETURN_FATAL(ValidateDexPath(context_path));
903 std::unique_ptr<File> context_file = OR_RETURN_NON_FATAL(OpenFileForReading(context_path));
904 context_fds.push_back(context_file->Fd());
905 fd_logger.Add(*context_file);
906 context_files.push_back(std::move(context_file));
907 }
908 args.AddIfNonEmpty("--class-loader-context-fds=%s", Join(context_fds, /*separator=*/':'))
909 .Add("--class-loader-context=%s", in_classLoaderContext.value())
910 .Add("--classpath-dir=%s", dex_dir);
911 }
912
913 std::unique_ptr<File> input_vdex_file = nullptr;
914 if (in_inputVdex.has_value()) {
915 std::string input_vdex_path = OR_RETURN_FATAL(BuildVdexPath(in_inputVdex.value()));
916 input_vdex_file = OR_RETURN_NON_FATAL(OpenFileForReading(input_vdex_path));
917 args.Add("--input-vdex-fd=%d", input_vdex_file->Fd());
918 fd_logger.Add(*input_vdex_file);
919 }
920
921 std::unique_ptr<File> dm_file = nullptr;
922 if (in_dmFile.has_value()) {
923 std::string dm_path = OR_RETURN_FATAL(BuildDexMetadataPath(in_dmFile.value()));
924 dm_file = OR_RETURN_NON_FATAL(OpenFileForReading(dm_path));
925 args.Add("--dm-fd=%d", dm_file->Fd());
926 fd_logger.Add(*dm_file);
927 }
928
929 std::unique_ptr<File> profile_file = nullptr;
930 if (profile_path.has_value()) {
931 profile_file = OR_RETURN_NON_FATAL(OpenFileForReading(profile_path.value()));
932 args.Add("--profile-file-fd=%d", profile_file->Fd());
933 fd_logger.Add(*profile_file);
934 struct stat profile_st = OR_RETURN_NON_FATAL(Fstat(*profile_file));
935 if (fs_permission.isOtherReadable && (profile_st.st_mode & S_IROTH) == 0) {
936 return NonFatal(
937 "Outputs cannot be other-readable because the profile '{}' is not other-readable"_format(
938 profile_file->GetPath()));
939 }
940 // TODO(b/260228411): Check uid and gid.
941 }
942
943 // Second-round restorecon. Restorecon recursively after the output files are created, so that the
944 // SELinux context is applied to all of them. The SELinux context of a file is mostly inherited
945 // from the parent directory upon creation, but the MLS label is not inherited, so we need to
946 // restorecon every file so that they have the right MLS label. If the files are in dalvik-cache,
947 // there's no need to restorecon because they inherits the SELinux context of the dalvik-cache
948 // directory and they don't need to have MLS labels.
949 if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
950 OR_RETURN_NON_FATAL(Restorecon(oat_dir_path, in_outputArtifacts.permissionSettings.seContext));
951 }
952
953 AddBootImageFlags(args);
954 AddCompilerConfigFlags(
955 in_instructionSet, in_compilerFilter, in_priorityClass, in_dexoptOptions, args);
956 AddPerfConfigFlags(in_priorityClass, art_exec_args, args);
957
958 // For being surfaced in crash reports on crashes.
959 args.Add("--comments=%s", in_dexoptOptions.comments);
960
961 art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
962
963 LOG(INFO) << "Running dex2oat: " << Join(art_exec_args.Get(), /*separator=*/" ")
964 << "\nOpened FDs: " << fd_logger;
965
966 ExecCallbacks callbacks{
967 .on_start =
968 [&](pid_t pid) {
969 std::lock_guard<std::mutex> lock(cancellation_signal->mu_);
970 cancellation_signal->pids_.insert(pid);
971 // Handle cancellation signals sent before the process starts.
972 if (cancellation_signal->is_cancelled_) {
973 int res = kill_(pid, SIGKILL);
974 DCHECK_EQ(res, 0);
975 }
976 },
977 .on_end =
978 [&](pid_t pid) {
979 std::lock_guard<std::mutex> lock(cancellation_signal->mu_);
980 // The pid should no longer receive kill signals sent by `cancellation_signal`.
981 cancellation_signal->pids_.erase(pid);
982 },
983 };
984
985 ProcessStat stat;
986 Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kLongTimeoutSec, callbacks, &stat);
987 _aidl_return->wallTimeMs = stat.wall_time_ms;
988 _aidl_return->cpuTimeMs = stat.cpu_time_ms;
989 if (!result.ok()) {
990 {
991 std::lock_guard<std::mutex> lock(cancellation_signal->mu_);
992 if (cancellation_signal->is_cancelled_) {
993 _aidl_return->cancelled = true;
994 return ScopedAStatus::ok();
995 }
996 }
997 return NonFatal("Failed to run dex2oat: " + result.error().message());
998 }
999
1000 LOG(INFO) << "dex2oat returned code {}"_format(result.value());
1001
1002 if (result.value() != 0) {
1003 return NonFatal("dex2oat returned an unexpected code: {}"_format(result.value()));
1004 }
1005
1006 int64_t size_bytes = 0;
1007 int64_t size_before_bytes = 0;
1008 for (const NewFile* file : files_to_commit) {
1009 size_bytes += GetSize(file->TempPath()).value_or(0);
1010 size_before_bytes += GetSize(file->FinalPath()).value_or(0);
1011 }
1012 for (std::string_view path : files_to_delete) {
1013 size_before_bytes += GetSize(path).value_or(0);
1014 }
1015 OR_RETURN_NON_FATAL(NewFile::CommitAllOrAbandon(files_to_commit, files_to_delete));
1016
1017 _aidl_return->sizeBytes = size_bytes;
1018 _aidl_return->sizeBeforeBytes = size_before_bytes;
1019 return ScopedAStatus::ok();
1020 }
1021
cancel()1022 ScopedAStatus ArtdCancellationSignal::cancel() {
1023 std::lock_guard<std::mutex> lock(mu_);
1024 is_cancelled_ = true;
1025 for (pid_t pid : pids_) {
1026 int res = kill_(pid, SIGKILL);
1027 DCHECK_EQ(res, 0);
1028 }
1029 return ScopedAStatus::ok();
1030 }
1031
getType(int64_t * _aidl_return)1032 ScopedAStatus ArtdCancellationSignal::getType(int64_t* _aidl_return) {
1033 *_aidl_return = reinterpret_cast<intptr_t>(kArtdCancellationSignalType);
1034 return ScopedAStatus::ok();
1035 }
1036
createCancellationSignal(std::shared_ptr<IArtdCancellationSignal> * _aidl_return)1037 ScopedAStatus Artd::createCancellationSignal(
1038 std::shared_ptr<IArtdCancellationSignal>* _aidl_return) {
1039 *_aidl_return = ndk::SharedRefBase::make<ArtdCancellationSignal>(kill_);
1040 return ScopedAStatus::ok();
1041 }
1042
cleanup(const std::vector<ProfilePath> & in_profilesToKeep,const std::vector<ArtifactsPath> & in_artifactsToKeep,const std::vector<VdexPath> & in_vdexFilesToKeep,int64_t * _aidl_return)1043 ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep,
1044 const std::vector<ArtifactsPath>& in_artifactsToKeep,
1045 const std::vector<VdexPath>& in_vdexFilesToKeep,
1046 int64_t* _aidl_return) {
1047 std::unordered_set<std::string> files_to_keep;
1048 for (const ProfilePath& profile : in_profilesToKeep) {
1049 files_to_keep.insert(OR_RETURN_FATAL(BuildProfileOrDmPath(profile)));
1050 }
1051 for (const ArtifactsPath& artifacts : in_artifactsToKeep) {
1052 std::string oat_path = OR_RETURN_FATAL(BuildOatPath(artifacts));
1053 files_to_keep.insert(OatPathToVdexPath(oat_path));
1054 files_to_keep.insert(OatPathToArtPath(oat_path));
1055 files_to_keep.insert(std::move(oat_path));
1056 }
1057 for (const VdexPath& vdex : in_vdexFilesToKeep) {
1058 files_to_keep.insert(OR_RETURN_FATAL(BuildVdexPath(vdex)));
1059 }
1060 *_aidl_return = 0;
1061 for (const std::string& file : OR_RETURN_NON_FATAL(ListManagedFiles())) {
1062 if (files_to_keep.find(file) == files_to_keep.end()) {
1063 LOG(INFO) << "Cleaning up obsolete file '{}'"_format(file);
1064 *_aidl_return += GetSizeAndDeleteFile(file);
1065 }
1066 }
1067 return ScopedAStatus::ok();
1068 }
1069
isIncrementalFsPath(const std::string & in_dexFile,bool * _aidl_return)1070 ScopedAStatus Artd::isIncrementalFsPath(const std::string& in_dexFile [[maybe_unused]],
1071 bool* _aidl_return) {
1072 #ifdef __BIONIC__
1073 OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
1074 struct statfs st;
1075 if (statfs(in_dexFile.c_str(), &st) != 0) {
1076 PLOG(ERROR) << "Failed to statfs '{}'"_format(in_dexFile);
1077 *_aidl_return = false;
1078 return ScopedAStatus::ok();
1079 }
1080 *_aidl_return = st.f_type == INCFS_MAGIC_NUMBER;
1081 return ScopedAStatus::ok();
1082 #else
1083 *_aidl_return = false;
1084 return ScopedAStatus::ok();
1085 #endif
1086 }
1087
Start()1088 Result<void> Artd::Start() {
1089 OR_RETURN(SetLogVerbosity());
1090
1091 ScopedAStatus status = ScopedAStatus::fromStatus(
1092 AServiceManager_registerLazyService(this->asBinder().get(), kServiceName));
1093 if (!status.isOk()) {
1094 return Error() << status.getDescription();
1095 }
1096
1097 ABinderProcess_startThreadPool();
1098
1099 return {};
1100 }
1101
GetOatFileAssistantContext()1102 Result<OatFileAssistantContext*> Artd::GetOatFileAssistantContext() {
1103 std::lock_guard<std::mutex> lock(ofa_context_mu_);
1104
1105 if (ofa_context_ == nullptr) {
1106 ofa_context_ = std::make_unique<OatFileAssistantContext>(
1107 std::make_unique<OatFileAssistantContext::RuntimeOptions>(
1108 OatFileAssistantContext::RuntimeOptions{
1109 .image_locations = *OR_RETURN(GetBootImageLocations()),
1110 .boot_class_path = *OR_RETURN(GetBootClassPath()),
1111 .boot_class_path_locations = *OR_RETURN(GetBootClassPath()),
1112 .deny_art_apex_data_files = DenyArtApexDataFiles(),
1113 }));
1114 std::string error_msg;
1115 if (!ofa_context_->FetchAll(&error_msg)) {
1116 return Error() << error_msg;
1117 }
1118 }
1119
1120 return ofa_context_.get();
1121 }
1122
GetBootImageLocations()1123 Result<const std::vector<std::string>*> Artd::GetBootImageLocations() {
1124 std::lock_guard<std::mutex> lock(cache_mu_);
1125
1126 if (!cached_boot_image_locations_.has_value()) {
1127 std::string location_str;
1128
1129 if (UseJitZygoteLocked()) {
1130 location_str = GetJitZygoteBootImageLocation();
1131 } else if (std::string value = GetUserDefinedBootImageLocationsLocked(); !value.empty()) {
1132 location_str = std::move(value);
1133 } else {
1134 std::string error_msg;
1135 std::string android_root = GetAndroidRootSafe(&error_msg);
1136 if (!error_msg.empty()) {
1137 return Errorf("Failed to get ANDROID_ROOT: {}", error_msg);
1138 }
1139 location_str = GetDefaultBootImageLocation(android_root, DenyArtApexDataFilesLocked());
1140 }
1141
1142 cached_boot_image_locations_ = Split(location_str, ":");
1143 }
1144
1145 return &cached_boot_image_locations_.value();
1146 }
1147
GetBootClassPath()1148 Result<const std::vector<std::string>*> Artd::GetBootClassPath() {
1149 std::lock_guard<std::mutex> lock(cache_mu_);
1150
1151 if (!cached_boot_class_path_.has_value()) {
1152 const char* env_value = getenv("BOOTCLASSPATH");
1153 if (env_value == nullptr || strlen(env_value) == 0) {
1154 return Errorf("Failed to get environment variable 'BOOTCLASSPATH'");
1155 }
1156 cached_boot_class_path_ = Split(env_value, ":");
1157 }
1158
1159 return &cached_boot_class_path_.value();
1160 }
1161
UseJitZygote()1162 bool Artd::UseJitZygote() {
1163 std::lock_guard<std::mutex> lock(cache_mu_);
1164 return UseJitZygoteLocked();
1165 }
1166
UseJitZygoteLocked()1167 bool Artd::UseJitZygoteLocked() {
1168 if (!cached_use_jit_zygote_.has_value()) {
1169 cached_use_jit_zygote_ =
1170 props_->GetBool("persist.device_config.runtime_native_boot.profilebootclasspath",
1171 "dalvik.vm.profilebootclasspath",
1172 /*default_value=*/false);
1173 }
1174
1175 return cached_use_jit_zygote_.value();
1176 }
1177
GetUserDefinedBootImageLocations()1178 const std::string& Artd::GetUserDefinedBootImageLocations() {
1179 std::lock_guard<std::mutex> lock(cache_mu_);
1180 return GetUserDefinedBootImageLocationsLocked();
1181 }
1182
GetUserDefinedBootImageLocationsLocked()1183 const std::string& Artd::GetUserDefinedBootImageLocationsLocked() {
1184 if (!cached_user_defined_boot_image_locations_.has_value()) {
1185 cached_user_defined_boot_image_locations_ = props_->GetOrEmpty("dalvik.vm.boot-image");
1186 }
1187
1188 return cached_user_defined_boot_image_locations_.value();
1189 }
1190
DenyArtApexDataFiles()1191 bool Artd::DenyArtApexDataFiles() {
1192 std::lock_guard<std::mutex> lock(cache_mu_);
1193 return DenyArtApexDataFilesLocked();
1194 }
1195
DenyArtApexDataFilesLocked()1196 bool Artd::DenyArtApexDataFilesLocked() {
1197 if (!cached_deny_art_apex_data_files_.has_value()) {
1198 cached_deny_art_apex_data_files_ =
1199 !props_->GetBool("odsign.verification.success", /*default_value=*/false);
1200 }
1201
1202 return cached_deny_art_apex_data_files_.value();
1203 }
1204
GetProfman()1205 Result<std::string> Artd::GetProfman() { return BuildArtBinPath("profman"); }
1206
GetArtExec()1207 Result<std::string> Artd::GetArtExec() { return BuildArtBinPath("art_exec"); }
1208
ShouldUseDex2Oat64()1209 bool Artd::ShouldUseDex2Oat64() {
1210 return !props_->GetOrEmpty("ro.product.cpu.abilist64").empty() &&
1211 props_->GetBool("dalvik.vm.dex2oat64.enabled", /*default_value=*/false);
1212 }
1213
GetDex2Oat()1214 Result<std::string> Artd::GetDex2Oat() {
1215 std::string binary_name = ShouldUseDex2Oat64() ? "dex2oat64" : "dex2oat32";
1216 // TODO(b/234351700): Should we use the "d" variant?
1217 return BuildArtBinPath(binary_name);
1218 }
1219
ShouldCreateSwapFileForDexopt()1220 bool Artd::ShouldCreateSwapFileForDexopt() {
1221 // Create a swap file by default. Dex2oat will decide whether to use it or not.
1222 return props_->GetBool("dalvik.vm.dex2oat-swap", /*default_value=*/true);
1223 }
1224
AddBootImageFlags(CmdlineBuilder & args)1225 void Artd::AddBootImageFlags(/*out*/ CmdlineBuilder& args) {
1226 if (UseJitZygote()) {
1227 args.Add("--force-jit-zygote");
1228 } else {
1229 args.AddIfNonEmpty("--boot-image=%s", GetUserDefinedBootImageLocations());
1230 }
1231 }
1232
AddCompilerConfigFlags(const std::string & instruction_set,const std::string & compiler_filter,PriorityClass priority_class,const DexoptOptions & dexopt_options,CmdlineBuilder & args)1233 void Artd::AddCompilerConfigFlags(const std::string& instruction_set,
1234 const std::string& compiler_filter,
1235 PriorityClass priority_class,
1236 const DexoptOptions& dexopt_options,
1237 /*out*/ CmdlineBuilder& args) {
1238 args.Add("--instruction-set=%s", instruction_set);
1239 std::string features_prop = "dalvik.vm.isa.{}.features"_format(instruction_set);
1240 args.AddIfNonEmpty("--instruction-set-features=%s", props_->GetOrEmpty(features_prop));
1241 std::string variant_prop = "dalvik.vm.isa.{}.variant"_format(instruction_set);
1242 args.AddIfNonEmpty("--instruction-set-variant=%s", props_->GetOrEmpty(variant_prop));
1243
1244 args.Add("--compiler-filter=%s", compiler_filter)
1245 .Add("--compilation-reason=%s", dexopt_options.compilationReason);
1246
1247 args.AddIf(priority_class >= PriorityClass::INTERACTIVE, "--compact-dex-level=none");
1248
1249 args.AddIfNonEmpty("--max-image-block-size=%s",
1250 props_->GetOrEmpty("dalvik.vm.dex2oat-max-image-block-size"))
1251 .AddIfNonEmpty("--very-large-app-threshold=%s",
1252 props_->GetOrEmpty("dalvik.vm.dex2oat-very-large"))
1253 .AddIfNonEmpty(
1254 "--resolve-startup-const-strings=%s",
1255 props_->GetOrEmpty("persist.device_config.runtime.dex2oat_resolve_startup_strings",
1256 "dalvik.vm.dex2oat-resolve-startup-strings"));
1257
1258 args.AddIf(dexopt_options.debuggable, "--debuggable")
1259 .AddIf(props_->GetBool("debug.generate-debug-info", /*default_value=*/false),
1260 "--generate-debug-info")
1261 .AddIf(props_->GetBool("dalvik.vm.dex2oat-minidebuginfo", /*default_value=*/false),
1262 "--generate-mini-debug-info");
1263
1264 args.AddRuntimeIf(DenyArtApexDataFiles(), "-Xdeny-art-apex-data-files")
1265 .AddRuntime("-Xtarget-sdk-version:%d", dexopt_options.targetSdkVersion)
1266 .AddRuntimeIf(dexopt_options.hiddenApiPolicyEnabled, "-Xhidden-api-policy:enabled");
1267 }
1268
AddPerfConfigFlags(PriorityClass priority_class,CmdlineBuilder & art_exec_args,CmdlineBuilder & dex2oat_args)1269 void Artd::AddPerfConfigFlags(PriorityClass priority_class,
1270 /*out*/ CmdlineBuilder& art_exec_args,
1271 /*out*/ CmdlineBuilder& dex2oat_args) {
1272 // CPU set and number of threads.
1273 std::string default_cpu_set_prop = "dalvik.vm.dex2oat-cpu-set";
1274 std::string default_threads_prop = "dalvik.vm.dex2oat-threads";
1275 std::string cpu_set;
1276 std::string threads;
1277 if (priority_class >= PriorityClass::BOOT) {
1278 cpu_set = props_->GetOrEmpty("dalvik.vm.boot-dex2oat-cpu-set");
1279 threads = props_->GetOrEmpty("dalvik.vm.boot-dex2oat-threads");
1280 } else if (priority_class >= PriorityClass::INTERACTIVE_FAST) {
1281 cpu_set = props_->GetOrEmpty("dalvik.vm.restore-dex2oat-cpu-set", default_cpu_set_prop);
1282 threads = props_->GetOrEmpty("dalvik.vm.restore-dex2oat-threads", default_threads_prop);
1283 } else if (priority_class <= PriorityClass::BACKGROUND) {
1284 cpu_set = props_->GetOrEmpty("dalvik.vm.background-dex2oat-cpu-set", default_cpu_set_prop);
1285 threads = props_->GetOrEmpty("dalvik.vm.background-dex2oat-threads", default_threads_prop);
1286 } else {
1287 cpu_set = props_->GetOrEmpty(default_cpu_set_prop);
1288 threads = props_->GetOrEmpty(default_threads_prop);
1289 }
1290 dex2oat_args.AddIfNonEmpty("--cpu-set=%s", cpu_set).AddIfNonEmpty("-j%s", threads);
1291
1292 if (priority_class < PriorityClass::BOOT) {
1293 art_exec_args
1294 .Add(priority_class <= PriorityClass::BACKGROUND ? "--set-task-profile=Dex2OatBackground" :
1295 "--set-task-profile=Dex2OatBootComplete")
1296 .Add("--set-priority=background");
1297 }
1298
1299 dex2oat_args.AddRuntimeIfNonEmpty("-Xms%s", props_->GetOrEmpty("dalvik.vm.dex2oat-Xms"))
1300 .AddRuntimeIfNonEmpty("-Xmx%s", props_->GetOrEmpty("dalvik.vm.dex2oat-Xmx"));
1301
1302 // Enable compiling dex files in isolation on low ram devices.
1303 // It takes longer but reduces the memory footprint.
1304 dex2oat_args.AddIf(props_->GetBool("ro.config.low_ram", /*default_value=*/false),
1305 "--compile-individually");
1306 }
1307
ExecAndReturnCode(const std::vector<std::string> & args,int timeout_sec,const ExecCallbacks & callbacks,ProcessStat * stat) const1308 Result<int> Artd::ExecAndReturnCode(const std::vector<std::string>& args,
1309 int timeout_sec,
1310 const ExecCallbacks& callbacks,
1311 ProcessStat* stat) const {
1312 std::string error_msg;
1313 ExecResult result =
1314 exec_utils_->ExecAndReturnResult(args, timeout_sec, callbacks, stat, &error_msg);
1315 if (result.status != ExecResult::kExited) {
1316 return Error() << error_msg;
1317 }
1318 return result.exit_code;
1319 }
1320
Fstat(const File & file) const1321 Result<struct stat> Artd::Fstat(const File& file) const {
1322 struct stat st;
1323 if (fstat_(file.Fd(), &st) != 0) {
1324 return Errorf("Unable to fstat file '{}'", file.GetPath());
1325 }
1326 return st;
1327 }
1328
1329 } // namespace artd
1330 } // namespace art
1331