using ::aidl::com::android::server::art::ArtConstants; using ::aidl::com::android::server::art::ArtdDexoptResult; using ::aidl::com::android::server::art::ArtifactsLocation; using ::aidl::com::android::server::art::ArtifactsPath; using ::aidl::com::android::server::art::CopyAndRewriteProfileResult; using ::aidl::com::android::server::art::DexMetadataPath; using ::aidl::com::android::server::art::DexoptOptions; using ::aidl::com::android::server::art::DexoptTrigger; using ::aidl::com::android::server::art::FileVisibility; using ::aidl::com::android::server::art::FsPermission; using ::aidl::com::android::server::art::GetDexoptNeededResult; using ::aidl::com::android::server::art::GetDexoptStatusResult; using ::aidl::com::android::server::art::IArtdCancellationSignal; using ::aidl::com::android::server::art::IArtdNotification; using ::aidl::com::android::server::art::MergeProfileOptions; using ::aidl::com::android::server::art::OutputArtifacts; using ::aidl::com::android::server::art::OutputProfile; using ::aidl::com::android::server::art::OutputSecureDexMetadataCompanion; using ::aidl::com::android::server::art::PreRebootStagedFilesStatus; using ::aidl::com::android::server::art::PriorityClass; using ::aidl::com::android::server::art::ProfilePath; using ::aidl::com::android::server::art::RuntimeArtifactsPath; using ::aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Basename; using ::android::base::Dirname; using ::android::base::ErrnoError; using ::android::base::Error; using ::android::base::Join; using ::android::base::make_scope_guard; using ::android::base::ParseInt; using ::android::base::ParseUint; using ::android::base::ReadFileToString; using ::android::base::Result; using ::android::base::Split; using ::android::base::Tokenize; using ::android::base::Trim; using ::android::base::unique_fd; using ::android::base::WriteStringToFd; using ::android::base::WriteStringToFile; using ::android::fs_mgr::FstabEntry; using ::art::service::FlattenAndValidateClassLoaderContext; using ::art::service::ValidateDexPath; using ::art::tools::CmdlineBuilder; using ::art::tools::Fatal; using ::art::tools::GetProcMountsAncestorsOfPath; using ::art::tools::NonFatal; using ::ndk::ScopedAStatus; using ::ndk::ScopedFileDescriptor;
using DexoptComparator = DexoptTrigger::DexoptComparator; using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath; using TmpProfilePath = ProfilePath::TmpProfilePath; using WritableProfilePath = ProfilePath::WritableProfilePath;
// Timeout for short operations, such as merging profiles.
constexpr int kShortTimeoutSec = 60; // 1 minute.
// Timeout for long operations, such as compilation. We set it to be smaller than the Package // Manager watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that if the operation // is called from the Package Manager's thread handler, it will be aborted before that watchdog // would take down the system server.
constexpr int kLongTimeoutSec = 570; // 9.5 minutes.
std::optional<int64_t> GetSize(std::string_view path) {
std::error_code ec;
int64_t size = std::filesystem::file_size(path, ec); if (ec) { // It is okay if the file does not exist. We don't have to log it. if (ec.value() != ENOENT) {
LOG(ERROR) << ART_FORMAT("Failed to get the file size of '{}': {}", path, ec.message());
} return std::nullopt;
} return size;
}
// Deletes a file. Returns the size of the deleted file, or 0 if the deleted file is empty or an // error occurs.
int64_t GetSizeAndDeleteFile(const std::string& path) {
std::optional<int64_t> size = GetSize(path); if (!size.has_value()) { return0;
} if (!DeleteFile(path)) { return0;
} return size.value();
}
Result<OatFileAssistant::DexoptTrigger> DexoptTriggerFromAidl(const DexoptTrigger& aidl_value) { if (aidl_value.dexoptComparators.empty()) { return Errorf("No dexopt comparators provided");
}
constexpr std::array kPrimaryComparators = {
DexoptComparator::COMPARING_COMPILER_FILTER,
DexoptComparator::COMPARING_COMPILER_FILTER_REVERSED,
DexoptComparator::CUSTOM_TARGET_IS_BETTER_THAN_CURRENT,
DexoptComparator::CUSTOM_TARGET_IS_WORSE_THAN_CURRENT}; if (std::ranges::find(kPrimaryComparators, aidl_value.dexoptComparators[0]) ==
kPrimaryComparators.end()) { return Errorf("The first comparator must be a primary comparator");
}
std::vector<OatFileAssistant::DexoptComparator> comparators; for (const DexoptComparator& aidl_comparator : aidl_value.dexoptComparators) { switch (aidl_comparator) { case DexoptComparator::COMPARING_COMPILER_FILTER:
comparators.push_back(OatFileAssistant::DexoptComparator::kComparingCompilerFilter); continue; case DexoptComparator::COMPARING_COMPILER_FILTER_REVERSED:
comparators.push_back(OatFileAssistant::DexoptComparator::kComparingCompilerFilterReversed); continue; case DexoptComparator::COMPARING_PRIMARY_BOOT_IMAGE_STATUS:
comparators.push_back(OatFileAssistant::DexoptComparator::kComparingPrimaryBootImageStatus); continue; case DexoptComparator::COMPARING_EXTRACTION_STATUS:
comparators.push_back(OatFileAssistant::DexoptComparator::kComparingExtractionStatus); continue; case DexoptComparator::CUSTOM_TARGET_IS_BETTER_THAN_CURRENT: if (!aidl_value.customComparatorReason.has_value()) { return Errorf("No custom comparator reason provided");
}
comparators.push_back(OatFileAssistant::DexoptComparator::kCustomTargetIsBetterThanCurrent); continue; case DexoptComparator::CUSTOM_TARGET_IS_WORSE_THAN_CURRENT: if (!aidl_value.customComparatorReason.has_value()) { return Errorf("No custom comparator reason provided");
}
comparators.push_back(OatFileAssistant::DexoptComparator::kCustomTargetIsWorseThanCurrent); continue; // No default. All cases should be explicitly handled, or the compilation will fail.
} // This should never happen. Just in case we get a non-enumerator value.
LOG(FATAL) << "Unexpected comparator " << static_cast<int>(aidl_comparator);
} return OatFileAssistant::DexoptTrigger{std::move(comparators), aidl_value.customComparatorReason};
}
ArtifactsLocation ArtifactsLocationToAidl(OatFileAssistant::Location location) { switch (location) { case OatFileAssistant::Location::kLocationNoneOrError: return ArtifactsLocation::NONE_OR_ERROR; case OatFileAssistant::Location::kLocationOat: return ArtifactsLocation::DALVIK_CACHE; case OatFileAssistant::Location::kLocationOdex: return ArtifactsLocation::NEXT_TO_DEX; case OatFileAssistant::Location::kLocationDm: return ArtifactsLocation::DM; case OatFileAssistant::Location::kLocationSdmOat: return ArtifactsLocation::SDM_DALVIK_CACHE; case OatFileAssistant::Location::kLocationSdmOdex: return ArtifactsLocation::SDM_NEXT_TO_DEX; // No default. All cases should be explicitly handled, or the compilation will fail.
} // This should never happen. Just in case we get a non-enumerator value.
LOG(FATAL) << "Unexpected Location " << location;
}
Result<bool> CreateDir(const std::string& path) {
std::error_code ec; bool created = std::filesystem::create_directory(path, ec); if (ec) { return Errorf("Failed to create directory '{}': {}", path, ec.message());
} return created;
}
Result<FileVisibility> GetFileVisibility(const std::string& file) {
std::error_code ec;
std::filesystem::file_status status = std::filesystem::status(file, ec); if (!std::filesystem::status_known(status)) { return Errorf("Failed to get status of '{}': {}", file, ec.message());
} if (!std::filesystem::exists(status)) { return FileVisibility::NOT_FOUND;
}
Result<ArtdCancellationSignal*> ToArtdCancellationSignal(IArtdCancellationSignal* input) { if (input == nullptr) { return Error() << "Cancellation signal must not be nullptr";
} // We cannot use `dynamic_cast` because ART code is compiled with `-fno-rtti`, so we have to check // the magic number.
int64_t type; if (!input->getType(&type).isOk() ||
type != reinterpret_cast<intptr_t>(kArtdCancellationSignalType)) { // The cancellation signal must be created by `Artd::createCancellationSignal`. return Error() << "Invalid cancellation signal type";
} returnstatic_cast<ArtdCancellationSignal*>(input);
}
Result<void> CopyFile(const std::string& src_path, const NewFile& dst_file) {
std::string content; if (!ReadFileToString(src_path, &content)) { return Errorf("Failed to read file '{}': {}", src_path, strerror(errno));
} if (!WriteStringToFd(content, dst_file.Fd())) { return Errorf("Failed to write file '{}': {}", dst_file.TempPath(), strerror(errno));
} if (fsync(dst_file.Fd()) != 0) { return Errorf("Failed to flush file '{}': {}", dst_file.TempPath(), strerror(errno));
} if (lseek(dst_file.Fd(), /*offset=*/0, SEEK_SET) != 0) { return Errorf( "Failed to reset the offset for file '{}': {}", dst_file.TempPath(), strerror(errno));
} return {};
}
if (result == ProfmanResult::kCopyAndUpdateNoMatch) { return bad_profile( "The profile does not match the APK (The checksums in the profile do not match the " "checksums of the .dex files in the APK)");
} return bad_profile("The profile is in the wrong format or an I/O error has occurred");
}
// Returns the fd on success, or an invalid fd if the dex file contains no profile, or error if any // error occurs.
Result<File> ExtractEmbeddedProfileToFd(const std::string& dex_path) {
std::unique_ptr<File> dex_file = OR_RETURN(OpenFileForReading(dex_path));
std::string error_msg;
uint32_t magic; if (!ReadMagicAndReset(dex_file->Fd(), &magic, &error_msg)) { return Error() << error_msg;
} if (!IsZipMagic(magic)) { if (DexFileLoader::IsMagicValid(magic)) { // The dex file can be a plain dex file. This is expected. return File();
} return Error() << "File is neither a zip file nor a plain dex file";
}
std::unique_ptr<ZipArchive> zip_archive(
ZipArchive::OpenFromOwnedFd(dex_file->Fd(), dex_path.c_str(), &error_msg)); if (zip_archive == nullptr) { return Error() << error_msg;
}
constexpr constchar* kEmbeddedProfileEntry = "assets/art-profile/baseline.prof";
std::unique_ptr<ZipEntry> zip_entry(zip_archive->FindOrNull(kEmbeddedProfileEntry, &error_msg));
size_t size; if (zip_entry == nullptr || (size = zip_entry->GetUncompressedLength()) == 0) { if (!error_msg.empty()) {
LOG(WARNING) << error_msg;
} // The dex file doesn't necessarily contain a profile. This is expected. return File();
}
// The name is for debugging only.
std::string memfd_name =
ART_FORMAT("{} extracted in memory from {}", kEmbeddedProfileEntry, dex_path);
File memfd(memfd_create(memfd_name.c_str(), /*flags=*/0),
memfd_name, /*check_usage=*/false); if (!memfd.IsValid()) { return ErrnoError() << "Failed to create memfd";
} if (ftruncate(memfd.Fd(), size) != 0) { return ErrnoError() << "Failed to ftruncate memfd";
} // Map with MAP_SHARED because we're feeding the fd to profman.
MemMap mem_map = MemMap::MapFile(size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
memfd.Fd(), /*start=*/0, /*low_4gb=*/false,
memfd_name.c_str(),
&error_msg); if (!mem_map.IsValid()) { return Errorf("Failed to mmap memfd: {}", error_msg);
} if (!zip_entry->ExtractToMemory(mem_map.Begin(), &error_msg)) { return Errorf("Failed to extract '{}': {}", kEmbeddedProfileEntry, error_msg);
}
// Reopen the memfd with readonly to make SELinux happy when the fd is passed to a child process // who doesn't have write permission. (b/303909581)
std::string path = ART_FORMAT("/proc/self/fd/{}", memfd.Fd()); // NOLINTNEXTLINE - O_CLOEXEC is omitted on purpose
File memfd_readonly(open(path.c_str(), O_RDONLY),
memfd_name, /*check_usage=*/false, /*read_only_mode=*/true); if (!memfd_readonly.IsOpened()) { return ErrnoErrorf("Failed to open file '{}' ('{}')", path, memfd_name);
}
// A helper class for handling the Pre-reboot staged metadata file. // // An older version of ART may access this file, but it doesn't need to understand its content. // Specifically, the file can be created by a new version of ART during Pre-reboot Dexopt and // checked by an old version of ART during background dexopt before the reboot. In this case, // background dexopt only needs to know the file creation time. // // The full file content only needs to be understood by the same version of ART. // // File format: // <magic> // <build_fingerprint> // <apex_timestamps> class PreRebootStagedMetadata { public: static Result<PreRebootStagedFilesStatus> Check(ArtdInjector* injector, const std::string& path,
std::string_view expected_build_fingerprint,
std::string_view expected_apex_timestamps) {
std::unique_ptr<File> staged_metadata_file = OR_RETURN(OpenFileForReading(path));
struct stat st = OR_RETURN(injector->Fstat(*staged_metadata_file));
PreRebootStagedFilesStatus ret = {
.isCommittable = true,
.createdAtMillis = static_cast<int64_t>(NsToMs(TimeSpecToNs(st.st_mtim)))};
std::string content; if (!ReadFileToString(path, &content)) { return ErrnoErrorf("Failed to read '{}'", path);
}
#define RETURN_FATAL_IF_PRE_REBOOT(options) \ if ((options).is_pre_reboot) { \ return Fatal("This method is not supported in Pre-reboot Dexopt mode"); \
}
#define RETURN_FATAL_IF_NOT_PRE_REBOOT(options) \ if (!(options).is_pre_reboot) { \ return Fatal("This method is only supported in Pre-reboot Dexopt mode"); \
}
#define RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL(expected, arg, log_name) \
{ \ auto&& __return_fatal_tmp = PreRebootFlag(arg); \ if ((expected) != __return_fatal_tmp) { \ return Fatal(ART_FORMAT("Expected flag 'isPreReboot' in argument '{}' to be {}, got {}", \
log_name, \
expected, \
__return_fatal_tmp)); \
} \
}
// We ignore odex_status because it is not meaningful. It can only be either "up-to-date", // "apk-more-recent", or "io-error-no-oat", which means it doesn't give us information in addition // to what we can learn from compiler_filter because compiler_filter will be the actual compiler // filter, "run-from-apk-fallback", and "run-from-apk" in those three cases respectively.
DCHECK(ignored_odex_status == "up-to-date" || ignored_odex_status == "apk-more-recent"||
ignored_odex_status == "io-error-no-oat");
Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec); if (!result.ok()) { return NonFatal("Failed to run profman: " + result.error().message());
}
LOG(INFO) << ART_FORMAT("profman returned code {}", result.value());
if (result.value() != ProfmanResult::kSkipCompilationSmallDelta &&
result.value() != ProfmanResult::kSkipCompilationEmptyProfiles) { return NonFatal(ART_FORMAT("profman returned an unexpected code: {}", result.value()));
}
Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec); if (!result.ok()) { return NonFatal("Failed to run profman: " + result.error().message());
}
LOG(INFO) << ART_FORMAT("profman returned code {}", result.value());
if (result.value() != ProfmanResult::kCopyAndUpdateSuccess) { return NonFatal(ART_FORMAT("profman returned an unexpected code: {}", result.value()));
}
std::error_code ec;
std::filesystem::rename(tmp_profile_path, ref_profile_path, ec); if (ec) { return NonFatal(ART_FORMAT( "Failed to move '{}' to '{}': {}", tmp_profile_path, ref_profile_path, ec.message()));
}
return ScopedAStatus::ok();
}
ndk::ScopedAStatus Artd::deleteProfile(const ProfilePath& in_profile) { // `in_profile` can be either a Pre-reboot path or an ordinary one.
std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
DeleteFile(profile_path);
ndk::ScopedAStatus Artd::getOdexVisibility(const ArtifactsPath& in_artifactsPath,
FileVisibility* _aidl_return) { // `in_artifactsPath` can be either a Pre-reboot path or an ordinary one.
std::string oat_path = OR_RETURN_FATAL(BuildArtifactsPath(in_artifactsPath)).oat_path;
*_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(oat_path)); return ScopedAStatus::ok();
}
ndk::ScopedAStatus Artd::getVdexVisibility(const ArtifactsPath& in_artifactsPath,
FileVisibility* _aidl_return) { // `in_artifactsPath` can be either a Pre-reboot path or an ordinary one.
std::string vdex_path = OR_RETURN_FATAL(BuildArtifactsPath(in_artifactsPath)).vdex_path;
*_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(vdex_path)); return ScopedAStatus::ok();
}
if (in_referenceProfile.has_value()) { if (in_options.dumpOnly || in_options.dumpClassesAndMethods) { return Fatal( "Reference profile must not be set when 'dumpOnly' or 'dumpClassesAndMethods' is set");
} // `in_referenceProfile` can be either a Pre-reboot profile or an ordinary one.
std::string reference_profile_path =
OR_RETURN_FATAL(BuildProfileOrDmPath(*in_referenceProfile)); if (in_referenceProfile->getTag() == ProfilePath::dexMetadataPath) { return Fatal(ART_FORMAT("Does not support DM file, got '{}'", reference_profile_path));
}
OR_RETURN_NON_FATAL(CopyFile(reference_profile_path, *output_profile_file));
}
if (in_options.dumpOnly || in_options.dumpClassesAndMethods) {
args.Add("--dump-output-to-fd=%d", output_profile_file->Fd());
} else { // profman is ok with this being an empty file when in_referenceProfile isn't set.
args.Add("--reference-profile-file-fd=%d", output_profile_file->Fd());
}
fd_logger.Add(*output_profile_file);
Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec); if (!result.ok()) { return NonFatal("Failed to run profman: " + result.error().message());
}
LOG(INFO) << ART_FORMAT("profman returned code {}", result.value());
Result<std::unique_ptr<File>> sdm_file = OpenFileForReading(sdm_path); if (!sdm_file.ok()) { if (sdm_file.error().code() == ENOENT) { // No SDM file found. That's typical. return ScopedAStatus::ok();
} return NonFatal(sdm_file.error().message());
} struct stat sdm_st = OR_RETURN_NON_FATAL(injector_->Fstat(*sdm_file.value()));
std::string error_msg;
std::unique_ptr<SdcReader> sdc_reader = SdcReader::Load(sdc_path, &error_msg); if (sdc_reader != nullptr && sdc_reader->GetSdmTimestampNs() == TimeSpecToNs(sdm_st.st_mtim)) { // Already has an SDC file for the SDM file. return ScopedAStatus::ok();
}
std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache. if (!in_outputSdc.sdcPath.isInDalvikCache) {
OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputSdc.sdcPath.dexPath,
in_outputSdc.sdcPath.isa,
in_outputSdc.permissionSettings.dirFsPermission,
&oat_dir_path));
// Unlike the two `Restorecon` calls in `dexopt`, we only need one restorecon here because SDM // files are for primary dex files, whose oat directory doesn't have an MLS label.
OR_RETURN_NON_FATAL(
injector_->Restorecon(oat_dir_path, /*se_context=*/std::nullopt, /*recurse=*/true));
}
RawArtifactsPath artifacts_path =
OR_RETURN_FATAL(BuildArtifactsPath(in_outputArtifacts.artifactsPath));
OR_RETURN_FATAL(ValidateDexPath(in_dexFile)); // `in_profile` can be either a Pre-reboot profile or an ordinary one.
std::optional<std::string> profile_path =
in_profile.has_value()
? std::make_optional(OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile.value())))
: std::nullopt;
ArtdCancellationSignal* cancellation_signal =
OR_RETURN_FATAL(ToArtdCancellationSignal(in_cancellationSignal.get()));
std::unique_ptr<ClassLoaderContext> context = nullptr; if (in_classLoaderContext.has_value()) {
context = ClassLoaderContext::Create(in_classLoaderContext.value()); if (context == nullptr) { return Fatal(
ART_FORMAT("Class loader context '{}' is invalid", in_classLoaderContext.value()));
}
}
std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache. if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts.artifactsPath.dexPath,
in_outputArtifacts.artifactsPath.isa,
in_outputArtifacts.permissionSettings.dirFsPermission,
&oat_dir_path));
// First-round restorecon. artd doesn't have the permission to create files with the // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will // inherit `dalvikcache_data_file` rather than `apk_data_file`.
OR_RETURN_NON_FATAL(injector_->Restorecon(
oat_dir_path, in_outputArtifacts.permissionSettings.seContext, /*recurse=*/true));
}
std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
args.Add("--zip-fd=%d", dex_file->Fd()).Add("--zip-location=%s", in_dexFile);
fd_logger.Add(*dex_file); // Check if the dex file is other-readable compared to the given fs_permission. struct stat dex_st = OR_RETURN_NON_FATAL(injector_->Fstat(*dex_file)); for (constauto& fs_permission : {odex_fs_permission, vdex_fs_permission}) { if ((dex_st.st_mode & S_IROTH) == 0) { if (fs_permission.isOtherReadable) { return NonFatal(ART_FORMAT( "Outputs cannot be other-readable because the dex file '{}' is not other-readable",
dex_file->GetPath()));
} // Negative numbers mean no `chown`. 0 means root. // Note: this check is more strict than it needs to be. For example, it doesn't allow the // outputs to belong to a group that is a subset of the dex file's group. This is for // simplicity, and it's okay as we don't have to handle such complicated cases in practice. if ((fs_permission.uid > 0 && static_cast<uid_t>(fs_permission.uid) != dex_st.st_uid) ||
(fs_permission.gid > 0 && static_cast<gid_t>(fs_permission.gid) != dex_st.st_uid && static_cast<gid_t>(fs_permission.gid) != dex_st.st_gid)) { return NonFatal(ART_FORMAT( "Outputs' owner doesn't match the dex file '{}' (outputs: {}:{}, dex file: {}:{})",
dex_file->GetPath(),
fs_permission.uid,
fs_permission.gid,
dex_st.st_uid,
dex_st.st_gid));
}
}
}
std::unique_ptr<File> profile_file = nullptr; if (profile_path.has_value()) {
profile_file = OR_RETURN_NON_FATAL(OpenFileForReading(profile_path.value()));
args.Add("--profile-file-fd=%d", profile_file->Fd());
fd_logger.Add(*profile_file); struct stat profile_st = OR_RETURN_NON_FATAL(injector_->Fstat(*profile_file)); if (odex_fs_permission.isOtherReadable && (profile_st.st_mode & S_IROTH) == 0) { return NonFatal(ART_FORMAT( "Odex file cannot be other-readable because the profile '{}' is not other-readable",
profile_file->GetPath()));
} // TODO(b/260228411): Check uid and gid.
}
// Second-round restorecon. Restorecon recursively after the output files are created, so that the // SELinux context is applied to all of them. The SELinux context of a file is mostly inherited // from the parent directory upon creation, but the MLS label is not inherited, so we need to // restorecon every file so that they have the right MLS label. If the files are in dalvik-cache, // there's no need to restorecon because they inherits the SELinux context of the dalvik-cache // directory and they don't need to have MLS labels. if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
OR_RETURN_NON_FATAL(injector_->Restorecon(
oat_dir_path, in_outputArtifacts.permissionSettings.seContext, /*recurse=*/true));
}
ExecCallbacks ArtdCancellationSignal::CreateExecCallbacks() { return {
.on_start =
[&](pid_t pid) {
std::lock_guard<std::mutex> lock(mu_);
pids_.insert(pid); // Handle cancellation signals sent before the process starts. if (is_cancelled_) { // Kill the whole process and then kill process group since there is no knowledge if // there yet exist one forked process or already created process group. int res = injector_->Kill(pid, SIGKILL);
DCHECK_EQ(res, 0);
injector_->Kill(-pid, SIGKILL);
}
},
.on_end =
[&](pid_t pid) {
std::lock_guard<std::mutex> lock(mu_); // The pid should no longer receive kill signals sent by `cancellation_signal`.
pids_.erase(pid);
},
};
}
ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) { // The artifacts should be in the global dalvik-cache directory if: // (1). the dex file is on a system partition, even if the partition is remounted read-write, // or // (2). the dex file is in any other readonly location. (At the time of writing, this only // include Incremental FS.) // // We cannot rely on access(2) because: // - It doesn't take effective capabilities into account, from which artd gets root access // to the filesystem. // - The `faccessat` variant with the `AT_EACCESS` flag, which takes effective capabilities // into account, is not supported by bionic.
OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
std::vector<FstabEntry> entries = OR_RETURN_NON_FATAL(GetProcMountsAncestorsOfPath(in_dexFile)); // The last one controls because `/proc/mounts` reflects the sequence of `mount`. for (auto it = entries.rbegin(); it != entries.rend(); it++) { if (it->fs_type == "overlay") { // Ignore the overlays created by `remount`. continue;
} // We need to special-case Incremental FS since it is tagged as read-write while it's actually // not.
*_aidl_return = (it->flags & MS_RDONLY) != 0 || it->fs_type == "incremental-fs"; return ScopedAStatus::ok();
}
return NonFatal(ART_FORMAT("Fstab entries not found for '{}'", in_dexFile));
}
unique_fd inotify_fd(inotify_init1(IN_NONBLOCK | IN_CLOEXEC)); if (inotify_fd < 0) { return NonFatal(ART_FORMAT("Failed to inotify_init1: {}", strerror(errno)));
}
// Watch the dir rather than the file itself because profiles are moved in rather than updated in // place.
std::string dir = Dirname(path); int wd = inotify_add_watch(inotify_fd, dir.c_str(), IN_MOVED_TO); if (wd < 0) { return NonFatal(ART_FORMAT("Failed to inotify_add_watch '{}': {}", dir, strerror(errno)));
}
unique_fd pidfd = PidfdOpen(in_pid, /*flags=*/0); if (pidfd < 0) { if (errno == ESRCH) { // The process has gone now.
LOG(INFO) << ART_FORMAT("Process exited without sending notification '{}'", path);
*_aidl_return = ndk::SharedRefBase::make<ArtdNotification>(); return ScopedAStatus::ok();
} return NonFatal(ART_FORMAT("Failed to pidfd_open {}: {}", in_pid, strerror(errno)));
}
if (!mu_.try_lock()) { return Fatal("`wait` can be called only once");
}
std::lock_guard<std::mutex> lock(mu_, std::adopt_lock);
LOG(INFO) << ART_FORMAT("Waiting for notification '{}'", path_);
if (is_called_) { return Fatal("`wait` can be called only once");
}
is_called_ = true;
if (done_) {
*_aidl_return = true; return ScopedAStatus::ok();
}
auto src_artifacts = std::make_unique<RawArtifactsPath>(
OR_RETURN_FATAL(BuildArtifactsPath(pre_reboot_artifacts))); auto dst_artifacts =
std::make_unique<RawArtifactsPath>(OR_RETURN_FATAL(BuildArtifactsPath(artifacts)));
if (OS::FileExists(src_artifacts->oat_path.c_str())) {
files_to_move.emplace_back(src_artifacts->oat_path, dst_artifacts->oat_path);
files_to_move.emplace_back(src_artifacts->vdex_path, dst_artifacts->vdex_path); if (OS::FileExists(src_artifacts->art_path.c_str())) {
files_to_move.emplace_back(src_artifacts->art_path, dst_artifacts->art_path);
} else {
files_to_remove.push_back(dst_artifacts->art_path);
}
}
}
for (const WritableProfilePath& profile : in_profiles) {
RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(profile, "profiles");
auto src_profile = std::make_unique<std::string>(
OR_RETURN_FATAL(BuildWritableProfilePath(pre_reboot_profile))); auto dst_profile =
std::make_unique<std::string>(OR_RETURN_FATAL(BuildWritableProfilePath(profile)));
if (OS::FileExists(src_profile->c_str())) {
files_to_move.emplace_back(*src_profile, *dst_profile);
}
}
ScopedAStatus Artd::checkPreRebootSystemRequirements(const std::string& in_chrootDir, bool* _aidl_return) {
RETURN_FATAL_IF_PRE_REBOOT(options_);
BuildSystemProperties new_props =
OR_RETURN_NON_FATAL(BuildSystemProperties::Create(in_chrootDir + "/system/build.prop"));
std::string old_release_str = props_->GetOrEmpty("ro.build.version.release"); int old_release; if (!ParseInt(old_release_str, &old_release)) { return NonFatal(
ART_FORMAT("Failed to read or parse old release number, got '{}'", old_release_str));
}
std::string new_release_str = new_props.GetOrEmpty("ro.build.version.release"); int new_release; if (!ParseInt(new_release_str, &new_release)) { return NonFatal(
ART_FORMAT("Failed to read or parse new release number, got '{}'", new_release_str));
} if (new_release - old_release >= 2) { // When the release version difference is large, there is no particular technical reason why we // can't run Pre-reboot Dexopt, but we cannot test and support those cases.
LOG(WARNING) << ART_FORMAT( "Pre-reboot Dexopt not supported due to large difference in release versions (old_release: " "{}, new_release: {})",
old_release,
new_release);
*_aidl_return = false; return ScopedAStatus::ok();
}
Result<BootClasspathFds> Artd::OpenBootClasspathFds(const std::vector<std::string>& bcp_jars) {
BootClasspathFds result; for (const std::string& jar : bcp_jars) { // Special treatment for Compilation OS. When we pass in files to CompOS we also need to pass // in the verity digest for those files. Verity digests are only cheaply provided if the file // resides in a partition that supports fs-verity or has an accompanying `fsv_meta` file. // Since APEXes do not have fs-verity enabled nor are fsv_meta files provided the verity digest // would need to computed at runtime, which is slow. // Since all the APEXes that contain BCP jars are mounted within the VM's file system we set // the fd to -1 to indicate that the compiler should search for the BCP jars and open up the // files itself. if (jar.starts_with("/apex/")) {
result.fds.push_back(-1);
} else {
std::unique_ptr<File> jar_file = OR_RETURN(OpenFileForReading(jar.c_str()));
result.fds.push_back(jar_file->Fd());
result.files.push_back(std::move(jar_file));
}
} return result;
}
bool Artd::ShouldCreateSwapFileForDexopt() { // Create a swap file by default. Dex2oat will decide whether to use it or not. return props_->GetBool("dalvik.vm.dex2oat-swap", /*default_value=*/true);
}
// Enable compiling dex files in isolation on low ram devices. // It takes longer but reduces the memory footprint.
dex2oat_args.AddIf(props_->GetBool("ro.config.low_ram", /*default_value=*/false), "--compile-individually");
for (const std::string& flag :
Tokenize(props_->GetOrEmpty("dalvik.vm.dex2oat-flags"), /*delimiters=*/" ")) {
dex2oat_args.AddIfNonEmpty("%s", flag);
}
}
Result<int> Artd::ExecAndReturnCode(const std::vector<std::string>& args, int timeout_sec, const ExecCallbacks& callbacks,
ProcessStat* stat) const {
std::string error_msg; // Create a new process group so that we can kill the process subtree at once by killing the // process group.
ExecResult result = exec_utils_->ExecAndReturnResult(
args, timeout_sec, callbacks, /*new_process_group=*/true, stat, &error_msg); if (result.status != ExecResult::kExited) { return Error() << error_msg;
} return result.exit_code;
}
if (!preparation_done) {
std::error_code ec; bool is_empty = std::filesystem::is_empty(tmp_dir, ec); if (ec) { return NonFatal(ART_FORMAT("Failed to check dir '{}': {}", tmp_dir, ec.message()));
} if (!is_empty) { return Fatal( "preRebootInit must not be concurrently called or retried after cancellation or failure");
}
}
OR_RETURN_NON_FATAL(PreRebootInitClearEnvs());
OR_RETURN_NON_FATAL(PreRebootInitSetEnvFromFile(injector_->GetInitEnvironRcPath())); if (pre_reboot_build_props_ == nullptr) {
pre_reboot_build_props_ = OR_RETURN_NON_FATAL(injector_->GetPreRebootBuildSystemProperties());
} if (!preparation_done) {
OR_RETURN_NON_FATAL(PreRebootInitDeriveClasspath(classpath_file));
}
OR_RETURN_NON_FATAL(PreRebootInitSetEnvFromFile(classpath_file)); if (!preparation_done) {
OR_RETURN_NON_FATAL(BindMountNewDir(art_apex_data_dir, GetArtApexData()));
OR_RETURN_NON_FATAL(BindMountNewDir(odrefresh_dir, "/data/misc/odrefresh"));
ArtdCancellationSignal* cancellation_signal =
OR_RETURN_FATAL(ToArtdCancellationSignal(in_cancellationSignal.get())); if (!OR_RETURN_NON_FATAL(PreRebootInitBootImages(cancellation_signal))) {
*_aidl_return = false; return ScopedAStatus::ok();
}
}
Result<int> result = ExecAndReturnCode(args.Get(), kShortTimeoutSec); if (!result.ok()) { return Errorf("Failed to run derive_classpath: {}", result.error().message());
}
LOG(INFO) << ART_FORMAT("derive_classpath returned code {}", result.value());
if (result.value() != 0) { return Errorf("derive_classpath returned an unexpected code: {}", result.value());
}
if (output->FlushClose() != 0) { return ErrnoErrorf("Failed to flush and close '{}'", path);
}
// A clean view of the new system partition prepared by dexopt_chroot_setup, disregarding the // /system/etc overrides, to make sure odrefresh gets the new boot image profile and other files // in /system/etc.
unique_fd new_system_dir; if (OS::DirectoryExists("/mnt/new_system")) { // odrefresh doesn't have the SELinux permission to find files under `/mnt` in chroot, so we // open the directory and pass it as a dirfd to odrefresh.
new_system_dir = OR_RETURN_WITH_CONTEXT(OpenDirectory("/mnt/new_system"), "Failed to open the clean system view");
args.Add("--env=ANDROID_ROOT=/proc/self/fd/%d", new_system_dir.get())
.Add("--keep-fds=%d", new_system_dir.get());
}
Result<int> result =
ExecAndReturnCode(args.Get(), kLongTimeoutSec, cancellation_signal->CreateExecCallbacks()); if (!result.ok()) { if (cancellation_signal->IsCancelled()) { returnfalse;
} return Errorf("Failed to run odrefresh: {}", result.error().message());
}
LOG(INFO) << ART_FORMAT("odrefresh returned code {}", result.value());
if (result.value() != odrefresh::ExitCode::kCompilationSuccess &&
result.value() != odrefresh::ExitCode::kOkay) { return Errorf("odrefresh returned an unexpected code: {}", result.value());
}
std::string BuildSystemProperties::GetProperty(const std::string& key) const { auto it = system_properties_.find(key); return it != system_properties_.end() ? it->second : "";
}
} // namespace artd
} // namespace art
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.32Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.