From 75d4422b08058ba92dfdeebc1bbe1f89d591b721 Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Mon, 8 Apr 2024 16:51:57 +0100 Subject: Implement Pre-reboot Dexopt. Pre-reboot Dexopt reuses the normal dexopt workflow. The entry point is `ArtManagerLocal.dexoptPackages`. The only differences are: - It appends the ".staged" suffix to the filenames of the output dexopt artifacts and profiles. - It does not clear cur profiles. - It does not delete runtime artifacts. - It never updates the dex use protobuf file. Bug: 311377497 Test: atest ArtServiceTests Test: m test-art-host-gtest-art_artd_tests Change-Id: I7ab98015a85c0a24cc2083a317048dcf548449ea --- artd/README.md | 9 + artd/artd.cc | 103 +++++++--- artd/artd_test.cc | 228 ++++++++++++++++++--- .../com/android/server/art/ArtifactsPath.aidl | 5 + artd/binder/com/android/server/art/IArtd.aidl | 16 ++ .../binder/com/android/server/art/ProfilePath.aidl | 12 ++ artd/path_utils.cc | 79 +++++-- artd/path_utils.h | 57 ++++-- artd/path_utils_test.cc | 130 ++++++++---- .../java/com/android/server/art/AidlUtils.java | 63 ++++-- .../com/android/server/art/ArtFileManager.java | 14 +- .../com/android/server/art/ArtManagerLocal.java | 17 +- .../com/android/server/art/DexUseManagerLocal.java | 9 +- .../java/com/android/server/art/Dexopter.java | 34 +-- .../com/android/server/art/PrimaryDexUtils.java | 10 +- .../com/android/server/art/PrimaryDexopter.java | 8 +- .../com/android/server/art/SecondaryDexopter.java | 7 +- .../android/server/art/ArtManagerLocalTest.java | 125 +++++------ .../com/android/server/art/DexUseManagerTest.java | 8 + .../art/PrimaryDexopterParameterizedTest.java | 52 +++-- .../android/server/art/PrimaryDexopterTest.java | 67 ++++-- .../server/art/PrimaryDexopterTestBase.java | 1 + .../android/server/art/SecondaryDexopterTest.java | 20 +- 23 files changed, 803 insertions(+), 271 deletions(-) diff --git a/artd/README.md b/artd/README.md index a2dfc099e9..3a073c7b08 100644 --- a/artd/README.md +++ b/artd/README.md @@ -14,3 +14,12 @@ doesn't include options passed to dex2oat and other processes. - `dalvik.vm.artd-verbose`: Log verbosity of the artd process. The syntax is the same as the runtime's `-verbose` flag. + +### The `--pre-reboot` flag + +artd can be run in Pre-reboot mode through the `--pre-reboot` flag. The +Pre-reboot mode is for generating outputs for Pre-reboot Dexopt. The flag does +not change the actual behavior, but only affects the service name, the log tag, +a few return checks, etc. Note that how artd addresses input files and output +files is solely determined by AIDL arguments and is **not** affected by the +`--pre-reboot` flag. diff --git a/artd/artd.cc b/artd/artd.cc index 04223d2eb4..6154c8207b 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -252,7 +252,8 @@ Result PrepareArtifactsDirs(const OutputArtifacts& output_artifacts, return {}; } - std::filesystem::path oat_path(OR_RETURN(BuildOatPath(output_artifacts.artifactsPath))); + std::filesystem::path oat_path( + OR_RETURN(BuildArtifactsPath(output_artifacts.artifactsPath)).oat_path); std::filesystem::path isa_dir = oat_path.parent_path(); std::filesystem::path oat_dir = isa_dir.parent_path(); DCHECK_EQ(oat_dir.filename(), "oat"); @@ -481,11 +482,33 @@ std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger) { } // namespace +#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)); \ + } \ + } + +#define RETURN_FATAL_IF_PRE_REBOOT_MISMATCH(options, arg, log_name) \ + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL(options.is_pre_reboot, arg, log_name) + +#define RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(arg, log_name) \ + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL(false, arg, log_name) + Result Restorecon( const std::string& path, const std::optional& se_context, @@ -514,12 +537,15 @@ ScopedAStatus Artd::isAlive(bool* _aidl_return) { } ScopedAStatus Artd::deleteArtifacts(const ArtifactsPath& in_artifactsPath, int64_t* _aidl_return) { - std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath)); + RETURN_FATAL_IF_PRE_REBOOT(options_); + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_artifactsPath, "artifactsPath"); + + RawArtifactsPath path = OR_RETURN_FATAL(BuildArtifactsPath(in_artifactsPath)); *_aidl_return = 0; - *_aidl_return += GetSizeAndDeleteFile(oat_path); - *_aidl_return += GetSizeAndDeleteFile(OatPathToVdexPath(oat_path)); - *_aidl_return += GetSizeAndDeleteFile(OatPathToArtPath(oat_path)); + *_aidl_return += GetSizeAndDeleteFile(path.oat_path); + *_aidl_return += GetSizeAndDeleteFile(path.vdex_path); + *_aidl_return += GetSizeAndDeleteFile(path.art_path); return ScopedAStatus::ok(); } @@ -528,6 +554,8 @@ ScopedAStatus Artd::getDexoptStatus(const std::string& in_dexFile, const std::string& in_instructionSet, const std::optional& in_classLoaderContext, GetDexoptStatusResult* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); + Result ofa_context = GetOatFileAssistantContext(); if (!ofa_context.ok()) { return NonFatal("Failed to get runtime options: " + ofa_context.error().message()); @@ -569,6 +597,8 @@ ScopedAStatus Artd::getDexoptStatus(const std::string& in_dexFile, ndk::ScopedAStatus Artd::isProfileUsable(const ProfilePath& in_profile, const std::string& in_dexFile, bool* _aidl_return) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_profile, "profile"); + std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile)); OR_RETURN_FATAL(ValidateDexPath(in_dexFile)); @@ -621,6 +651,7 @@ ndk::ScopedAStatus Artd::CopyAndRewriteProfileImpl(File src, OutputProfile* dst_aidl, const std::string& dex_path, CopyAndRewriteProfileResult* aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT_MISMATCH(options_, *dst_aidl, "dst"); std::string dst_path = OR_RETURN_FATAL(BuildFinalProfilePath(dst_aidl->profilePath)); OR_RETURN_FATAL(ValidateDexPath(dex_path)); @@ -678,6 +709,8 @@ ndk::ScopedAStatus Artd::copyAndRewriteProfile(const ProfilePath& in_src, OutputProfile* in_dst, const std::string& in_dexFile, CopyAndRewriteProfileResult* _aidl_return) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_src, "src"); + std::string src_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_src)); Result> src = OpenFileForReading(src_path); @@ -712,6 +745,7 @@ ndk::ScopedAStatus Artd::copyAndRewriteEmbeddedProfile(OutputProfile* in_dst, } ndk::ScopedAStatus Artd::commitTmpProfile(const TmpProfilePath& in_profile) { + RETURN_FATAL_IF_PRE_REBOOT_MISMATCH(options_, in_profile, "profile"); std::string tmp_profile_path = OR_RETURN_FATAL(BuildTmpProfilePath(in_profile)); std::string ref_profile_path = OR_RETURN_FATAL(BuildFinalProfilePath(in_profile)); @@ -726,6 +760,8 @@ ndk::ScopedAStatus Artd::commitTmpProfile(const TmpProfilePath& in_profile) { } ndk::ScopedAStatus Artd::deleteProfile(const ProfilePath& in_profile) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_profile, "profile"); + std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile)); std::error_code ec; @@ -739,6 +775,7 @@ ndk::ScopedAStatus Artd::deleteProfile(const ProfilePath& in_profile) { ndk::ScopedAStatus Artd::getProfileVisibility(const ProfilePath& in_profile, FileVisibility* _aidl_return) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_profile, "profile"); std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile)); *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(profile_path)); return ScopedAStatus::ok(); @@ -746,7 +783,8 @@ ndk::ScopedAStatus Artd::getProfileVisibility(const ProfilePath& in_profile, ndk::ScopedAStatus Artd::getArtifactsVisibility(const ArtifactsPath& in_artifactsPath, FileVisibility* _aidl_return) { - std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath)); + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_artifactsPath, "artifactsPath"); + std::string oat_path = OR_RETURN_FATAL(BuildArtifactsPath(in_artifactsPath)).oat_path; *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(oat_path)); return ScopedAStatus::ok(); } @@ -773,12 +811,15 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector& in_profil bool* _aidl_return) { std::vector profile_paths; for (const ProfilePath& profile : in_profiles) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(profile, "profiles"); std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(profile)); if (profile.getTag() == ProfilePath::dexMetadataPath) { return Fatal(ART_FORMAT("Does not support DM file, got '{}'", profile_path)); } profile_paths.push_back(std::move(profile_path)); } + + RETURN_FATAL_IF_PRE_REBOOT_MISMATCH(options_, *in_outputProfile, "outputProfile"); std::string output_profile_path = OR_RETURN_FATAL(BuildFinalProfilePath(in_outputProfile->profilePath)); for (const std::string& dex_file : in_dexFiles) { @@ -826,6 +867,7 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector& in_profil 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) { @@ -950,10 +992,11 @@ ndk::ScopedAStatus Artd::dexopt( ArtdDexoptResult* _aidl_return) { _aidl_return->cancelled = false; - std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_outputArtifacts.artifactsPath)); - std::string vdex_path = OatPathToVdexPath(oat_path); - std::string art_path = OatPathToArtPath(oat_path); + RETURN_FATAL_IF_PRE_REBOOT_MISMATCH(options_, in_outputArtifacts, "outputArtifacts"); + 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 profile_path = in_profile.has_value() ? std::make_optional(OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile.value()))) : @@ -1018,12 +1061,13 @@ ndk::ScopedAStatus Artd::dexopt( } } - std::unique_ptr oat_file = OR_RETURN_NON_FATAL(NewFile::Create(oat_path, fs_permission)); - args.Add("--oat-fd=%d", oat_file->Fd()).Add("--oat-location=%s", oat_path); + std::unique_ptr oat_file = + OR_RETURN_NON_FATAL(NewFile::Create(artifacts_path.oat_path, fs_permission)); + args.Add("--oat-fd=%d", oat_file->Fd()).Add("--oat-location=%s", artifacts_path.oat_path); fd_logger.Add(*oat_file); std::unique_ptr vdex_file = - OR_RETURN_NON_FATAL(NewFile::Create(vdex_path, fs_permission)); + OR_RETURN_NON_FATAL(NewFile::Create(artifacts_path.vdex_path, fs_permission)); args.Add("--output-vdex-fd=%d", vdex_file->Fd()); fd_logger.Add(*vdex_file); @@ -1032,18 +1076,18 @@ ndk::ScopedAStatus Artd::dexopt( std::unique_ptr art_file = nullptr; if (in_dexoptOptions.generateAppImage) { - art_file = OR_RETURN_NON_FATAL(NewFile::Create(art_path, fs_permission)); + art_file = OR_RETURN_NON_FATAL(NewFile::Create(artifacts_path.art_path, fs_permission)); args.Add("--app-image-fd=%d", art_file->Fd()); args.AddIfNonEmpty("--image-format=%s", props_->GetOrEmpty("dalvik.vm.appimageformat")); fd_logger.Add(*art_file); files_to_commit.push_back(art_file.get()); } else { - files_to_delete.push_back(art_path); + files_to_delete.push_back(artifacts_path.art_path); } std::unique_ptr swap_file = nullptr; if (ShouldCreateSwapFileForDexopt()) { - std::string swap_file_path = ART_FORMAT("{}.swap", oat_path); + std::string swap_file_path = ART_FORMAT("{}.swap", artifacts_path.oat_path); swap_file = OR_RETURN_NON_FATAL(NewFile::Create(swap_file_path, FsPermission{.uid = -1, .gid = -1})); args.Add("--swap-fd=%d", swap_file->Fd()); @@ -1070,6 +1114,7 @@ ndk::ScopedAStatus Artd::dexopt( std::unique_ptr input_vdex_file = nullptr; if (in_inputVdex.has_value()) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_inputVdex.value(), "inputVdex"); std::string input_vdex_path = OR_RETURN_FATAL(BuildVdexPath(in_inputVdex.value())); input_vdex_file = OR_RETURN_NON_FATAL(OpenFileForReading(input_vdex_path)); args.Add("--input-vdex-fd=%d", input_vdex_file->Fd()); @@ -1204,17 +1249,21 @@ ScopedAStatus Artd::cleanup(const std::vector& in_profilesToKeep, const std::vector& in_vdexFilesToKeep, const std::vector& in_runtimeArtifactsToKeep, int64_t* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); std::unordered_set files_to_keep; for (const ProfilePath& profile : in_profilesToKeep) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(profile, "profilesToKeep"); files_to_keep.insert(OR_RETURN_FATAL(BuildProfileOrDmPath(profile))); } for (const ArtifactsPath& artifacts : in_artifactsToKeep) { - std::string oat_path = OR_RETURN_FATAL(BuildOatPath(artifacts)); - files_to_keep.insert(OatPathToVdexPath(oat_path)); - files_to_keep.insert(OatPathToArtPath(oat_path)); - files_to_keep.insert(std::move(oat_path)); + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(artifacts, "artifactsToKeep"); + RawArtifactsPath path = OR_RETURN_FATAL(BuildArtifactsPath(artifacts)); + files_to_keep.insert(std::move(path.oat_path)); + files_to_keep.insert(std::move(path.vdex_path)); + files_to_keep.insert(std::move(path.art_path)); } for (const VdexPath& vdex : in_vdexFilesToKeep) { + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(vdex, "vdexFilesToKeep"); files_to_keep.insert(OR_RETURN_FATAL(BuildVdexPath(vdex))); } std::string android_data = OR_RETURN_NON_FATAL(GetAndroidDataOrError()); @@ -1268,6 +1317,7 @@ ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_r ScopedAStatus Artd::deleteRuntimeArtifacts(const RuntimeArtifactsPath& in_runtimeArtifactsPath, int64_t* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); OR_RETURN_FATAL(ValidateRuntimeArtifactsPath(in_runtimeArtifactsPath)); *_aidl_return = 0; std::string android_data = OR_LOG_AND_RETURN_OK(GetAndroidDataOrError()); @@ -1280,15 +1330,19 @@ ScopedAStatus Artd::deleteRuntimeArtifacts(const RuntimeArtifactsPath& in_runtim } ScopedAStatus Artd::getArtifactsSize(const ArtifactsPath& in_artifactsPath, int64_t* _aidl_return) { - std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath)); + RETURN_FATAL_IF_PRE_REBOOT(options_); + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_artifactsPath, "artifactsPath"); + RawArtifactsPath path = OR_RETURN_FATAL(BuildArtifactsPath(in_artifactsPath)); *_aidl_return = 0; - *_aidl_return += GetSize(oat_path).value_or(0); - *_aidl_return += GetSize(OatPathToVdexPath(oat_path)).value_or(0); - *_aidl_return += GetSize(OatPathToArtPath(oat_path)).value_or(0); + *_aidl_return += GetSize(path.oat_path).value_or(0); + *_aidl_return += GetSize(path.vdex_path).value_or(0); + *_aidl_return += GetSize(path.art_path).value_or(0); return ScopedAStatus::ok(); } ScopedAStatus Artd::getVdexFileSize(const VdexPath& in_vdexPath, int64_t* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_vdexPath, "vdexPath"); std::string vdex_path = OR_RETURN_FATAL(BuildVdexPath(in_vdexPath)); *_aidl_return = GetSize(vdex_path).value_or(0); return ScopedAStatus::ok(); @@ -1296,6 +1350,7 @@ ScopedAStatus Artd::getVdexFileSize(const VdexPath& in_vdexPath, int64_t* _aidl_ ScopedAStatus Artd::getRuntimeArtifactsSize(const RuntimeArtifactsPath& in_runtimeArtifactsPath, int64_t* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); OR_RETURN_FATAL(ValidateRuntimeArtifactsPath(in_runtimeArtifactsPath)); *_aidl_return = 0; std::string android_data = OR_LOG_AND_RETURN_OK(GetAndroidDataOrError()); @@ -1308,6 +1363,8 @@ ScopedAStatus Artd::getRuntimeArtifactsSize(const RuntimeArtifactsPath& in_runti } ScopedAStatus Artd::getProfileSize(const ProfilePath& in_profile, int64_t* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(in_profile, "profile"); std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile)); *_aidl_return = GetSize(profile_path).value_or(0); return ScopedAStatus::ok(); diff --git a/artd/artd_test.cc b/artd/artd_test.cc index 7c3f1bc803..fccf091464 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -129,6 +129,7 @@ using ::testing::WithArg; using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath; using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath; using TmpProfilePath = ProfilePath::TmpProfilePath; +using WritableProfilePath = ProfilePath::WritableProfilePath; using std::literals::operator""s; // NOLINT @@ -392,7 +393,8 @@ class ArtdTest : public CommonArtTest { compiler_filter_ = "speed"; tmp_profile_path_ = TmpProfilePath{.finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}, + .profileName = "primary", + .isPreReboot = false}, .id = "12345"}; profile_path_ = tmp_profile_path_; vdex_path_ = artifacts_path_; @@ -447,7 +449,7 @@ class ArtdTest : public CommonArtTest { std::pair, OutputProfile>>; - // Runs `copyAndRewriteProfile` with `tmp_profile_path_` and `dex_file_`. + // Runs `copyAndRewriteProfile` with `profile_path_` and `dex_file_`. template RunCopyAndRewriteProfileResult RunCopyAndRewriteProfile() { OutputProfile dst{.profilePath = tmp_profile_path_, @@ -457,7 +459,7 @@ class ArtdTest : public CommonArtTest { CopyAndRewriteProfileResult result; ndk::ScopedAStatus status = - artd_->copyAndRewriteProfile(tmp_profile_path_, &dst, dex_file_, &result); + artd_->copyAndRewriteProfile(profile_path_.value(), &dst, dex_file_, &result); if constexpr (kExpectOk) { if (!status.isOk()) { return Error() << status.getMessage(); @@ -568,10 +570,10 @@ class ArtdTest : public CommonArtTest { } // Files to be replaced. - std::string oat_path = OR_FATAL(BuildOatPath(artifacts_path_)); - CreateFile(oat_path, "old_oat"); - CreateFile(OatPathToVdexPath(oat_path), "old_vdex"); - CreateFile(OatPathToArtPath(oat_path), "old_art"); + RawArtifactsPath artifacts_path = OR_FATAL(BuildArtifactsPath(artifacts_path_)); + CreateFile(artifacts_path.oat_path, "old_oat"); + CreateFile(artifacts_path.vdex_path, "old_vdex"); + CreateFile(artifacts_path.art_path, "old_art"); } }; @@ -1389,7 +1391,7 @@ TEST_F(ArtdTest, isProfileUsableFailed) { } TEST_F(ArtdTest, copyAndRewriteProfileSuccess) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateFile(src_file, "valid_profile"); CreateFile(dex_file_); @@ -1421,7 +1423,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileSuccess) { // The input is a plain profile file in the wrong format. TEST_F(ArtdTest, copyAndRewriteProfileBadProfileWrongFormat) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateFile(src_file, "wrong_format"); CreateFile(dex_file_); @@ -1440,7 +1442,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileBadProfileWrongFormat) { // The input is a plain profile file that doesn't match the APK. TEST_F(ArtdTest, copyAndRewriteProfileBadProfileNoMatch) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateFile(src_file, "no_match"); CreateFile(dex_file_); @@ -1458,7 +1460,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileBadProfileNoMatch) { // The input is a plain profile file that is empty. TEST_F(ArtdTest, copyAndRewriteProfileNoProfileEmpty) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateFile(src_file, ""); CreateFile(dex_file_); @@ -1486,7 +1488,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileNoProfileNoFile) { // The input is a dm file with a profile entry in the wrong format. TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmWrongFormat) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateZipWithSingleEntry(src_file, "primary.prof", "wrong_format"); CreateFile(dex_file_); @@ -1505,7 +1507,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmWrongFormat) { // The input is a dm file with a profile entry that doesn't match the APK. TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmNoMatch) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateZipWithSingleEntry(src_file, "primary.prof", "no_match"); CreateFile(dex_file_); @@ -1523,7 +1525,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmNoMatch) { // The input is a dm file with a profile entry that is empty. TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmEmpty) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateZipWithSingleEntry(src_file, "primary.prof"); CreateFile(dex_file_); @@ -1540,7 +1542,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmEmpty) { // The input is a dm file without a profile entry. TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmNoEntry) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateZipWithSingleEntry(src_file, "primary.vdex"); CreateFile(dex_file_); @@ -1556,7 +1558,7 @@ TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmNoEntry) { } TEST_F(ArtdTest, copyAndRewriteProfileException) { - std::string src_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_)); + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateFile(src_file, "valid_profile"); CreateFile(dex_file_); @@ -1780,13 +1782,15 @@ TEST_F(ArtdGetVisibilityTest, getProfileVisibilityPermissionDenied) { } TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityOtherReadable) { - TestGetVisibilityOtherReadable( - &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_))); + TestGetVisibilityOtherReadable(&Artd::getArtifactsVisibility, + artifacts_path_, + OR_FATAL(BuildArtifactsPath(artifacts_path_)).oat_path); } TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotOtherReadable) { - TestGetVisibilityNotOtherReadable( - &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_))); + TestGetVisibilityNotOtherReadable(&Artd::getArtifactsVisibility, + artifacts_path_, + OR_FATAL(BuildArtifactsPath(artifacts_path_)).oat_path); } TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotFound) { @@ -1794,8 +1798,9 @@ TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotFound) { } TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityPermissionDenied) { - TestGetVisibilityPermissionDenied( - &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_))); + TestGetVisibilityPermissionDenied(&Artd::getArtifactsVisibility, + artifacts_path_, + OR_FATAL(BuildArtifactsPath(artifacts_path_)).oat_path); } TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityOtherReadable) { @@ -1837,8 +1842,7 @@ TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityPermissionDenied) { } TEST_F(ArtdTest, mergeProfiles) { - const TmpProfilePath& reference_profile_path = tmp_profile_path_; - std::string reference_profile_file = OR_FATAL(BuildTmpProfilePath(reference_profile_path)); + std::string reference_profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); CreateFile(reference_profile_file, "abc"); // Doesn't exist. @@ -1851,7 +1855,7 @@ TEST_F(ArtdTest, mergeProfiles) { std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path)); CreateFile(profile_1_file, "def"); - OutputProfile output_profile{.profilePath = reference_profile_path, + OutputProfile output_profile{.profilePath = tmp_profile_path_, .fsPermission = FsPermission{.uid = -1, .gid = -1}}; output_profile.profilePath.id = ""; output_profile.profilePath.tmpPath = ""; @@ -1884,7 +1888,7 @@ TEST_F(ArtdTest, mergeProfiles) { bool result; EXPECT_TRUE(artd_ ->mergeProfiles({profile_0_path, profile_1_path}, - reference_profile_path, + profile_path_, &output_profile, {dex_file_1, dex_file_2}, /*in_options=*/{}, @@ -1939,10 +1943,6 @@ TEST_F(ArtdTest, mergeProfilesEmptyReferenceProfile) { } TEST_F(ArtdTest, mergeProfilesProfilesDontExist) { - const TmpProfilePath& reference_profile_path = tmp_profile_path_; - std::string reference_profile_file = OR_FATAL(BuildTmpProfilePath(reference_profile_path)); - CreateFile(reference_profile_file, "abc"); - // Doesn't exist. PrimaryCurProfilePath profile_0_path{ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"}; @@ -1953,7 +1953,7 @@ TEST_F(ArtdTest, mergeProfilesProfilesDontExist) { .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}; std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path)); - OutputProfile output_profile{.profilePath = reference_profile_path, + OutputProfile output_profile{.profilePath = tmp_profile_path_, .fsPermission = FsPermission{.uid = -1, .gid = -1}}; output_profile.profilePath.id = ""; output_profile.profilePath.tmpPath = ""; @@ -1965,7 +1965,7 @@ TEST_F(ArtdTest, mergeProfilesProfilesDontExist) { bool result; EXPECT_TRUE(artd_ ->mergeProfiles({profile_0_path}, - std::nullopt, + /*in_referenceProfile=*/std::nullopt, &output_profile, {dex_file_}, /*in_options=*/{}, @@ -2479,6 +2479,9 @@ class ArtdPreRebootTest : public ArtdTest { )"; ASSERT_TRUE(WriteStringToFile(ART_FORMAT(kInitEnvironRcTmpl, art_root_, android_data_), init_environ_rc_path_)); + + tmp_profile_path_.finalPath.get().isPreReboot = true; + output_artifacts_.artifactsPath.isPreReboot = true; } std::string pre_reboot_tmp_dir_; @@ -2599,6 +2602,167 @@ TEST_F(ArtdPreRebootTest, preRebootInitNoRetry) { "preRebootInit must not be concurrently called or retried after failure"); } +TEST_F(ArtdPreRebootTest, dexopt) { + std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); + + dexopt_options_.generateAppImage = true; + + EXPECT_CALL( + *mock_exec_utils_, + DoExecAndReturnCode( + WhenSplitBy("--", _, Contains(Flag("--profile-file-fd=", FdOf(profile_file)))), _, _)) + .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")), + WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")), + WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")), + Return(0))); + RunDexopt(); + + CheckContent(scratch_path_ + "/a/oat/arm64/b.odex.staged", "oat"); + CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex.staged", "vdex"); + CheckContent(scratch_path_ + "/a/oat/arm64/b.art.staged", "art"); +} + +TEST_F(ArtdPreRebootTest, dexoptPreRebootProfile) { + profile_path_->get() + .finalPath.get() + .isPreReboot = true; + std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); + + dexopt_options_.generateAppImage = true; + + EXPECT_CALL( + *mock_exec_utils_, + DoExecAndReturnCode( + WhenSplitBy("--", _, Contains(Flag("--profile-file-fd=", FdOf(profile_file)))), _, _)) + .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")), + WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")), + WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")), + Return(0))); + RunDexopt(); + + CheckContent(scratch_path_ + "/a/oat/arm64/b.odex.staged", "oat"); + CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex.staged", "vdex"); + CheckContent(scratch_path_ + "/a/oat/arm64/b.art.staged", "art"); +} + +TEST_F(ArtdPreRebootTest, copyAndRewriteProfile) { + std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); + CreateFile(src_file, "valid_profile"); + + CreateFile(dex_file_); + + EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode) + .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")), + Return(ProfmanResult::kCopyAndUpdateSuccess))); + + auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile()); + + EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::SUCCESS); + EXPECT_THAT(dst.profilePath.tmpPath, ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re")); + CheckContent(dst.profilePath.tmpPath, "def"); +} + +TEST_F(ArtdPreRebootTest, copyAndRewriteEmbeddedProfile) { + CreateZipWithSingleEntry(dex_file_, "assets/art-profile/baseline.prof", "valid_profile"); + + EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode) + .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")), + Return(ProfmanResult::kCopyAndUpdateSuccess))); + + auto [result, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile()); + + EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::SUCCESS); + EXPECT_THAT(dst.profilePath.tmpPath, ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re")); + CheckContent(dst.profilePath.tmpPath, "def"); +} + +TEST_F(ArtdPreRebootTest, mergeProfiles) { + std::string reference_profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); + CreateFile(reference_profile_file, "abc"); + + PrimaryCurProfilePath profile_1_path{ + .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}; + std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path)); + CreateFile(profile_1_file, "def"); + + OutputProfile output_profile{.profilePath = tmp_profile_path_, + .fsPermission = FsPermission{.uid = -1, .gid = -1}}; + output_profile.profilePath.id = ""; + output_profile.profilePath.tmpPath = ""; + + std::string dex_file_1 = scratch_path_ + "/a/b.apk"; + CreateFile(dex_file_1); + + EXPECT_CALL( + *mock_exec_utils_, + DoExecAndReturnCode( + WhenSplitBy("--", + _, + AllOf(Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))), + Contains(Flag("--profile-file-fd=", FdHasContent("def"))))), + _, + _)) + .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")), + Return(ProfmanResult::kCompile))); + + bool result; + ASSERT_STATUS_OK(artd_->mergeProfiles({profile_1_path}, + profile_path_, + &output_profile, + {dex_file_1}, + /*in_options=*/{}, + &result)); + EXPECT_TRUE(result); + EXPECT_THAT(output_profile.profilePath.tmpPath, + ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re")); + CheckContent(output_profile.profilePath.tmpPath, "merged"); +} + +TEST_F(ArtdPreRebootTest, mergeProfilesPreRebootReference) { + profile_path_->get() + .finalPath.get() + .isPreReboot = true; + std::string reference_profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value())); + CreateFile(reference_profile_file, "abc"); + + PrimaryCurProfilePath profile_1_path{ + .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}; + std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path)); + CreateFile(profile_1_file, "def"); + + OutputProfile output_profile{.profilePath = tmp_profile_path_, + .fsPermission = FsPermission{.uid = -1, .gid = -1}}; + output_profile.profilePath.id = ""; + output_profile.profilePath.tmpPath = ""; + + std::string dex_file_1 = scratch_path_ + "/a/b.apk"; + CreateFile(dex_file_1); + + EXPECT_CALL( + *mock_exec_utils_, + DoExecAndReturnCode( + WhenSplitBy("--", + _, + AllOf(Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))), + Contains(Flag("--profile-file-fd=", FdHasContent("def"))))), + _, + _)) + .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")), + Return(ProfmanResult::kCompile))); + + bool result; + ASSERT_STATUS_OK(artd_->mergeProfiles({profile_1_path}, + profile_path_, + &output_profile, + {dex_file_1}, + /*in_options=*/{}, + &result)); + EXPECT_TRUE(result); + EXPECT_THAT(output_profile.profilePath.tmpPath, + ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re")); + CheckContent(output_profile.profilePath.tmpPath, "merged"); +} + } // namespace } // namespace artd } // namespace art diff --git a/artd/binder/com/android/server/art/ArtifactsPath.aidl b/artd/binder/com/android/server/art/ArtifactsPath.aidl index 3122f0f6b9..e1b1906263 100644 --- a/artd/binder/com/android/server/art/ArtifactsPath.aidl +++ b/artd/binder/com/android/server/art/ArtifactsPath.aidl @@ -28,4 +28,9 @@ parcelable ArtifactsPath { @utf8InCpp String isa; /** Whether the dexopt artifacts are in the dalvik-cache folder. */ boolean isInDalvikCache; + /** + * Whether the dexopt artifacts are for Pre-reboot Dexopt. For now, this is always `false` for + * inputs because we never take Pre-reboot artifacts as inputs. + */ + boolean isPreReboot; } diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index 4273d85515..7f0bc5690c 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -27,6 +27,8 @@ interface IArtd { * Note that this method doesn't delete runtime artifacts. To delete them, call * `deleteRuntimeArtifacts`. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long deleteArtifacts(in com.android.server.art.ArtifactsPath artifactsPath); @@ -34,6 +36,8 @@ interface IArtd { /** * Returns the dexopt status of a dex file. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal and non-fatal errors. */ com.android.server.art.GetDexoptStatusResult getDexoptStatus( @@ -177,6 +181,8 @@ interface IArtd { * kept. For each entry in `vdexFilesToKeep`, only the VDEX file will be kept. Note that VDEX * files included in `artifactsToKeep` don't have to be listed in `vdexFilesToKeep`. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long cleanup(in List profilesToKeep, @@ -195,6 +201,8 @@ interface IArtd { /** * Deletes runtime artifacts and returns the released space, in bytes. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long deleteRuntimeArtifacts( @@ -204,6 +212,8 @@ interface IArtd { * Returns the size of the dexopt artifacts, in bytes, or 0 if they don't exist or a non-fatal * error occurred. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long getArtifactsSize(in com.android.server.art.ArtifactsPath artifactsPath); @@ -212,6 +222,8 @@ interface IArtd { * Returns the size of the vdex file, in bytes, or 0 if it doesn't exist or a non-fatal error * occurred. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long getVdexFileSize(in com.android.server.art.VdexPath vdexPath); @@ -220,6 +232,8 @@ interface IArtd { * Returns the size of the runtime artifacts, in bytes, or 0 if they don't exist or a non-fatal * error occurred. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long getRuntimeArtifactsSize( @@ -231,6 +245,8 @@ interface IArtd { * * Operates on the whole DM file if given one. * + * Not supported in Pre-reboot Dexopt mode. + * * Throws fatal errors. Logs and ignores non-fatal errors. */ long getProfileSize(in com.android.server.art.ProfilePath profile); diff --git a/artd/binder/com/android/server/art/ProfilePath.aidl b/artd/binder/com/android/server/art/ProfilePath.aidl index 43df531d89..d0bbcf2be0 100644 --- a/artd/binder/com/android/server/art/ProfilePath.aidl +++ b/artd/binder/com/android/server/art/ProfilePath.aidl @@ -38,6 +38,12 @@ union ProfilePath { @utf8InCpp String packageName; /** The stem of the profile file */ @utf8InCpp String profileName; + /** + * Whether the profile is for Pre-reboot Dexopt. For now, this is `false` for inputs, unless + * the data structure refers to a temporary profile, as an argument of `mergeProfiles` or + * `dexopt`, in the Pre-reboot Dexopt mode. + */ + boolean isPreReboot; } /** @@ -70,6 +76,12 @@ union ProfilePath { * `{/data,/mnt/expand/}/{user,user_de}///...`. */ @utf8InCpp String dexPath; + /** + * Whether the profile is for Pre-reboot Dexopt. For now, this is `false` for inputs, unless + * the data structure refers to a temporary profile, as an argument of `mergeProfiles` or + * `dexopt`, in the Pre-reboot Dexopt mode. + */ + boolean isPreReboot; } /** Represents a current profile of a secondary dex file. */ diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 1be75b9358..d3eb6d4dfe 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -39,6 +39,8 @@ namespace { using ::aidl::com::android::server::art::ArtifactsPath; using ::aidl::com::android::server::art::DexMetadataPath; +using ::aidl::com::android::server::art::OutputArtifacts; +using ::aidl::com::android::server::art::OutputProfile; using ::aidl::com::android::server::art::ProfilePath; using ::aidl::com::android::server::art::RuntimeArtifactsPath; using ::aidl::com::android::server::art::VdexPath; @@ -56,6 +58,8 @@ using SecondaryRefProfilePath = ProfilePath::SecondaryRefProfilePath; using TmpProfilePath = ProfilePath::TmpProfilePath; using WritableProfilePath = ProfilePath::WritableProfilePath; +constexpr const char* kPreRebootSuffix = ".staged"; + // Only to be changed for testing. std::string_view gListRootDir = "/"; @@ -153,7 +157,7 @@ Result BuildArtBinPath(const std::string& binary_name) { return ART_FORMAT("{}/bin/{}", OR_RETURN(GetArtRootOrError()), binary_name); } -Result BuildOatPath(const ArtifactsPath& artifacts_path) { +Result BuildArtifactsPath(const ArtifactsPath& artifacts_path) { OR_RETURN(ValidateDexPath(artifacts_path.dexPath)); InstructionSet isa = GetInstructionSetFromString(artifacts_path.isa.c_str()); @@ -162,31 +166,44 @@ Result BuildOatPath(const ArtifactsPath& artifacts_path) { } std::string error_msg; - std::string path; + RawArtifactsPath path; if (artifacts_path.isInDalvikCache) { // Apps' OAT files are never in ART APEX data. - if (!OatFileAssistant::DexLocationToOatFilename( - artifacts_path.dexPath, isa, /*deny_art_apex_data_files=*/true, &path, &error_msg)) { + if (!OatFileAssistant::DexLocationToOatFilename(artifacts_path.dexPath, + isa, + /*deny_art_apex_data_files=*/true, + &path.oat_path, + &error_msg)) { return Error() << error_msg; } - return path; } else { if (!OatFileAssistant::DexLocationToOdexFilename( - artifacts_path.dexPath, isa, &path, &error_msg)) { + artifacts_path.dexPath, isa, &path.oat_path, &error_msg)) { return Error() << error_msg; } - return path; } + + path.vdex_path = ReplaceFileExtension(path.oat_path, "vdex"); + path.art_path = ReplaceFileExtension(path.oat_path, "art"); + + if (artifacts_path.isPreReboot) { + path.oat_path += kPreRebootSuffix; + path.vdex_path += kPreRebootSuffix; + path.art_path += kPreRebootSuffix; + } + + return path; } Result BuildPrimaryRefProfilePath( const PrimaryRefProfilePath& primary_ref_profile_path) { OR_RETURN(ValidatePathElement(primary_ref_profile_path.packageName, "packageName")); OR_RETURN(ValidatePathElementSubstring(primary_ref_profile_path.profileName, "profileName")); - return ART_FORMAT("{}/misc/profiles/ref/{}/{}.prof", + return ART_FORMAT("{}/misc/profiles/ref/{}/{}.prof{}", OR_RETURN(GetAndroidDataOrError()), primary_ref_profile_path.packageName, - primary_ref_profile_path.profileName); + primary_ref_profile_path.profileName, + primary_ref_profile_path.isPreReboot ? kPreRebootSuffix : ""); } Result BuildPrebuiltProfilePath(const PrebuiltProfilePath& prebuilt_profile_path) { @@ -209,8 +226,10 @@ Result BuildSecondaryRefProfilePath( const SecondaryRefProfilePath& secondary_ref_profile_path) { OR_RETURN(ValidateDexPath(secondary_ref_profile_path.dexPath)); std::filesystem::path dex_path(secondary_ref_profile_path.dexPath); - return ART_FORMAT( - "{}/oat/{}.prof", dex_path.parent_path().string(), dex_path.filename().string()); + return ART_FORMAT("{}/oat/{}.prof{}", + dex_path.parent_path().string(), + dex_path.filename().string(), + secondary_ref_profile_path.isPreReboot ? kPreRebootSuffix : ""); } Result BuildSecondaryCurProfilePath( @@ -271,7 +290,43 @@ Result BuildProfileOrDmPath(const ProfilePath& profile_path) { Result BuildVdexPath(const VdexPath& vdex_path) { DCHECK(vdex_path.getTag() == VdexPath::artifactsPath); - return OatPathToVdexPath(OR_RETURN(BuildOatPath(vdex_path.get()))); + return OR_RETURN(BuildArtifactsPath(vdex_path.get())).vdex_path; +} + +bool PreRebootFlag(const ProfilePath& profile_path) { + switch (profile_path.getTag()) { + case ProfilePath::primaryRefProfilePath: + return profile_path.get().isPreReboot; + case ProfilePath::secondaryRefProfilePath: + return profile_path.get().isPreReboot; + case ProfilePath::tmpProfilePath: + return PreRebootFlag(profile_path.get()); + case ProfilePath::prebuiltProfilePath: + case ProfilePath::primaryCurProfilePath: + case ProfilePath::secondaryCurProfilePath: + case ProfilePath::dexMetadataPath: + return false; + // 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) << ART_FORMAT("Unexpected profile path type {}", + fmt::underlying(profile_path.getTag())); +} + +bool PreRebootFlag(const TmpProfilePath& tmp_profile_path) { + return PreRebootFlag(tmp_profile_path.finalPath); +} + +bool PreRebootFlag(const OutputProfile& profile) { return PreRebootFlag(profile.profilePath); } + +bool PreRebootFlag(const ArtifactsPath& artifacts_path) { return artifacts_path.isPreReboot; } + +bool PreRebootFlag(const OutputArtifacts& artifacts) { + return PreRebootFlag(artifacts.artifactsPath); +} + +bool PreRebootFlag(const VdexPath& vdex_path) { + return PreRebootFlag(vdex_path.get()); } void TestOnlySetListRootDir(std::string_view root_dir) { gListRootDir = root_dir; } diff --git a/artd/path_utils.h b/artd/path_utils.h index 9cbec2351a..a4a6e4f2a7 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -18,16 +18,23 @@ #define ART_ARTD_PATH_UTILS_H_ #include +#include #include #include "aidl/com/android/server/art/BnArtd.h" +#include "android-base/logging.h" #include "android-base/result.h" -#include "base/file_utils.h" -#include "fstab/fstab.h" +#include "base/macros.h" namespace art { namespace artd { +struct RawArtifactsPath { + std::string oat_path; + std::string vdex_path; + std::string art_path; +}; + android::base::Result GetAndroidDataOrError(); android::base::Result GetAndroidExpandOrError(); @@ -48,20 +55,10 @@ android::base::Result ValidateRuntimeArtifactsPath( android::base::Result BuildArtBinPath(const std::string& binary_name); -// Returns the absolute path to the OAT file built from the `ArtifactsPath`. -android::base::Result BuildOatPath( +// Returns the absolute paths to files built from the `ArtifactsPath`. +android::base::Result BuildArtifactsPath( const aidl::com::android::server::art::ArtifactsPath& artifacts_path); -// Returns the path to the VDEX file that corresponds to the OAT file. -inline std::string OatPathToVdexPath(const std::string& oat_path) { - return ReplaceFileExtension(oat_path, "vdex"); -} - -// Returns the path to the ART file that corresponds to the OAT file. -inline std::string OatPathToArtPath(const std::string& oat_path) { - return ReplaceFileExtension(oat_path, "art"); -} - android::base::Result BuildPrimaryRefProfilePath( const aidl::com::android::server::art::ProfilePath::PrimaryRefProfilePath& primary_ref_profile_path); @@ -96,6 +93,38 @@ android::base::Result BuildProfileOrDmPath( android::base::Result BuildVdexPath( const aidl::com::android::server::art::VdexPath& vdex_path); +// Takes an argument of type `WritableProfilePath`. Returns the pre-reboot flag by value if the +// argument is const, or by reference otherwise. +template , + aidl::com::android::server::art::ProfilePath::WritableProfilePath>>> +std::conditional_t, bool, bool&> PreRebootFlag(T& profile_path) { + switch (profile_path.getTag()) { + case T::forPrimary: + return profile_path.template get().isPreReboot; + case T::forSecondary: + return profile_path.template get().isPreReboot; + // 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) << ART_FORMAT("Unexpected writable profile path type {}", + fmt::underlying(profile_path.getTag())); +} + +template bool PreRebootFlag( + const aidl::com::android::server::art::ProfilePath::WritableProfilePath& profile_path); +template bool& PreRebootFlag( + aidl::com::android::server::art::ProfilePath::WritableProfilePath& profile_path); + +bool PreRebootFlag(const aidl::com::android::server::art::ProfilePath& profile_path); +bool PreRebootFlag( + const aidl::com::android::server::art::ProfilePath::TmpProfilePath& tmp_profile_path); +bool PreRebootFlag(const aidl::com::android::server::art::OutputProfile& profile); +bool PreRebootFlag(const aidl::com::android::server::art::ArtifactsPath& artifacts_path); +bool PreRebootFlag(const aidl::com::android::server::art::OutputArtifacts& artifacts); +bool PreRebootFlag(const aidl::com::android::server::art::VdexPath& vdex_path); + // Sets the root dir for `ListManagedFiles` and `ListRuntimeImageFiles`. // The passed string must be alive until the test ends. // For testing use only. diff --git a/artd/path_utils_test.cc b/artd/path_utils_test.cc index 5851245eac..116177a7a7 100644 --- a/artd/path_utils_test.cc +++ b/artd/path_utils_test.cc @@ -19,6 +19,7 @@ #include "aidl/com/android/server/art/BnArtd.h" #include "android-base/result-gmock.h" #include "base/common_art_test.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" namespace art { @@ -31,6 +32,8 @@ using ::aidl::com::android::server::art::ProfilePath; using ::android::base::testing::HasError; using ::android::base::testing::HasValue; using ::android::base::testing::WithMessage; +using ::testing::AllOf; +using ::testing::Field; using PrebuiltProfilePath = ProfilePath::PrebuiltProfilePath; using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath; @@ -50,75 +53,107 @@ TEST_F(PathUtilsTest, BuildArtBinPath) { EXPECT_THAT(BuildArtBinPath("foo"), HasValue(scratch_dir->GetPath() + "/bin/foo")); } -TEST_F(PathUtilsTest, BuildOatPath) { +TEST_F(PathUtilsTest, BuildArtifactsPath) { EXPECT_THAT( - BuildOatPath(ArtifactsPath{.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}), - HasValue("/a/oat/arm64/b.odex")); + BuildArtifactsPath(ArtifactsPath{ + .dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false, .isPreReboot = false}), + HasValue(AllOf(Field(&RawArtifactsPath::oat_path, "/a/oat/arm64/b.odex"), + Field(&RawArtifactsPath::vdex_path, "/a/oat/arm64/b.vdex"), + Field(&RawArtifactsPath::art_path, "/a/oat/arm64/b.art")))); } -TEST_F(PathUtilsTest, BuildOatPathDalvikCache) { +TEST_F(PathUtilsTest, BuildArtifactsPathPreReboot) { EXPECT_THAT( - BuildOatPath(ArtifactsPath{.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = true}), - HasValue(android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.dex")); + BuildArtifactsPath(ArtifactsPath{ + .dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false, .isPreReboot = true}), + HasValue(AllOf(Field(&RawArtifactsPath::oat_path, "/a/oat/arm64/b.odex.staged"), + Field(&RawArtifactsPath::vdex_path, "/a/oat/arm64/b.vdex.staged"), + Field(&RawArtifactsPath::art_path, "/a/oat/arm64/b.art.staged")))); } -TEST_F(PathUtilsTest, BuildOatPathInvalidDexPath) { +TEST_F(PathUtilsTest, BuildArtifactsPathDalvikCache) { EXPECT_THAT( - BuildOatPath(ArtifactsPath{.dexPath = "a/b.apk", .isa = "arm64", .isInDalvikCache = false}), - HasError(WithMessage("Path 'a/b.apk' is not an absolute path"))); + BuildArtifactsPath(ArtifactsPath{ + .dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = true, .isPreReboot = false}), + HasValue(AllOf(Field(&RawArtifactsPath::oat_path, + android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.dex"), + Field(&RawArtifactsPath::vdex_path, + android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.vdex"), + Field(&RawArtifactsPath::art_path, + android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.art")))); } -TEST_F(PathUtilsTest, BuildOatPathInvalidIsa) { - EXPECT_THAT(BuildOatPath( - ArtifactsPath{.dexPath = "/a/b.apk", .isa = "invalid", .isInDalvikCache = false}), - HasError(WithMessage("Instruction set 'invalid' is invalid"))); +TEST_F(PathUtilsTest, BuildArtifactsPathDalvikCachePreReboot) { + EXPECT_THAT( + BuildArtifactsPath(ArtifactsPath{ + .dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = true, .isPreReboot = true}), + HasValue(AllOf(Field(&RawArtifactsPath::oat_path, + android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.dex.staged"), + Field(&RawArtifactsPath::vdex_path, + android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.vdex.staged"), + Field(&RawArtifactsPath::art_path, + android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.art.staged")))); } -TEST_F(PathUtilsTest, OatPathToVdexPath) { - EXPECT_EQ(OatPathToVdexPath("/a/oat/arm64/b.odex"), "/a/oat/arm64/b.vdex"); +TEST_F(PathUtilsTest, BuildOatPathInvalidDexPath) { + EXPECT_THAT( + BuildArtifactsPath(ArtifactsPath{ + .dexPath = "a/b.apk", .isa = "arm64", .isInDalvikCache = false, .isPreReboot = false}), + HasError(WithMessage("Path 'a/b.apk' is not an absolute path"))); } -TEST_F(PathUtilsTest, OatPathToArtPath) { - EXPECT_EQ(OatPathToArtPath("/a/oat/arm64/b.odex"), "/a/oat/arm64/b.art"); +TEST_F(PathUtilsTest, BuildOatPathInvalidIsa) { + EXPECT_THAT( + BuildArtifactsPath(ArtifactsPath{ + .dexPath = "/a/b.apk", .isa = "invalid", .isInDalvikCache = false, .isPreReboot = false}), + HasError(WithMessage("Instruction set 'invalid' is invalid"))); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePath) { - EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}), - HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof")); + EXPECT_THAT( + BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "primary", .isPreReboot = false}), + HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof")); +} + +TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPreReboot) { + EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "primary", .isPreReboot = true}), + HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.staged")); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPackageNameOk) { - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "...", .profileName = "primary"}), + EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "...", .profileName = "primary", .isPreReboot = false}), HasValue(android_data_ + "/misc/profiles/ref/.../primary.prof")); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPackageNameWrong) { - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "..", .profileName = "primary"}), + EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "..", .profileName = "primary", .isPreReboot = false}), HasError(WithMessage("Invalid packageName '..'"))); - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "a/b", .profileName = "primary"}), + EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "a/b", .profileName = "primary", .isPreReboot = false}), HasError(WithMessage("packageName 'a/b' has invalid character '/'"))); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathProfileNameOk) { - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = ".."}), + EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "..", .isPreReboot = false}), HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/...prof")); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathProfileNameWrong) { - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "a/b"}), + EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "a/b", .isPreReboot = false}), HasError(WithMessage("profileName 'a/b' has invalid character '/'"))); } TEST_F(PathUtilsTest, BuildFinalProfilePathForPrimary) { EXPECT_THAT(BuildFinalProfilePath(TmpProfilePath{ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}, + .profileName = "primary", + .isPreReboot = false}, .id = "12345"}), HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof")); } @@ -126,7 +161,8 @@ TEST_F(PathUtilsTest, BuildFinalProfilePathForPrimary) { TEST_F(PathUtilsTest, BuildFinalProfilePathForSecondary) { EXPECT_THAT(BuildFinalProfilePath(TmpProfilePath{ .finalPath = SecondaryRefProfilePath{.dexPath = android_data_ + - "/user/0/com.android.foo/a.apk"}, + "/user/0/com.android.foo/a.apk", + .isPreReboot = false}, .id = "12345"}), HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof")); } @@ -135,7 +171,8 @@ TEST_F(PathUtilsTest, BuildTmpProfilePathForPrimary) { EXPECT_THAT( BuildTmpProfilePath(TmpProfilePath{ .finalPath = - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"}, + PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "primary", .isPreReboot = false}, .id = "12345"}), HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp")); } @@ -143,7 +180,8 @@ TEST_F(PathUtilsTest, BuildTmpProfilePathForPrimary) { TEST_F(PathUtilsTest, BuildTmpProfilePathForSecondary) { EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{ .finalPath = SecondaryRefProfilePath{.dexPath = android_data_ + - "/user/0/com.android.foo/a.apk"}, + "/user/0/com.android.foo/a.apk", + .isPreReboot = false}, .id = "12345"}), HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof.12345.tmp")); } @@ -151,7 +189,8 @@ TEST_F(PathUtilsTest, BuildTmpProfilePathForSecondary) { TEST_F(PathUtilsTest, BuildTmpProfilePathIdWrong) { EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}, + .profileName = "primary", + .isPreReboot = false}, .id = "123/45"}), HasError(WithMessage("id '123/45' has invalid character '/'"))); } @@ -168,9 +207,16 @@ TEST_F(PathUtilsTest, BuildPrimaryCurProfilePath) { } TEST_F(PathUtilsTest, BuildSecondaryRefProfilePath) { + EXPECT_THAT( + BuildSecondaryRefProfilePath(SecondaryRefProfilePath{ + .dexPath = android_data_ + "/user/0/com.android.foo/a.apk", .isPreReboot = false}), + HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof")); +} + +TEST_F(PathUtilsTest, BuildSecondaryRefProfilePathPreReboot) { EXPECT_THAT(BuildSecondaryRefProfilePath(SecondaryRefProfilePath{ - .dexPath = android_data_ + "/user/0/com.android.foo/a.apk"}), - HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof")); + .dexPath = android_data_ + "/user/0/com.android.foo/a.apk", .isPreReboot = true}), + HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof.staged")); } TEST_F(PathUtilsTest, BuildSecondaryCurProfilePath) { @@ -184,13 +230,15 @@ TEST_F(PathUtilsTest, BuildDexMetadataPath) { } TEST_F(PathUtilsTest, BuildProfilePath) { - EXPECT_THAT(BuildProfileOrDmPath(PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}), - HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof")); + EXPECT_THAT( + BuildProfileOrDmPath(PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "primary", .isPreReboot = false}), + HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof")); EXPECT_THAT( BuildProfileOrDmPath(TmpProfilePath{ .finalPath = - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"}, + PrimaryRefProfilePath{ + .packageName = "com.android.foo", .profileName = "primary", .isPreReboot = false}, .id = "12345"}), HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp")); EXPECT_THAT(BuildProfileOrDmPath(PrebuiltProfilePath{.dexPath = "/a/b.apk"}), diff --git a/libartservice/service/java/com/android/server/art/AidlUtils.java b/libartservice/service/java/com/android/server/art/AidlUtils.java index 4a1b75eaab..d42a6539c4 100644 --- a/libartservice/service/java/com/android/server/art/AidlUtils.java +++ b/libartservice/service/java/com/android/server/art/AidlUtils.java @@ -34,15 +34,24 @@ public final class AidlUtils { private AidlUtils() {} @NonNull - public static ArtifactsPath buildArtifactsPath( - @NonNull String dexPath, @NonNull String isa, boolean isInDalvikCache) { + private static ArtifactsPath buildArtifactsPath(@NonNull String dexPath, @NonNull String isa, + boolean isInDalvikCache, boolean isPreReboot) { var artifactsPath = new ArtifactsPath(); artifactsPath.dexPath = dexPath; artifactsPath.isa = isa; artifactsPath.isInDalvikCache = isInDalvikCache; + artifactsPath.isPreReboot = isPreReboot; return artifactsPath; } + @NonNull + public static ArtifactsPath buildArtifactsPathAsInput( + @NonNull String dexPath, @NonNull String isa, boolean isInDalvikCache) { + // The callers expect artifacts to be used as inputs, so we should always pick the + // non-Pre-reboot ones. + return buildArtifactsPath(dexPath, isa, isInDalvikCache, false /* isPreReboot */); + } + @NonNull public static FsPermission buildFsPermission( int uid, int gid, boolean isOtherReadable, boolean isOtherExecutable) { @@ -78,34 +87,41 @@ public final class AidlUtils { @NonNull public static OutputArtifacts buildOutputArtifacts(@NonNull String dexPath, @NonNull String isa, - boolean isInDalvikCache, @NonNull PermissionSettings permissionSettings) { + boolean isInDalvikCache, @NonNull PermissionSettings permissionSettings, + boolean isPreReboot) { var outputArtifacts = new OutputArtifacts(); - outputArtifacts.artifactsPath = buildArtifactsPath(dexPath, isa, isInDalvikCache); + outputArtifacts.artifactsPath = + buildArtifactsPath(dexPath, isa, isInDalvikCache, isPreReboot); outputArtifacts.permissionSettings = permissionSettings; return outputArtifacts; } @NonNull - public static PrimaryRefProfilePath buildPrimaryRefProfilePath( - @NonNull String packageName, @NonNull String profileName) { + private static PrimaryRefProfilePath buildPrimaryRefProfilePath( + @NonNull String packageName, @NonNull String profileName, boolean isPreReboot) { var primaryRefProfilePath = new PrimaryRefProfilePath(); primaryRefProfilePath.packageName = packageName; primaryRefProfilePath.profileName = profileName; + primaryRefProfilePath.isPreReboot = isPreReboot; return primaryRefProfilePath; } @NonNull - public static SecondaryRefProfilePath buildSecondaryRefProfilePath(@NonNull String dexPath) { + private static SecondaryRefProfilePath buildSecondaryRefProfilePath( + @NonNull String dexPath, boolean isPreReboot) { var secondaryRefProfilePath = new SecondaryRefProfilePath(); secondaryRefProfilePath.dexPath = dexPath; + secondaryRefProfilePath.isPreReboot = isPreReboot; return secondaryRefProfilePath; } @NonNull - public static ProfilePath buildProfilePathForPrimaryRef( + public static ProfilePath buildProfilePathForPrimaryRefAsInput( @NonNull String packageName, @NonNull String profileName) { + // The callers expect a profile to be used as an input, so we should always pick the + // non-Pre-reboot one. return ProfilePath.primaryRefProfilePath( - buildPrimaryRefProfilePath(packageName, profileName)); + buildPrimaryRefProfilePath(packageName, profileName, false /* isPreReboot */)); } @NonNull @@ -131,8 +147,11 @@ public final class AidlUtils { } @NonNull - public static ProfilePath buildProfilePathForSecondaryRef(@NonNull String dexPath) { - return ProfilePath.secondaryRefProfilePath(buildSecondaryRefProfilePath(dexPath)); + public static ProfilePath buildProfilePathForSecondaryRefAsInput(@NonNull String dexPath) { + // The callers expect a profile to be used as an input, so we should always pick the + // non-Pre-reboot one. + return ProfilePath.secondaryRefProfilePath( + buildSecondaryRefProfilePath(dexPath, false /* isPreReboot */)); } @NonNull @@ -156,18 +175,18 @@ public final class AidlUtils { @NonNull public static OutputProfile buildOutputProfileForPrimary(@NonNull String packageName, - @NonNull String profileName, int uid, int gid, boolean isPublic) { - return buildOutputProfile(WritableProfilePath.forPrimary( - buildPrimaryRefProfilePath(packageName, profileName)), + @NonNull String profileName, int uid, int gid, boolean isPublic, boolean isPreReboot) { + return buildOutputProfile(WritableProfilePath.forPrimary(buildPrimaryRefProfilePath( + packageName, profileName, isPreReboot)), uid, gid, isPublic); } @NonNull public static OutputProfile buildOutputProfileForSecondary( - @NonNull String dexPath, int uid, int gid, boolean isPublic) { - return buildOutputProfile( - WritableProfilePath.forSecondary(buildSecondaryRefProfilePath(dexPath)), uid, gid, - isPublic); + @NonNull String dexPath, int uid, int gid, boolean isPublic, boolean isPreReboot) { + return buildOutputProfile(WritableProfilePath.forSecondary( + buildSecondaryRefProfilePath(dexPath, isPreReboot)), + uid, gid, isPublic); } @NonNull @@ -190,13 +209,15 @@ public final class AidlUtils { @NonNull public static String toString(@NonNull PrimaryRefProfilePath profile) { - return String.format("PrimaryRefProfilePath[packageName = %s, profileName = %s]", - profile.packageName, profile.profileName); + return String.format( + "PrimaryRefProfilePath[packageName = %s, profileName = %s, isPreReboot = %b]", + profile.packageName, profile.profileName, profile.isPreReboot); } @NonNull public static String toString(@NonNull SecondaryRefProfilePath profile) { - return String.format("SecondaryRefProfilePath[dexPath = %s]", profile.dexPath); + return String.format("SecondaryRefProfilePath[dexPath = %s, isPreReboot = %b]", + profile.dexPath, profile.isPreReboot); } @NonNull diff --git a/libartservice/service/java/com/android/server/art/ArtFileManager.java b/libartservice/service/java/com/android/server/art/ArtFileManager.java index b46c8df9f2..234ca01f7c 100644 --- a/libartservice/service/java/com/android/server/art/ArtFileManager.java +++ b/libartservice/service/java/com/android/server/art/ArtFileManager.java @@ -111,7 +111,7 @@ public class ArtFileManager { boolean isInDalvikCache = Utils.isInDalvikCache(pkgState, mInjector.getArtd()); for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) { for (Abi abi : Utils.getAllAbis(pkgState)) { - artifacts.add(AidlUtils.buildArtifactsPath( + artifacts.add(AidlUtils.buildArtifactsPathAsInput( dexInfo.dexPath(), abi.isa(), isInDalvikCache)); // Runtime images are only generated for primary dex files. runtimeArtifacts.add(AidlUtils.buildRuntimeArtifactsPath( @@ -123,7 +123,7 @@ public class ArtFileManager { if (options.forSecondaryDex()) { for (SecondaryDexInfo dexInfo : getSecondaryDexInfo(pkgState, options)) { for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { - artifacts.add(AidlUtils.buildArtifactsPath( + artifacts.add(AidlUtils.buildArtifactsPathAsInput( dexInfo.dexPath(), abi.isa(), false /* isInDalvikCache */)); } } @@ -153,8 +153,9 @@ public class ArtFileManager { dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()); if (result.artifactsLocation == ArtifactsLocation.DALVIK_CACHE || result.artifactsLocation == ArtifactsLocation.NEXT_TO_DEX) { - ArtifactsPath thisArtifacts = AidlUtils.buildArtifactsPath(dexInfo.dexPath(), - abi.isa(), result.artifactsLocation == ArtifactsLocation.DALVIK_CACHE); + ArtifactsPath thisArtifacts = + AidlUtils.buildArtifactsPathAsInput(dexInfo.dexPath(), abi.isa(), + result.artifactsLocation == ArtifactsLocation.DALVIK_CACHE); if (result.compilationReason.equals(ArtConstants.REASON_VDEX)) { // Only the VDEX file is usable. vdexFiles.add(VdexPath.artifactsPath(thisArtifacts)); @@ -190,7 +191,7 @@ public class ArtFileManager { if (options.forPrimaryDex()) { for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) { - refProfiles.add(PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo)); + refProfiles.add(PrimaryDexUtils.buildRefProfilePathAsInput(pkgState, dexInfo)); curProfiles.addAll(mInjector.isSystemOrRootOrShell() ? PrimaryDexUtils.getCurProfiles( mInjector.getUserManager(), pkgState, dexInfo) @@ -206,7 +207,8 @@ public class ArtFileManager { && !mInjector.getCallingUserHandle().equals(dexInfo.userHandle())) { continue; } - refProfiles.add(AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath())); + refProfiles.add( + AidlUtils.buildProfilePathForSecondaryRefAsInput(dexInfo.dexPath())); curProfiles.add(AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); } } diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java index b548f9d83d..a9a357776e 100644 --- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java @@ -743,12 +743,14 @@ public final class ArtManagerLocal { List profiles = new ArrayList<>(); + // Doesn't support Pre-reboot. InitProfileResult result = Utils.getOrInitReferenceProfile(mInjector.getArtd(), - dexInfo.dexPath(), PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo), + dexInfo.dexPath(), + PrimaryDexUtils.buildRefProfilePathAsInput(pkgState, dexInfo), PrimaryDexUtils.getExternalProfiles(dexInfo), dmInfo.config().getEnableEmbeddedProfile(), PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID, - Process.SYSTEM_UID, false /* isPublic */)); + Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */)); if (!result.externalProfileErrors().isEmpty()) { Log.e(TAG, "Error occurred when initializing from external profiles: " @@ -764,8 +766,10 @@ public final class ArtManagerLocal { profiles.addAll( PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), pkgState, dexInfo)); - OutputProfile output = PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, - Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */); + // Doesn't support Pre-reboot. + OutputProfile output = + PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID, + Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */); try { return mergeProfilesAndGetFd(profiles, output, List.of(dexInfo.dexPath()), options); @@ -805,7 +809,7 @@ public final class ArtManagerLocal { List profiles = new ArrayList<>(); // System server profiles. - profiles.add(AidlUtils.buildProfilePathForPrimaryRef( + profiles.add(AidlUtils.buildProfilePathForPrimaryRefAsInput( Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY)); for (UserHandle handle : mInjector.getUserManager().getUserHandles(true /* excludeDying */)) { @@ -825,9 +829,10 @@ public final class ArtManagerLocal { } }); + // Doesn't support Pre-reboot. OutputProfile output = AidlUtils.buildOutputProfileForPrimary(Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY, Process.SYSTEM_UID, Process.SYSTEM_UID, - false /* isPublic */); + false /* isPublic */, false /* isPreReboot */); List dexPaths = Arrays.stream(CLASSPATHS_FOR_BOOT_IMAGE_PROFILE) .map(envVar -> Constants.getenv(envVar)) diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java index 04414d0369..7c231c9ce0 100644 --- a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java @@ -168,6 +168,7 @@ public class DexUseManagerLocal { /** Notifies dex use manager that {@link Context#registerReceiver} is ready for use. */ public void systemReady() { + Utils.check(!mInjector.isPreReboot()); // Save the data when the device is being shut down. The receiver is blocking, with a // 10s timeout. mInjector.getContext().registerReceiver(new BroadcastReceiver() { @@ -528,6 +529,7 @@ public class DexUseManagerLocal { } private void save() { + Utils.check(!mInjector.isPreReboot()); var builder = DexUseProto.newBuilder(); int thisRevision; synchronized (mLock) { @@ -561,6 +563,7 @@ public class DexUseManagerLocal { } private void maybeSaveAsync() { + Utils.check(!mInjector.isPreReboot()); mDebouncer.maybeRunAsync(this::save); } @@ -1163,7 +1166,7 @@ public class DexUseManagerLocal { mContext = context; // Call the getters for various dependencies, to ensure correct initialization order. - ArtModuleServiceInitializer.getArtModuleServiceManager(); + GlobalInjector.getInstance().checkArtModuleServiceManager(); getPackageManagerLocal(); } @@ -1199,6 +1202,10 @@ public class DexUseManagerLocal { } } + public boolean isPreReboot() { + return GlobalInjector.getInstance().isPreReboot(); + } + @NonNull private PackageManagerLocal getPackageManagerLocal() { return Objects.requireNonNull( diff --git a/libartservice/service/java/com/android/server/art/Dexopter.java b/libartservice/service/java/com/android/server/art/Dexopter.java index d8d6b3d5c6..69b861527e 100644 --- a/libartservice/service/java/com/android/server/art/Dexopter.java +++ b/libartservice/service/java/com/android/server/art/Dexopter.java @@ -293,7 +293,9 @@ public abstract class Dexopter { profile = null; } } - if (profileMerged) { + // We keep the current profiles in the Pre-reboot Dexopt case, to leave it to + // background dexopt. + if (profileMerged && !mInjector.isPreReboot()) { // Note that this is just an optimization, to reduce the amount of data that // the runtime writes on every profile save. The profile merge result on the // next run won't change regardless of whether the cleanup is done or not @@ -393,8 +395,8 @@ public abstract class Dexopter { private InitProfileResult getOrInitReferenceProfile( @NonNull DexInfoType dexInfo, boolean enableEmbeddedProfile) throws RemoteException { return Utils.getOrInitReferenceProfile(mInjector.getArtd(), dexInfo.dexPath(), - buildRefProfilePath(dexInfo), getExternalProfiles(dexInfo), enableEmbeddedProfile, - buildOutputProfile(dexInfo, true /* isPublic */)); + buildRefProfilePathAsInput(dexInfo), getExternalProfiles(dexInfo), + enableEmbeddedProfile, buildOutputProfile(dexInfo, true /* isPublic */)); } @Nullable @@ -472,7 +474,7 @@ public abstract class Dexopter { dexoptTrigger |= DexoptTrigger.COMPILER_FILTER_IS_SAME; } - ArtifactsPath existingArtifactsPath = AidlUtils.buildArtifactsPath( + ArtifactsPath existingArtifactsPath = AidlUtils.buildArtifactsPathAsInput( target.dexInfo().dexPath(), target.isa(), target.isInDalvikCache()); if (options.needsToBePublic() @@ -493,8 +495,9 @@ public abstract class Dexopter { @NonNull PermissionSettings permissionSettings, @PriorityClass int priorityClass, @NonNull DexoptOptions dexoptOptions, IArtdCancellationSignal artdCancellationSignal) throws RemoteException { - OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(target.dexInfo().dexPath(), - target.isa(), target.isInDalvikCache(), permissionSettings); + OutputArtifacts outputArtifacts = + AidlUtils.buildOutputArtifacts(target.dexInfo().dexPath(), target.isa(), + target.isInDalvikCache(), permissionSettings, mInjector.isPreReboot()); VdexPath inputVdex = getInputVdex(getDexoptNeededResult, target.dexInfo().dexPath(), target.isa()); @@ -527,7 +530,9 @@ public abstract class Dexopter { // images are still usable, technically, they can still be used to improve runtime // performance; if they are no longer usable, they will be deleted by the file GC during the // daily background dexopt job anyway. - if (!result.cancelled) { + // We keep the runtime artifacts in the Pre-reboot Dexopt case because they are still needed + // before the reboot. + if (!result.cancelled && !mInjector.isPreReboot()) { mInjector.getArtd().deleteRuntimeArtifacts(AidlUtils.buildRuntimeArtifactsPath( mPkgState.getPackageName(), target.dexInfo().dexPath(), target.isa())); } @@ -543,11 +548,11 @@ public abstract class Dexopter { } switch (getDexoptNeededResult.artifactsLocation) { case ArtifactsLocation.DALVIK_CACHE: - return VdexPath.artifactsPath( - AidlUtils.buildArtifactsPath(dexPath, isa, true /* isInDalvikCache */)); + return VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( + dexPath, isa, true /* isInDalvikCache */)); case ArtifactsLocation.NEXT_TO_DEX: - return VdexPath.artifactsPath( - AidlUtils.buildArtifactsPath(dexPath, isa, false /* isInDalvikCache */)); + return VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( + dexPath, isa, false /* isInDalvikCache */)); case ArtifactsLocation.DM: // The DM file is passed to dex2oat as a separate flag whenever it exists. return null; @@ -635,7 +640,8 @@ public abstract class Dexopter { @NonNull protected abstract List getAllAbis(@NonNull DexInfoType dexInfo); /** Returns the path to the reference profile of the given dex file. */ - @NonNull protected abstract ProfilePath buildRefProfilePath(@NonNull DexInfoType dexInfo); + @NonNull + protected abstract ProfilePath buildRefProfilePathAsInput(@NonNull DexInfoType dexInfo); /** * Returns the data structure that represents the temporary profile to use during processing. @@ -773,5 +779,9 @@ public abstract class Dexopter { public DexMetadataHelper getDexMetadataHelper() { return new DexMetadataHelper(); } + + public boolean isPreReboot() { + return GlobalInjector.getInstance().isPreReboot(); + } } } diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java b/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java index 4615886625..c37fdbe6e3 100644 --- a/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java +++ b/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java @@ -312,18 +312,20 @@ public class PrimaryDexUtils { } @NonNull - public static ProfilePath buildRefProfilePath( + public static ProfilePath buildRefProfilePathAsInput( @NonNull PackageState pkgState, @NonNull PrimaryDexInfo dexInfo) { String profileName = getProfileName(dexInfo.splitName()); - return AidlUtils.buildProfilePathForPrimaryRef(pkgState.getPackageName(), profileName); + return AidlUtils.buildProfilePathForPrimaryRefAsInput( + pkgState.getPackageName(), profileName); } @NonNull public static OutputProfile buildOutputProfile(@NonNull PackageState pkgState, - @NonNull PrimaryDexInfo dexInfo, int uid, int gid, boolean isPublic) { + @NonNull PrimaryDexInfo dexInfo, int uid, int gid, boolean isPublic, + boolean isPreReboot) { String profileName = getProfileName(dexInfo.splitName()); return AidlUtils.buildOutputProfileForPrimary( - pkgState.getPackageName(), profileName, uid, gid, isPublic); + pkgState.getPackageName(), profileName, uid, gid, isPublic, isPreReboot); } @NonNull diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java index 38fca5f5f0..6ac7988f9e 100644 --- a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java +++ b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java @@ -148,16 +148,16 @@ public class PrimaryDexopter extends Dexopter { @Override @NonNull - protected ProfilePath buildRefProfilePath(@NonNull DetailedPrimaryDexInfo dexInfo) { - return PrimaryDexUtils.buildRefProfilePath(mPkgState, dexInfo); + protected ProfilePath buildRefProfilePathAsInput(@NonNull DetailedPrimaryDexInfo dexInfo) { + return PrimaryDexUtils.buildRefProfilePathAsInput(mPkgState, dexInfo); } @Override @NonNull protected OutputProfile buildOutputProfile( @NonNull DetailedPrimaryDexInfo dexInfo, boolean isPublic) { - return PrimaryDexUtils.buildOutputProfile( - mPkgState, dexInfo, Process.SYSTEM_UID, mSharedGid, isPublic); + return PrimaryDexUtils.buildOutputProfile(mPkgState, dexInfo, Process.SYSTEM_UID, + mSharedGid, isPublic, mInjector.isPreReboot()); } @Override diff --git a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java index 2841ee278b..fe0cdb0091 100644 --- a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java +++ b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java @@ -116,8 +116,8 @@ public class SecondaryDexopter extends Dexopter { @Override @NonNull - protected ProfilePath buildRefProfilePath(@NonNull CheckedSecondaryDexInfo dexInfo) { - return AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath()); + protected ProfilePath buildRefProfilePathAsInput(@NonNull CheckedSecondaryDexInfo dexInfo) { + return AidlUtils.buildProfilePathForSecondaryRefAsInput(dexInfo.dexPath()); } @Override @@ -125,7 +125,8 @@ public class SecondaryDexopter extends Dexopter { protected OutputProfile buildOutputProfile( @NonNull CheckedSecondaryDexInfo dexInfo, boolean isPublic) { int uid = getUid(dexInfo); - return AidlUtils.buildOutputProfileForSecondary(dexInfo.dexPath(), uid, uid, isPublic); + return AidlUtils.buildOutputProfileForSecondary( + dexInfo.dexPath(), uid, uid, isPublic, mInjector.isPreReboot()); } @Override diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java index b7d67eccc3..fa217ab89d 100644 --- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java +++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java @@ -284,17 +284,17 @@ public class ArtManagerLocalTest { assertThat(result.getFreedBytes()) .isEqualTo(6 * DEXOPT_ARTIFACTS_FREED + 4 * RUNTIME_ARTIFACTS_FREED); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/not_found.apk", "arm64", false /* isInDalvikCache */))); verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( @@ -331,13 +331,13 @@ public class ArtManagerLocalTest { assertThat(result.getFreedBytes()) .isEqualTo(6 * DEXOPT_ARTIFACTS_FREED + 4 * RUNTIME_ARTIFACTS_FREED); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "x86_64", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "x86", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "x86_64", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "x86", mIsInDalvikCache))); verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( @@ -350,9 +350,9 @@ public class ArtManagerLocalTest { PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "x86"))); // We assume that the ISA got from `DexUseManagerLocal` is already the translated one. - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/1.apk", "x86_64", false /* isInDalvikCache */))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/not_found.apk", "x86_64", false /* isInDalvikCache */))); // Verify that there are no more calls than the ones above. @@ -462,19 +462,19 @@ public class ArtManagerLocalTest { mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1); verify(mArtd).deleteProfile( - deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"))); + deepEq(AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"))); verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary"))); verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME_1, "primary"))); verify(mArtd).deleteProfile( - deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk"))); + deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"))); verify(mArtd).deleteProfile( deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk"))); - verify(mArtd).deleteProfile(deepEq( - AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/not_found.apk"))); + verify(mArtd).deleteProfile(deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput( + "/data/user/0/foo/not_found.apk"))); verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/not_found.apk"))); } @@ -521,19 +521,19 @@ public class ArtManagerLocalTest { .isSameInstanceAs(result); verify(mArtd).deleteProfile( - deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"))); + deepEq(AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"))); verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary"))); verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME_1, "primary"))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache))); verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( @@ -546,11 +546,11 @@ public class ArtManagerLocalTest { PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); verify(mArtd).deleteProfile( - deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk"))); + deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"))); verify(mArtd).deleteProfile( deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk"))); - verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath( + verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */))); } @@ -836,7 +836,8 @@ public class ArtManagerLocalTest { File tempFile = File.createTempFile("primary", ".prof"); tempFile.deleteOnExit(); - ProfilePath refProfile = AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"); + ProfilePath refProfile = + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); String dexPath = "/somewhere/app/foo/base.apk"; when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(true); @@ -848,7 +849,8 @@ public class ArtManagerLocalTest { 1 /* userId */, PKG_NAME_1, "primary"))), isNull(), deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME_1, "primary", - Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)), + Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, + false /* isPreReboot */)), deepEq(List.of(dexPath)), deepEq(options))) .thenAnswer(invocation -> { try (var writer = new FileWriter(tempFile)) { @@ -880,7 +882,8 @@ public class ArtManagerLocalTest { File tempFileForSnapshot = File.createTempFile("primary", ".prof"); tempFileForSnapshot.deleteOnExit(); - ProfilePath refProfile = AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"); + ProfilePath refProfile = + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); String dexPath = "/somewhere/app/foo/base.apk"; // Simulate that the reference profile doesn't exist. @@ -920,7 +923,8 @@ public class ArtManagerLocalTest { @Test public void testSnapshotAppProfileFromEmbeddedProfile() throws Exception { - ProfilePath refProfile = AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"); + ProfilePath refProfile = + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); String dexPath = "/somewhere/app/foo/base.apk"; // Simulate that the reference profile doesn't exist. @@ -937,7 +941,8 @@ public class ArtManagerLocalTest { @Test public void testSnapshotAppProfileDisableEmbeddedProfile() throws Exception { - ProfilePath refProfile = AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"); + ProfilePath refProfile = + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); String dexPath = "/somewhere/app/foo/base.apk"; // Simulate that the reference profile doesn't exist. @@ -959,7 +964,7 @@ public class ArtManagerLocalTest { @Test public void testSnapshotAppProfileSplit() throws Exception { ProfilePath refProfile = - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "split_0.split"); + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "split_0.split"); String dexPath = "/somewhere/app/foo/split_0.apk"; when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(true); @@ -971,7 +976,8 @@ public class ArtManagerLocalTest { 1 /* userId */, PKG_NAME_1, "split_0.split"))), isNull(), deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME_1, "split_0.split", - Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)), + Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, + false /* isPreReboot */)), deepEq(List.of(dexPath)), any())) .thenReturn(false); @@ -1049,27 +1055,28 @@ public class ArtManagerLocalTest { when(mArtd.mergeProfiles( inAnyOrderDeepEquals( - AidlUtils.buildProfilePathForPrimaryRef("android", "primary"), + AidlUtils.buildProfilePathForPrimaryRefAsInput("android", "primary"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, "android", "primary"), AidlUtils.buildProfilePathForPrimaryCur( 1 /* userId */, "android", "primary"), - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"), + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, PKG_NAME_1, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 1 /* userId */, PKG_NAME_1, "primary"), - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "split_0.split"), + AidlUtils.buildProfilePathForPrimaryRefAsInput( + PKG_NAME_1, "split_0.split"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, PKG_NAME_1, "split_0.split"), AidlUtils.buildProfilePathForPrimaryCur( 1 /* userId */, PKG_NAME_1, "split_0.split"), - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_2, "primary"), + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_2, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, PKG_NAME_2, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 1 /* userId */, PKG_NAME_2, "primary"), - AidlUtils.buildProfilePathForPrimaryRef( + AidlUtils.buildProfilePathForPrimaryRefAsInput( PKG_NAME_HIBERNATING, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, PKG_NAME_HIBERNATING, "primary"), @@ -1077,7 +1084,8 @@ public class ArtManagerLocalTest { 1 /* userId */, PKG_NAME_HIBERNATING, "primary")), isNull(), deepEq(AidlUtils.buildOutputProfileForPrimary("android", "primary", - Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)), + Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, + false /* isPreReboot */)), deepEq(List.of("bcp0", "bcp1", "sscp0", "sscp1", "sssj0", "sssj1")), deepEq(options))) .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`. @@ -1144,25 +1152,27 @@ public class ArtManagerLocalTest { mArtManagerLocal.cleanup(mSnapshot); verify(mArtd).cleanup( - inAnyOrderDeepEquals(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"), + inAnyOrderDeepEquals( + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, PKG_NAME_1, "primary"), AidlUtils.buildProfilePathForPrimaryCur( 1 /* userId */, PKG_NAME_1, "primary"), - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "split_0.split"), + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "split_0.split"), AidlUtils.buildProfilePathForPrimaryCur( 0 /* userId */, PKG_NAME_1, "split_0.split"), AidlUtils.buildProfilePathForPrimaryCur( 1 /* userId */, PKG_NAME_1, "split_0.split"), - AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk"), + AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"), AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk")), - inAnyOrderDeepEquals(AidlUtils.buildArtifactsPath("/somewhere/app/foo/base.apk", - "arm64", false /* isInDalvikCache */), - AidlUtils.buildArtifactsPath( + inAnyOrderDeepEquals( + AidlUtils.buildArtifactsPathAsInput("/somewhere/app/foo/base.apk", "arm64", + false /* isInDalvikCache */), + AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */), - AidlUtils.buildArtifactsPath("/somewhere/app/foo/split_0.apk", "arm64", - true /* isInDalvikCache */)), - inAnyOrderDeepEquals(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( + AidlUtils.buildArtifactsPathAsInput("/somewhere/app/foo/split_0.apk", + "arm64", true /* isInDalvikCache */)), + inAnyOrderDeepEquals(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm", false /* isInDalvikCache */))), inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath( PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"), @@ -1227,14 +1237,14 @@ public class ArtManagerLocalTest { .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any()); // These are counted as TYPE_DEXOPT_ARTIFACT. - doReturn(1l << 0).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPath( + doReturn(1l << 0).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/base.apk", "arm64", false /* isInDalvikCache */))); - doReturn(1l << 1).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPath( + doReturn(1l << 1).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPathAsInput( "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */))); - doReturn(1l << 2).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPath( + doReturn(1l << 2).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm64", true /* isInDalvikCache */))); doReturn(1l << 3).when(mArtd).getVdexFileSize( - deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( + deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( "/somewhere/app/foo/split_0.apk", "arm", false /* isInDalvikCache */)))); doReturn(1l << 4).when(mArtd).getRuntimeArtifactsSize( deepEq(AidlUtils.buildRuntimeArtifactsPath( @@ -1250,11 +1260,11 @@ public class ArtManagerLocalTest { // These are counted as TYPE_REF_PROFILE. doReturn(1l << 6).when(mArtd).getProfileSize( - deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"))); - doReturn(1l << 7).when(mArtd).getProfileSize( - deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "split_0.split"))); + deepEq(AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"))); + doReturn(1l << 7).when(mArtd).getProfileSize(deepEq( + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "split_0.split"))); doReturn(1l << 8).when(mArtd).getProfileSize( - deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk"))); + deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"))); long expectedRefProfileSize = (1l << 6) + (1l << 7) + (1l << 8); int expectedGetProfileSizeCalls = 3; @@ -1278,15 +1288,16 @@ public class ArtManagerLocalTest { // These are counted as TYPE_DEXOPT_ARTIFACT. // Dexopt artifacts of secondary dex files. - doReturn(1l << 12).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPath( - "/data/user/1/foo/1.apk", "arm64", false /* isInDalvikCache */))); + doReturn(1l << 12).when(mArtd).getArtifactsSize( + deepEq(AidlUtils.buildArtifactsPathAsInput( + "/data/user/1/foo/1.apk", "arm64", false /* isInDalvikCache */))); expectedDexoptArtifactSize += (1l << 12); expectedGetArtifactsSizeCalls += 1; // These are counted as TYPE_REF_PROFILE. // Reference profiles of secondary dex files. - doReturn(1l << 13).when(mArtd).getProfileSize( - deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/1/foo/1.apk"))); + doReturn(1l << 13).when(mArtd).getProfileSize(deepEq( + AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/1/foo/1.apk"))); expectedRefProfileSize += (1l << 13); expectedGetProfileSizeCalls += 1; diff --git a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java index 5662090aeb..5bd2edc6d2 100644 --- a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java +++ b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java @@ -166,6 +166,7 @@ public class DexUseManagerTest { .thenAnswer(invocation -> mMockClock.createScheduledExecutor()); lenient().when(mInjector.getContext()).thenReturn(mContext); lenient().when(mInjector.getAllPackageNames()).thenReturn(mPackageStates.keySet()); + lenient().when(mInjector.isPreReboot()).thenReturn(false); mDexUseManager = new DexUseManagerLocal(mInjector); mDexUseManager.systemReady(); @@ -774,6 +775,13 @@ public class DexUseManagerTest { true /* isUsedByOtherApps */, mDefaultFileVisibility)); } + @Test(expected = IllegalStateException.class) + public void testPreRebootNoUpdate() throws Exception { + when(mInjector.isPreReboot()).thenReturn(true); + mDexUseManager.notifyDexContainersLoaded( + mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC")); + } + private AndroidPackage createPackage(String packageName) { AndroidPackage pkg = mock(AndroidPackage.class); lenient().when(pkg.getStorageUuid()).thenReturn(StorageManager.UUID_DEFAULT); diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java index 8f7befb957..30595aafd2 100644 --- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java +++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java @@ -195,6 +195,12 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { params.mExpectedCompilerFilter = "verify"; list.add(params); + params = new Params(); + params.mIsPreReboot = true; + params.mExpectedOutputIsPreReboot = true; + params.mExpectedDeletesRuntimeArtifacts = false; + list.add(params); + return list; } @@ -204,6 +210,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(mParams.mIsSystemUi); lenient().when(mInjector.isLauncherPackage(any())).thenReturn(mParams.mIsLauncher); + lenient().when(mInjector.isPreReboot()).thenReturn(mParams.mIsPreReboot); lenient() .when(SystemProperties.getBoolean(eq("dalvik.vm.always_debuggable"), anyBoolean())) @@ -285,7 +292,8 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { 400 /* cpuTimeMs */, 30000 /* sizeBytes */, 32000 /* sizeBeforeBytes */)) .when(mArtd) .dexopt(deepEq(buildOutputArtifacts("/somewhere/app/foo/base.apk", "arm64", - mParams.mIsInDalvikCache, permissionSettings)), + mParams.mIsInDalvikCache, permissionSettings, + mParams.mExpectedOutputIsPreReboot)), eq("/somewhere/app/foo/base.apk"), eq("arm64"), eq("PCL[]"), eq(mParams.mExpectedCompilerFilter), any() /* profile */, isNull() /* inputVdex */, isNull() /* dmFile */, @@ -299,7 +307,8 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { doThrow(ServiceSpecificException.class) .when(mArtd) .dexopt(deepEq(buildOutputArtifacts("/somewhere/app/foo/base.apk", "arm", - mParams.mIsInDalvikCache, permissionSettings)), + mParams.mIsInDalvikCache, permissionSettings, + mParams.mExpectedOutputIsPreReboot)), eq("/somewhere/app/foo/base.apk"), eq("arm"), eq("PCL[]"), eq(mParams.mExpectedCompilerFilter), any() /* profile */, isNull() /* inputVdex */, isNull() /* dmFile */, @@ -320,18 +329,23 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { 200 /* cpuTimeMs */, 10000 /* sizeBytes */, 0 /* sizeBeforeBytes */)) .when(mArtd) .dexopt(deepEq(buildOutputArtifacts("/somewhere/app/foo/split_0.apk", "arm", - mParams.mIsInDalvikCache, permissionSettings)), + mParams.mIsInDalvikCache, permissionSettings, + mParams.mExpectedOutputIsPreReboot)), eq("/somewhere/app/foo/split_0.apk"), eq("arm"), eq("PCL[base.apk]"), eq(mParams.mExpectedCompilerFilter), any() /* profile */, isNull() /* inputVdex */, isNull() /* dmFile */, eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any()); - // Only delete runtime artifacts for successful dexopt operations, namely the first one and - // the fourth one. - doReturn(1l).when(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( - PKG_NAME, "/somewhere/app/foo/base.apk", "arm64"))); - doReturn(1l).when(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( - PKG_NAME, "/somewhere/app/foo/split_0.apk", "arm"))); + if (mParams.mExpectedDeletesRuntimeArtifacts) { + // Only delete runtime artifacts for successful dexopt operations, namely the first one + // and the fourth one. + doReturn(1l).when(mArtd).deleteRuntimeArtifacts( + deepEq(AidlUtils.buildRuntimeArtifactsPath( + PKG_NAME, "/somewhere/app/foo/base.apk", "arm64"))); + doReturn(1l).when(mArtd).deleteRuntimeArtifacts( + deepEq(AidlUtils.buildRuntimeArtifactsPath( + PKG_NAME, "/somewhere/app/foo/split_0.apk", "arm"))); + } assertThat(mPrimaryDexopter.dexopt()) .comparingElementsUsing(TestingUtils.deepEquality()) @@ -360,6 +374,10 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { verify(mArtd, times(3)) .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), any()); + + if (!mParams.mExpectedDeletesRuntimeArtifacts) { + verify(mArtd, times(0)).deleteRuntimeArtifacts(any()); + } } private static class Params { @@ -379,6 +397,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { public boolean mShouldDowngrade = false; public boolean mSkipIfStorageLow = false; public boolean mIgnoreProfile = false; + public boolean mIsPreReboot = false; // System properties. public boolean mAlwaysDebuggable = false; @@ -390,6 +409,8 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; public boolean mExpectedIsDebuggable = false; public boolean mExpectedIsHiddenApiPolicyEnabled = true; + public boolean mExpectedOutputIsPreReboot = false; + public boolean mExpectedDeletesRuntimeArtifacts = true; public String toString() { return String.format("isInDalvikCache=%b," @@ -405,19 +426,24 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { + "shouldDowngrade=%b," + "skipIfStorageLow=%b," + "ignoreProfile=%b," + + "isPreReboot=%b," + "alwaysDebuggable=%b" + " => " + "expectedCallbackInputCompilerFilter=%s," + "expectedCompilerFilter=%s," + "expectedDexoptTrigger=%d," + "expectedIsDebuggable=%b," - + "expectedIsHiddenApiPolicyEnabled=%b", + + "expectedIsHiddenApiPolicyEnabled=%b," + + "expectedOutputIsPreReboot=%b," + + "expectedDeleteRuntimeArtifacts=%b", mIsInDalvikCache, mHiddenApiEnforcementPolicy, mIsVmSafeMode, mIsDebuggable, mIsSystemUi, mIsLauncher, mIsUseEmbeddedDex, mRequestedCompilerFilter, mCallbackReturnedCompilerFilter, mForce, mShouldDowngrade, mSkipIfStorageLow, - mIgnoreProfile, mAlwaysDebuggable, mExpectedCallbackInputCompilerFilter, - mExpectedCompilerFilter, mExpectedDexoptTrigger, mExpectedIsDebuggable, - mExpectedIsHiddenApiPolicyEnabled); + mIgnoreProfile, mIsPreReboot, mAlwaysDebuggable, + mExpectedCallbackInputCompilerFilter, mExpectedCompilerFilter, + mExpectedDexoptTrigger, mExpectedIsDebuggable, + mExpectedIsHiddenApiPolicyEnabled, mExpectedOutputIsPreReboot, + mExpectedDeletesRuntimeArtifacts); } } } diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java index a58add5f30..beb8c1eaf4 100644 --- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java +++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java @@ -71,18 +71,20 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { private final String mDexPath = "/somewhere/app/foo/base.apk"; private final String mDmPath = "/somewhere/app/foo/base.dm"; private final ProfilePath mRefProfile = - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"); + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME, "primary"); private final ProfilePath mPrebuiltProfile = AidlUtils.buildProfilePathForPrebuilt(mDexPath); private final ProfilePath mDmProfile = AidlUtils.buildProfilePathForDm(mDexPath); private final DexMetadataPath mDmFile = AidlUtils.buildDexMetadataPath(mDexPath); - private final OutputProfile mPublicOutputProfile = AidlUtils.buildOutputProfileForPrimary( - PKG_NAME, "primary", Process.SYSTEM_UID, SHARED_GID, true /* isOtherReadable */); - private final OutputProfile mPrivateOutputProfile = AidlUtils.buildOutputProfileForPrimary( - PKG_NAME, "primary", Process.SYSTEM_UID, SHARED_GID, false /* isOtherReadable */); + private final OutputProfile mPublicOutputProfile = + AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "primary", Process.SYSTEM_UID, + SHARED_GID, true /* isOtherReadable */, false /* isPreReboot */); + private final OutputProfile mPrivateOutputProfile = + AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "primary", Process.SYSTEM_UID, + SHARED_GID, false /* isOtherReadable */, false /* isPreReboot */); private final String mSplit0DexPath = "/somewhere/app/foo/split_0.apk"; private final ProfilePath mSplit0RefProfile = - AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "split_0.split"); + AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME, "split_0.split"); private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; @@ -163,7 +165,7 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { doReturn(mArtdDexoptResult) .when(mArtd) .dexopt(any(), eq(mDexPath), eq("arm"), any(), any(), any(), - deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( + deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( mDexPath, "arm", true /* isInDalvikCache */))), any(), anyInt(), any(), any()); @@ -174,7 +176,7 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { doReturn(mArtdDexoptResult) .when(mArtd) .dexopt(any(), eq(mSplit0DexPath), eq("arm64"), any(), any(), any(), - deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( + deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( mSplit0DexPath, "arm64", false /* isInDalvikCache */))), any(), anyInt(), any(), any()); @@ -303,8 +305,7 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { verifyEmbeddedProfileNotUsed(mDexPath); } - @Test - public void testDexoptMergesProfiles() throws Exception { + private void checkDexoptMergesProfiles() throws Exception { setPackageInstalledForUserIds(0, 2); when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(true); @@ -340,13 +341,29 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { false /* isOtherReadable */); inOrder.verify(mArtd).commitTmpProfile(deepEq(mPrivateOutputProfile.profilePath)); + } - inOrder.verify(mArtd).deleteProfile(deepEq( + @Test + public void testDexoptMergesProfiles() throws Exception { + checkDexoptMergesProfiles(); + + verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary"))); - inOrder.verify(mArtd).deleteProfile(deepEq( + verify(mArtd).deleteProfile(deepEq( AidlUtils.buildProfilePathForPrimaryCur(2 /* userId */, PKG_NAME, "primary"))); } + @Test + public void testDexoptMergesProfilesPreReboot() throws Exception { + when(mInjector.isPreReboot()).thenReturn(true); + mPublicOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; + mPrivateOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; + + checkDexoptMergesProfiles(); + + verify(mArtd, never()).deleteProfile(any()); + } + @Test public void testDexoptMergesProfilesMergeFailed() throws Exception { setPackageInstalledForUserIds(0, 2); @@ -397,8 +414,7 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { mPrimaryDexopter.dexopt(); } - @Test - public void testDexoptUsesDmProfile() throws Exception { + private void checkDexoptUsesDmProfile() throws Exception { makeProfileUsable(mDmProfile); makeEmbeddedProfileUsable(mDexPath); @@ -420,6 +436,20 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { verifyEmbeddedProfileNotUsed(mDexPath); } + @Test + public void testDexoptUsesDmProfile() throws Exception { + checkDexoptUsesDmProfile(); + } + + @Test + public void testDexoptUsesDmProfilePreReboot() throws Exception { + when(mInjector.isPreReboot()).thenReturn(true); + mPublicOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; + mPrivateOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; + + checkDexoptUsesDmProfile(); + } + private void checkDexoptUsesEmbeddedProfile() throws Exception { makeEmbeddedProfileUsable(mDexPath); @@ -500,6 +530,15 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase { verifyEmbeddedProfileNotUsed(mDexPath); } + @Test + public void testDexoptUsesEmbeddedProfilePreReboot() throws Exception { + when(mInjector.isPreReboot()).thenReturn(true); + mPublicOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; + mPrivateOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; + + checkDexoptUsesEmbeddedProfile(); + } + @Test public void testDexoptExternalProfileErrors() throws Exception { // Having no profile should not be reported. diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java index 5e26835da2..3105e4e51b 100644 --- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java +++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java @@ -92,6 +92,7 @@ public class PrimaryDexopterTestBase { lenient().when(mInjector.getArtVersion()).thenReturn(ART_VERSION); lenient().when(mInjector.getConfig()).thenReturn(mConfig); lenient().when(mInjector.getDexMetadataHelper()).thenReturn(mDexMetadataHelper); + lenient().when(mInjector.isPreReboot()).thenReturn(false); lenient() .when(SystemProperties.get("dalvik.vm.systemuicompilerfilter")) diff --git a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java index 13ff28a908..07dd07dae7 100644 --- a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java +++ b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java @@ -81,12 +81,16 @@ public class SecondaryDexopterTest { .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX) .build(); - private final ProfilePath mDex1RefProfile = AidlUtils.buildProfilePathForSecondaryRef(DEX_1); + private final ProfilePath mDex1RefProfile = + AidlUtils.buildProfilePathForSecondaryRefAsInput(DEX_1); private final ProfilePath mDex1CurProfile = AidlUtils.buildProfilePathForSecondaryCur(DEX_1); - private final ProfilePath mDex2RefProfile = AidlUtils.buildProfilePathForSecondaryRef(DEX_2); - private final ProfilePath mDex3RefProfile = AidlUtils.buildProfilePathForSecondaryRef(DEX_3); + private final ProfilePath mDex2RefProfile = + AidlUtils.buildProfilePathForSecondaryRefAsInput(DEX_2); + private final ProfilePath mDex3RefProfile = + AidlUtils.buildProfilePathForSecondaryRefAsInput(DEX_3); private final OutputProfile mDex1PrivateOutputProfile = - AidlUtils.buildOutputProfileForSecondary(DEX_1, UID, UID, false /* isOtherReadable */); + AidlUtils.buildOutputProfileForSecondary( + DEX_1, UID, UID, false /* isOtherReadable */, false /* isPreReboot */); private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; @@ -322,8 +326,8 @@ public class SecondaryDexopterTest { private void checkDexoptWithPrivateProfile(IArtd artd, String dexPath, String isa, ProfilePath profile, String classLoaderContext) throws Exception { PermissionSettings permissionSettings = buildPermissionSettings(false /* isPublic */); - OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts( - dexPath, isa, false /* isInDalvikCache */, permissionSettings); + OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(dexPath, isa, + false /* isInDalvikCache */, permissionSettings, false /* isPreReboot */); artd.dexopt(deepEq(outputArtifacts), eq(dexPath), eq(isa), eq(classLoaderContext), eq("speed-profile"), deepEq(profile), any(), isNull() /* dmFile */, anyInt(), argThat(dexoptOptions -> dexoptOptions.generateAppImage == true), any()); @@ -332,8 +336,8 @@ public class SecondaryDexopterTest { private void checkDexoptWithNoProfile(IArtd artd, String dexPath, String isa, String compilerFilter, String classLoaderContext, boolean isPublic) throws Exception { PermissionSettings permissionSettings = buildPermissionSettings(isPublic); - OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts( - dexPath, isa, false /* isInDalvikCache */, permissionSettings); + OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(dexPath, isa, + false /* isInDalvikCache */, permissionSettings, false /* isPreReboot */); artd.dexopt(deepEq(outputArtifacts), eq(dexPath), eq(isa), eq(classLoaderContext), eq(compilerFilter), isNull(), any(), isNull() /* dmFile */, anyInt(), argThat(dexoptOptions -> dexoptOptions.generateAppImage == false), any()); -- cgit v1.2.3