summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Android Build Coastguard Worker <android-build-coastguard-worker@google.com> 2025-03-21 20:05:44 -0700
committer Android Build Coastguard Worker <android-build-coastguard-worker@google.com> 2025-03-21 20:05:44 -0700
commitb61923778ebb23c5b282ff67b509493a96593419 (patch)
treed59971c5123aae888aa7de90665c9a3d3fc9a4d8
parentb6f024a322376684cb357b48767353f18fb39dfc (diff)
parentb73b25fdd947faef7856ba9277581553a96f8675 (diff)
Snap for 13256841 from b73b25fdd947faef7856ba9277581553a96f8675 to 25Q2-release
Change-Id: Ib9d9918eb8a5bbf74e53ba61aaf7a2121c45a357
-rw-r--r--artd/artd.cc129
-rw-r--r--artd/artd.h15
-rw-r--r--artd/artd_test.cc225
-rw-r--r--artd/binder/com/android/server/art/ArtifactsLocation.aidl11
-rw-r--r--artd/binder/com/android/server/art/IArtd.aidl30
-rw-r--r--artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl30
-rw-r--r--artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl39
-rw-r--r--artd/path_utils.cc59
-rw-r--r--artd/path_utils.h10
-rw-r--r--artd/path_utils_test.cc15
-rw-r--r--compiler/optimizing/instruction_simplifier_test.cc8
-rw-r--r--compiler/optimizing/load_store_elimination_test.cc18
-rw-r--r--compiler/optimizing/reference_type_propagation_test.cc28
-rw-r--r--compiler/utils/assembler_test_base.h2
-rw-r--r--dex2oat/linker/elf_writer_test.cc9
-rw-r--r--dex2oat/linker/multi_oat_relative_patcher_test.cc2
-rw-r--r--dex2oat/linker/relative_patcher_test.h2
-rw-r--r--libartbase/Android.bp6
-rw-r--r--libartbase/base/arena_allocator_test.cc2
-rw-r--r--libartbase/base/common_art_test.cc137
-rw-r--r--libartbase/base/common_art_test.h43
-rw-r--r--libartbase/base/file_utils.cc9
-rw-r--r--libartbase/base/file_utils.h7
-rw-r--r--libartbase/base/hash_set_test.cc2
-rw-r--r--libartbase/base/intrusive_forward_list_test.cc2
-rw-r--r--libartbase/base/logging_test.cc2
-rw-r--r--libartbase/base/mem_map_test.cc14
-rw-r--r--libartbase/base/metrics/metrics_test.cc2
-rw-r--r--libartbase/base/testing.cc144
-rw-r--r--libartbase/base/testing.h31
-rw-r--r--libartbase/base/time_utils.h9
-rw-r--r--libartbase/base/unix_file/fd_file_test.cc8
-rw-r--r--libartbase/base/unix_file/random_access_file_test.h2
-rw-r--r--libartbase/base/utils_test.cc10
-rw-r--r--libartpalette/Android.bp33
-rw-r--r--libartpalette/apex/palette_test.cc27
-rw-r--r--libartservice/service/java/com/android/server/art/AidlUtils.java27
-rw-r--r--libartservice/service/java/com/android/server/art/ArtFileManager.java105
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagerLocal.java30
-rw-r--r--libartservice/service/java/com/android/server/art/ArtShellCommand.java19
-rw-r--r--libartservice/service/java/com/android/server/art/DexUseManagerLocal.java23
-rw-r--r--libartservice/service/java/com/android/server/art/Dexopter.java37
-rw-r--r--libartservice/service/java/com/android/server/art/PrimaryDexopter.java39
-rw-r--r--libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java132
-rw-r--r--libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java33
-rw-r--r--libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java22
-rw-r--r--libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java126
-rw-r--r--libartservice/service/native/service_test.cc4
-rw-r--r--libarttools/art_exec_test.cc4
-rw-r--r--libarttools/cmdline_builder_test.cc2
-rw-r--r--libarttools/system_properties_test.cc2
-rw-r--r--libdexfile/dex/code_item_accessors_test.cc2
-rw-r--r--libdexfile/dex/descriptors_names_test.cc2
-rw-r--r--libdexfile/dex/dex_file_loader_test.cc2
-rw-r--r--libdexfile/dex/dex_file_verifier_test.cc2
-rw-r--r--libdexfile/dex/type_lookup_table_test.cc8
-rw-r--r--libdexfile/dex/utf_test.cc2
-rw-r--r--libnativebridge/tests/NativeBridgeTest.h2
-rw-r--r--libnativeloader/native_loader_api_test.cpp2
-rw-r--r--libnativeloader/native_loader_test.cpp14
-rw-r--r--oatdump/oatdump_test.cc2
-rw-r--r--oatdump/oatdump_test.h2
-rw-r--r--odrefresh/odr_metrics_record_test.cc2
-rw-r--r--runtime/Android.bp2
-rw-r--r--runtime/arch/memcmp16_test.cc3
-rw-r--r--runtime/class_linker.cc10
-rw-r--r--runtime/common_runtime_test.h4
-rw-r--r--runtime/dex2oat_environment_test.h52
-rw-r--r--runtime/dexopt_test.cc32
-rw-r--r--runtime/dexopt_test.h10
-rw-r--r--runtime/exec_utils_test.cc4
-rw-r--r--runtime/gc/space/image_space.cc40
-rw-r--r--runtime/gc/space/image_space.h10
-rw-r--r--runtime/gc/space/space_create_test.cc4
-rw-r--r--runtime/jit/jit_memory_region_test.cc2
-rw-r--r--runtime/metrics/reporter_test.cc2
-rw-r--r--runtime/oat/oat.cc2
-rw-r--r--runtime/oat/oat.h9
-rw-r--r--runtime/oat/oat_file.cc132
-rw-r--r--runtime/oat/oat_file.h13
-rw-r--r--runtime/oat/oat_file_assistant.cc47
-rw-r--r--runtime/oat/oat_file_assistant.h36
-rw-r--r--runtime/oat/oat_file_assistant_context.h2
-rw-r--r--runtime/oat/oat_file_assistant_test.cc311
-rw-r--r--runtime/oat/sdc_file.cc114
-rw-r--r--runtime/oat/sdc_file.h102
-rw-r--r--runtime/oat/sdc_file_test.cc169
-rw-r--r--sigchainlib/sigchain_test.cc12
-rw-r--r--test/Android.bp4
-rwxr-xr-xtest/default_run.py12
-rw-r--r--test/generate-boot-image/generate-boot-image.cc3
-rw-r--r--test/standalone_test_lib_check.cc2
-rw-r--r--tools/libcore_gcstress_debug_failures.txt7
-rw-r--r--tools/luci/config/generated/cr-buildbucket.cfg4
-rw-r--r--tools/luci/config/generated/luci-logdog.cfg2
-rw-r--r--tools/luci/config/generated/luci-scheduler.cfg2
-rw-r--r--tools/luci/config/generated/project.cfg4
-rw-r--r--tools/luci/config/generated/realms.cfg10
-rwxr-xr-xtools/luci/config/main.star5
99 files changed, 2331 insertions, 617 deletions
diff --git a/artd/artd.cc b/artd/artd.cc
index d781750306..144b783a29 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -84,6 +84,7 @@
#include "fstab/fstab.h"
#include "oat/oat_file_assistant.h"
#include "oat/oat_file_assistant_context.h"
+#include "oat/sdc_file.h"
#include "odrefresh/odrefresh.h"
#include "path_utils.h"
#include "profman/profman_result.h"
@@ -115,9 +116,11 @@ using ::aidl::com::android::server::art::IArtdNotification;
using ::aidl::com::android::server::art::MergeProfileOptions;
using ::aidl::com::android::server::art::OutputArtifacts;
using ::aidl::com::android::server::art::OutputProfile;
+using ::aidl::com::android::server::art::OutputSecureDexMetadataCompanion;
using ::aidl::com::android::server::art::PriorityClass;
using ::aidl::com::android::server::art::ProfilePath;
using ::aidl::com::android::server::art::RuntimeArtifactsPath;
+using ::aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths;
using ::aidl::com::android::server::art::VdexPath;
using ::android::base::Basename;
using ::android::base::Dirname;
@@ -235,6 +238,10 @@ ArtifactsLocation ArtifactsLocationToAidl(OatFileAssistant::Location location) {
return ArtifactsLocation::NEXT_TO_DEX;
case OatFileAssistant::Location::kLocationDm:
return ArtifactsLocation::DM;
+ case OatFileAssistant::Location::kLocationSdmOat:
+ return ArtifactsLocation::SDM_DALVIK_CACHE;
+ case OatFileAssistant::Location::kLocationSdmOdex:
+ return ArtifactsLocation::SDM_NEXT_TO_DEX;
// No default. All cases should be explicitly handled, or the compilation will fail.
}
// This should never happen. Just in case we get a non-enumerator value.
@@ -269,20 +276,18 @@ Result<void> PrepareArtifactsDir(const std::string& path, const FsPermission& fs
return {};
}
-Result<void> PrepareArtifactsDirs(const OutputArtifacts& output_artifacts,
+Result<void> PrepareArtifactsDirs(const std::string& dex_path,
+ const std::string& isa_str,
+ const FsPermission& dir_fs_permission,
/*out*/ std::string* oat_dir_path) {
- if (output_artifacts.artifactsPath.isInDalvikCache) {
- return {};
- }
-
std::filesystem::path oat_path(
- OR_RETURN(BuildArtifactsPath(output_artifacts.artifactsPath)).oat_path);
+ OR_RETURN(BuildOatPath(dex_path, isa_str, /*is_in_dalvik_cache=*/false)));
std::filesystem::path isa_dir = oat_path.parent_path();
std::filesystem::path oat_dir = isa_dir.parent_path();
DCHECK_EQ(oat_dir.filename(), "oat");
- OR_RETURN(PrepareArtifactsDir(oat_dir, output_artifacts.permissionSettings.dirFsPermission));
- OR_RETURN(PrepareArtifactsDir(isa_dir, output_artifacts.permissionSettings.dirFsPermission));
+ OR_RETURN(PrepareArtifactsDir(oat_dir, dir_fs_permission));
+ OR_RETURN(PrepareArtifactsDir(isa_dir, dir_fs_permission));
*oat_dir_path = oat_dir;
return {};
}
@@ -992,6 +997,64 @@ ndk::ScopedAStatus Artd::getDexoptNeeded(const std::string& in_dexFile,
return ScopedAStatus::ok();
}
+ndk::ScopedAStatus Artd::maybeCreateSdc(const OutputSecureDexMetadataCompanion& in_outputSdc) {
+ RETURN_FATAL_IF_PRE_REBOOT(options_);
+
+ if (in_outputSdc.permissionSettings.seContext.has_value()) {
+ // SDM files are for primary dex files.
+ return Fatal("'seContext' must be null");
+ }
+
+ std::string sdm_path = OR_RETURN_FATAL(BuildSdmPath(in_outputSdc.sdcPath));
+ std::string sdc_path = OR_RETURN_FATAL(BuildSdcPath(in_outputSdc.sdcPath));
+
+ Result<std::unique_ptr<File>> sdm_file = OpenFileForReading(sdm_path);
+ if (!sdm_file.ok()) {
+ if (sdm_file.error().code() == ENOENT) {
+ // No SDM file found. That's typical.
+ return ScopedAStatus::ok();
+ }
+ return NonFatal(sdm_file.error().message());
+ }
+ struct stat sdm_st = OR_RETURN_NON_FATAL(Fstat(*sdm_file.value()));
+
+ std::string error_msg;
+ std::unique_ptr<SdcReader> sdc_reader = SdcReader::Load(sdc_path, &error_msg);
+ if (sdc_reader != nullptr && sdc_reader->GetSdmTimestampNs() == TimeSpecToNs(sdm_st.st_mtim)) {
+ // Already has an SDC file for the SDM file.
+ return ScopedAStatus::ok();
+ }
+
+ std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache.
+ if (!in_outputSdc.sdcPath.isInDalvikCache) {
+ OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputSdc.sdcPath.dexPath,
+ in_outputSdc.sdcPath.isa,
+ in_outputSdc.permissionSettings.dirFsPermission,
+ &oat_dir_path));
+
+ // Unlike the two `restorecon_` calls in `dexopt`, we only need one restorecon here because SDM
+ // files are for primary dex files, whose oat directory doesn't have an MLS label.
+ OR_RETURN_NON_FATAL(restorecon_(oat_dir_path, /*se_context=*/std::nullopt, /*recurse=*/true));
+ }
+
+ OatFileAssistantContext* ofa_context = OR_RETURN_NON_FATAL(GetOatFileAssistantContext());
+
+ std::unique_ptr<NewFile> sdc_file = OR_RETURN_NON_FATAL(
+ NewFile::Create(sdc_path, in_outputSdc.permissionSettings.fileFsPermission));
+ SdcWriter writer(File(DupCloexec(sdc_file->Fd()), sdc_file->TempPath(), /*check_usage=*/true));
+
+ writer.SetSdmTimestampNs(TimeSpecToNs(sdm_st.st_mtim));
+ writer.SetApexVersions(ofa_context->GetApexVersions());
+
+ if (!writer.Save(&error_msg)) {
+ return NonFatal(error_msg);
+ }
+
+ OR_RETURN_NON_FATAL(sdc_file->CommitOrAbandon());
+
+ return ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Artd::dexopt(
const OutputArtifacts& in_outputArtifacts,
const std::string& in_dexFile,
@@ -1029,12 +1092,15 @@ ndk::ScopedAStatus Artd::dexopt(
}
std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache.
- OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts, &oat_dir_path));
-
- // First-round restorecon. artd doesn't have the permission to create files with the
- // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will
- // inherit `dalvikcache_data_file` rather than `apk_data_file`.
if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
+ OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts.artifactsPath.dexPath,
+ in_outputArtifacts.artifactsPath.isa,
+ in_outputArtifacts.permissionSettings.dirFsPermission,
+ &oat_dir_path));
+
+ // First-round restorecon. artd doesn't have the permission to create files with the
+ // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will
+ // inherit `dalvikcache_data_file` rather than `apk_data_file`.
OR_RETURN_NON_FATAL(restorecon_(
oat_dir_path, in_outputArtifacts.permissionSettings.seContext, /*recurse=*/true));
}
@@ -1274,12 +1340,14 @@ ScopedAStatus Artd::createCancellationSignal(
return ScopedAStatus::ok();
}
-ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep,
- const std::vector<ArtifactsPath>& in_artifactsToKeep,
- const std::vector<VdexPath>& in_vdexFilesToKeep,
- const std::vector<RuntimeArtifactsPath>& in_runtimeArtifactsToKeep,
- bool in_keepPreRebootStagedFiles,
- int64_t* _aidl_return) {
+ScopedAStatus Artd::cleanup(
+ const std::vector<ProfilePath>& in_profilesToKeep,
+ const std::vector<ArtifactsPath>& in_artifactsToKeep,
+ const std::vector<VdexPath>& in_vdexFilesToKeep,
+ const std::vector<SecureDexMetadataWithCompanionPaths>& in_SdmSdcFilesToKeep,
+ const std::vector<RuntimeArtifactsPath>& in_runtimeArtifactsToKeep,
+ bool in_keepPreRebootStagedFiles,
+ int64_t* _aidl_return) {
RETURN_FATAL_IF_PRE_REBOOT(options_);
std::unordered_set<std::string> files_to_keep;
for (const ProfilePath& profile : in_profilesToKeep) {
@@ -1297,6 +1365,10 @@ ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep,
RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(vdex, "vdexFilesToKeep");
files_to_keep.insert(OR_RETURN_FATAL(BuildVdexPath(vdex)));
}
+ for (const SecureDexMetadataWithCompanionPaths& sdm_sdc : in_SdmSdcFilesToKeep) {
+ files_to_keep.insert(OR_RETURN_FATAL(BuildSdmPath(sdm_sdc)));
+ files_to_keep.insert(OR_RETURN_FATAL(BuildSdcPath(sdm_sdc)));
+ }
std::string android_data = OR_RETURN_NON_FATAL(GetAndroidDataOrError());
std::string android_expand = OR_RETURN_NON_FATAL(GetAndroidExpandOrError());
for (const RuntimeArtifactsPath& runtime_image_path : in_runtimeArtifactsToKeep) {
@@ -1360,6 +1432,17 @@ ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_r
return NonFatal(ART_FORMAT("Fstab entries not found for '{}'", in_dexFile));
}
+ScopedAStatus Artd::deleteSdmSdcFiles(const SecureDexMetadataWithCompanionPaths& in_SdmSdcPaths,
+ int64_t* _aidl_return) {
+ RETURN_FATAL_IF_PRE_REBOOT(options_);
+
+ std::string sdm_path = OR_RETURN_FATAL(BuildSdmPath(in_SdmSdcPaths));
+ std::string sdc_path = OR_RETURN_FATAL(BuildSdcPath(in_SdmSdcPaths));
+
+ *_aidl_return = GetSizeAndDeleteFile(sdm_path) + GetSizeAndDeleteFile(sdc_path);
+ return ScopedAStatus::ok();
+}
+
ScopedAStatus Artd::deleteRuntimeArtifacts(const RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) {
RETURN_FATAL_IF_PRE_REBOOT(options_);
@@ -1393,6 +1476,14 @@ ScopedAStatus Artd::getVdexFileSize(const VdexPath& in_vdexPath, int64_t* _aidl_
return ScopedAStatus::ok();
}
+ndk::ScopedAStatus Artd::getSdmFileSize(const SecureDexMetadataWithCompanionPaths& in_sdmPath,
+ int64_t* _aidl_return) {
+ RETURN_FATAL_IF_PRE_REBOOT(options_);
+ std::string sdm_path = OR_RETURN_FATAL(BuildSdmPath(in_sdmPath));
+ *_aidl_return = GetSize(sdm_path).value_or(0);
+ return ScopedAStatus::ok();
+}
+
ScopedAStatus Artd::getRuntimeArtifactsSize(const RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) {
RETURN_FATAL_IF_PRE_REBOOT(options_);
diff --git a/artd/artd.h b/artd/artd.h
index 7426901688..d48a209b0d 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -38,6 +38,7 @@
#include "aidl/com/android/server/art/BnArtd.h"
#include "aidl/com/android/server/art/BnArtdCancellationSignal.h"
#include "aidl/com/android/server/art/BnArtdNotification.h"
+#include "aidl/com/android/server/art/SecureDexMetadataWithCompanionPaths.h"
#include "android-base/result.h"
#include "android-base/thread_annotations.h"
#include "android-base/unique_fd.h"
@@ -217,6 +218,10 @@ class Artd : public aidl::com::android::server::art::BnArtd {
int32_t in_dexoptTrigger,
aidl::com::android::server::art::GetDexoptNeededResult* _aidl_return) override;
+ ndk::ScopedAStatus maybeCreateSdc(
+ const aidl::com::android::server::art::OutputSecureDexMetadataCompanion& in_outputSdc)
+ override;
+
ndk::ScopedAStatus dexopt(
const aidl::com::android::server::art::OutputArtifacts& in_outputArtifacts,
const std::string& in_dexFile,
@@ -240,6 +245,8 @@ class Artd : public aidl::com::android::server::art::BnArtd {
const std::vector<aidl::com::android::server::art::ProfilePath>& in_profilesToKeep,
const std::vector<aidl::com::android::server::art::ArtifactsPath>& in_artifactsToKeep,
const std::vector<aidl::com::android::server::art::VdexPath>& in_vdexFilesToKeep,
+ const std::vector<aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths>&
+ in_SdmSdcFilesToKeep,
const std::vector<aidl::com::android::server::art::RuntimeArtifactsPath>&
in_runtimeArtifactsToKeep,
bool in_keepPreRebootStagedFiles,
@@ -249,6 +256,10 @@ class Artd : public aidl::com::android::server::art::BnArtd {
ndk::ScopedAStatus isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) override;
+ ndk::ScopedAStatus deleteSdmSdcFiles(
+ const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& in_sdmSdcPaths,
+ int64_t* _aidl_return) override;
+
ndk::ScopedAStatus deleteRuntimeArtifacts(
const aidl::com::android::server::art::RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) override;
@@ -260,6 +271,10 @@ class Artd : public aidl::com::android::server::art::BnArtd {
ndk::ScopedAStatus getVdexFileSize(const aidl::com::android::server::art::VdexPath& in_vdexPath,
int64_t* _aidl_return) override;
+ ndk::ScopedAStatus getSdmFileSize(
+ const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& in_sdmPath,
+ int64_t* _aidl_return) override;
+
ndk::ScopedAStatus getRuntimeArtifactsSize(
const aidl::com::android::server::art::RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) override;
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
index 76167c58fb..f6eeda7e57 100644
--- a/artd/artd_test.cc
+++ b/artd/artd_test.cc
@@ -43,6 +43,7 @@
#include "aidl/com/android/server/art/ArtConstants.h"
#include "aidl/com/android/server/art/BnArtd.h"
+#include "aidl/com/android/server/art/OutputArtifacts.h"
#include "android-base/collections.h"
#include "android-base/errors.h"
#include "android-base/file.h"
@@ -58,6 +59,7 @@
#include "base/common_art_test.h"
#include "base/macros.h"
#include "base/pidfd.h"
+#include "base/time_utils.h"
#include "exec_utils.h"
#include "file_utils.h"
#include "gmock/gmock.h"
@@ -94,6 +96,7 @@ using ::aidl::com::android::server::art::OutputProfile;
using ::aidl::com::android::server::art::PriorityClass;
using ::aidl::com::android::server::art::ProfilePath;
using ::aidl::com::android::server::art::RuntimeArtifactsPath;
+using ::aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths;
using ::aidl::com::android::server::art::VdexPath;
using ::android::base::Append;
using ::android::base::Dirname;
@@ -130,10 +133,12 @@ using ::testing::Property;
using ::testing::ResultOf;
using ::testing::Return;
using ::testing::SetArgPointee;
+using ::testing::StartsWith;
using ::testing::StrEq;
using ::testing::UnorderedElementsAreArray;
using ::testing::WithArg;
+using PermissionSettings = OutputArtifacts::PermissionSettings;
using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath;
using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath;
using TmpProfilePath = ProfilePath::TmpProfilePath;
@@ -153,10 +158,10 @@ ScopeGuard<std::function<void()>> ScopedSetLogger(android::base::LogFunction&& l
});
}
-void CheckContent(const std::string& path, const std::string& expected_content) {
+void CheckContent(const std::string& path, const Matcher<std::string>& expected_content_matcher) {
std::string actual_content;
ASSERT_TRUE(ReadFileToString(path, &actual_content));
- EXPECT_EQ(actual_content, expected_content);
+ EXPECT_THAT(actual_content, expected_content_matcher);
}
void CheckOtherReadable(const std::string& path, bool expected_value) {
@@ -384,24 +389,24 @@ class ArtdTest : public CommonArtTest {
};
struct stat st;
ASSERT_EQ(stat(scratch_path_.c_str(), &st), 0);
+ permission_settings_ = {
+ .dirFsPermission =
+ FsPermission{
+ .uid = static_cast<int32_t>(st.st_uid),
+ .gid = static_cast<int32_t>(st.st_gid),
+ .isOtherReadable = true,
+ .isOtherExecutable = true,
+ },
+ .fileFsPermission =
+ FsPermission{
+ .uid = static_cast<int32_t>(st.st_uid),
+ .gid = static_cast<int32_t>(st.st_gid),
+ .isOtherReadable = true,
+ },
+ };
output_artifacts_ = OutputArtifacts{
.artifactsPath = artifacts_path_,
- .permissionSettings =
- OutputArtifacts::PermissionSettings{
- .dirFsPermission =
- FsPermission{
- .uid = static_cast<int32_t>(st.st_uid),
- .gid = static_cast<int32_t>(st.st_gid),
- .isOtherReadable = true,
- .isOtherExecutable = true,
- },
- .fileFsPermission =
- FsPermission{
- .uid = static_cast<int32_t>(st.st_uid),
- .gid = static_cast<int32_t>(st.st_gid),
- .isOtherReadable = true,
- },
- },
+ .permissionSettings = permission_settings_,
};
clc_1_ = GetTestDexFileName("Main");
clc_2_ = GetTestDexFileName("Nested");
@@ -417,6 +422,12 @@ class ArtdTest : public CommonArtTest {
dm_path_ = DexMetadataPath{.dexPath = dex_file_};
std::filesystem::create_directories(
std::filesystem::path(OR_FATAL(BuildFinalProfilePath(tmp_profile_path_))).parent_path());
+
+ sdm_sdc_paths_ = {
+ .dexPath = dex_file_,
+ .isa = isa_,
+ .isInDalvikCache = false,
+ };
}
void TearDown() override {
@@ -547,6 +558,7 @@ class ArtdTest : public CommonArtTest {
std::string dex_file_;
std::string isa_;
ArtifactsPath artifacts_path_;
+ PermissionSettings permission_settings_;
OutputArtifacts output_artifacts_;
std::string clc_1_;
std::string clc_2_;
@@ -561,6 +573,8 @@ class ArtdTest : public CommonArtTest {
bool dex_file_other_readable_ = true;
bool profile_other_readable_ = true;
+ SecureDexMetadataWithCompanionPaths sdm_sdc_paths_;
+
private:
void InitFilesBeforeDexopt() {
// Required files.
@@ -701,6 +715,79 @@ TEST_F(ArtdTest, deleteArtifactsFileIsDir) {
EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
}
+TEST_F(ArtdTest, maybeCreateSdc) {
+ // Unable to create OatFileAssistantContext on host to get APEX versions.
+ TEST_DISABLED_FOR_HOST();
+
+ std::string sdm_file = OR_FAIL(BuildSdmPath(sdm_sdc_paths_));
+ std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_));
+ CreateFile(sdm_file);
+
+ ASSERT_STATUS_OK(artd_->maybeCreateSdc(
+ {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_}));
+
+ CheckContent(sdc_file, StartsWith("sdm-timestamp-ns="));
+}
+
+TEST_F(ArtdTest, maybeCreateSdcAlreadyCreated) {
+ // Unable to create OatFileAssistantContext on host to get APEX versions.
+ TEST_DISABLED_FOR_HOST();
+
+ std::string sdm_file = OR_FAIL(BuildSdmPath(sdm_sdc_paths_));
+ std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_));
+ CreateFile(sdm_file);
+
+ ASSERT_STATUS_OK(artd_->maybeCreateSdc(
+ {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_}));
+
+ struct stat sdc_st;
+ ASSERT_EQ(stat(sdc_file.c_str(), &sdc_st), 0);
+
+ ASSERT_STATUS_OK(artd_->maybeCreateSdc(
+ {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_}));
+
+ struct stat new_sdc_st;
+ ASSERT_EQ(stat(sdc_file.c_str(), &new_sdc_st), 0);
+
+ EXPECT_EQ(TimeSpecToNs(sdc_st.st_mtim), TimeSpecToNs(new_sdc_st.st_mtim));
+}
+
+TEST_F(ArtdTest, maybeCreateSdcOutdatedTimestamp) {
+ // Unable to create OatFileAssistantContext on host to get APEX versions.
+ TEST_DISABLED_FOR_HOST();
+
+ std::string sdm_file = OR_FAIL(BuildSdmPath(sdm_sdc_paths_));
+ std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_));
+ CreateFile(sdm_file);
+
+ ASSERT_STATUS_OK(artd_->maybeCreateSdc(
+ {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_}));
+
+ struct stat sdc_st;
+ ASSERT_EQ(stat(sdc_file.c_str(), &sdc_st), 0);
+
+ // Simulate that the SDM file is updated.
+ CreateFile(sdm_file);
+
+ ASSERT_STATUS_OK(artd_->maybeCreateSdc(
+ {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_}));
+
+ struct stat new_sdc_st;
+ ASSERT_EQ(stat(sdc_file.c_str(), &new_sdc_st), 0);
+
+ // The SDC file should be updated.
+ EXPECT_LT(TimeSpecToNs(sdc_st.st_mtim), TimeSpecToNs(new_sdc_st.st_mtim));
+}
+
+TEST_F(ArtdTest, maybeCreateSdcNoSdm) {
+ std::string sdc_file = OR_FAIL(BuildSdcPath(sdm_sdc_paths_));
+
+ ASSERT_STATUS_OK(artd_->maybeCreateSdc(
+ {.sdcPath = sdm_sdc_paths_, .permissionSettings = permission_settings_}));
+
+ EXPECT_FALSE(std::filesystem::exists(sdc_file));
+}
+
TEST_F(ArtdTest, dexopt) {
dexopt_options_.generateAppImage = true;
@@ -2143,15 +2230,23 @@ TEST_F(ArtdTest, mergeProfilesWithOptionsDumpClassesAndMethods) {
CheckContent(output_profile.profilePath.tmpPath, "dump");
}
+static std::string EncodeLocationForDalvikCache(const std::string& location) {
+ std::string encoded = location.substr(/*pos=*/1); // Remove the leading '/';
+ std::replace(encoded.begin(), encoded.end(), '/', '@');
+ return encoded;
+}
+
class ArtdCleanupTest : public ArtdTest {
protected:
void SetUpForCleanup() {
// Unmanaged files.
CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.odex");
+ CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.arm64.sdm");
CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.odex");
CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.txt");
CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.txt");
CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.tmp");
+ CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.sdc");
// Files to keep.
CreateGcKeptFile(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof");
@@ -2179,6 +2274,15 @@ class ArtdCleanupTest : public ArtdTest {
"/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art");
CreateGcKeptFile(android_data_ +
"/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art");
+ CreateGcKeptFile(android_data_ +
+ "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/base.arm64.sdm");
+ CreateGcKeptFile(android_data_ +
+ "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/oat/arm64/base.sdc");
+ CreateGcKeptFile(android_data_ +
+ "/app/~~jhrwafasr==/com.android.qux-bredcweff==/base.arm64.sdm");
+ CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/" +
+ EncodeLocationForDalvikCache(android_data_) +
+ "@app@~~jhrwafasr==@com.android.qux-bredcweff==@base.apk@classes.sdc");
// Files to remove.
CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof");
@@ -2220,6 +2324,26 @@ class ArtdCleanupTest : public ArtdTest {
"/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art");
CreateGcRemovedFile(android_data_ +
"/user/0/com.android.foo/cache/oat_primary/different_isa/base.art");
+ CreateGcRemovedFile(android_data_ +
+ "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/different_dex.arm64.sdm");
+ CreateGcRemovedFile(
+ android_data_ +
+ "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/oat/arm64/different_dex.sdc");
+ CreateGcRemovedFile(android_data_ +
+ "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/base.different_isa.sdm");
+ CreateGcRemovedFile(
+ android_data_ +
+ "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/oat/different_isa/base.sdc");
+ CreateGcRemovedFile(android_data_ +
+ "/app/~~jhrwafasr==/com.android.qux-bredcweff==/different_dex.arm64.sdm");
+ CreateGcRemovedFile(
+ android_data_ + "/dalvik-cache/arm64/" + EncodeLocationForDalvikCache(android_data_) +
+ "@app@~~jhrwafasr==@com.android.qux-bredcweff==@different_dex.apk@classes.sdc");
+ CreateGcRemovedFile(android_data_ +
+ "/app/~~jhrwafasr==/com.android.qux-bredcweff==/base.different_isa.sdm");
+ CreateGcRemovedFile(android_data_ + "/dalvik-cache/different_isa/" +
+ EncodeLocationForDalvikCache(android_data_) +
+ "@app@~~jhrwafasr==@com.android.qux-bredcweff==@base.apk@classes.sdc");
}
void CreateGcRemovedFile(const std::string& path) {
@@ -2260,6 +2384,18 @@ class ArtdCleanupTest : public ArtdTest {
.isInDalvikCache = false}},
},
{
+ SecureDexMetadataWithCompanionPaths{
+ .dexPath =
+ android_data_ + "/app/~~fadsfgadg==/com.android.baz-fadsfgadg==/base.apk",
+ .isa = "arm64",
+ .isInDalvikCache = false},
+ SecureDexMetadataWithCompanionPaths{
+ .dexPath =
+ android_data_ + "/app/~~jhrwafasr==/com.android.qux-bredcweff==/base.apk",
+ .isa = "arm64",
+ .isInDalvikCache = true},
+ },
+ {
RuntimeArtifactsPath{
.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"},
},
@@ -2348,6 +2484,19 @@ TEST_F(ArtdTest, isInDalvikCache) {
EXPECT_THAT(is_in_dalvik_cache("/foo"), HasValue(true));
}
+TEST_F(ArtdTest, deleteSdmSdcFiles) {
+ CreateFile(scratch_path_ + "/a/b.arm64.sdm", "**"); // 2 bytes.
+ CreateFile(scratch_path_ + "/a/oat/arm64/b.sdc", "*"); // 1 byte.
+
+ int64_t result = -1;
+ ASSERT_STATUS_OK(artd_->deleteSdmSdcFiles(
+ {.dexPath = scratch_path_ + "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}, &result));
+ EXPECT_EQ(result, 2 + 1);
+
+ EXPECT_FALSE(std::filesystem::exists(scratch_path_ + "/a/b.arm64.sdm"));
+ EXPECT_FALSE(std::filesystem::exists(scratch_path_ + "/a/oat/arm64/b.sdc"));
+}
+
TEST_F(ArtdTest, deleteRuntimeArtifacts) {
std::vector<std::string> removed_files;
std::vector<std::string> kept_files;
@@ -2413,6 +2562,8 @@ TEST_F(ArtdTest, deleteRuntimeArtifactsAndroidDataNotExist) {
EXPECT_EQ(aidl_return, 0);
}
+// Verifies that `deleteRuntimeArtifacts` doesn't treat "*" as a wildcard. It should either treat it
+// as a normal character in the path or reject it. The caller is never supposed to use a wildcard.
TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) {
std::vector<std::string> removed_files;
std::vector<std::string> kept_files;
@@ -2430,25 +2581,18 @@ TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) {
CreateKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art");
CreateRemovedFile(android_data_ + "/user/0/*/cache/oat_primary/arm64/base.art");
- CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/*/base.art");
CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/*.art");
int64_t aidl_return;
- ASSERT_TRUE(
- artd_
- ->deleteRuntimeArtifacts({.packageName = "*", .dexPath = "/a/b/base.apk", .isa = "arm64"},
- &aidl_return)
- .isOk());
- ASSERT_TRUE(artd_
- ->deleteRuntimeArtifacts(
- {.packageName = "com.android.foo", .dexPath = "/a/b/*.apk", .isa = "arm64"},
- &aidl_return)
- .isOk());
- ASSERT_TRUE(artd_
- ->deleteRuntimeArtifacts(
- {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "*"},
- &aidl_return)
- .isOk());
+ ASSERT_STATUS_OK(artd_->deleteRuntimeArtifacts(
+ {.packageName = "*", .dexPath = "/a/b/base.apk", .isa = "arm64"}, &aidl_return));
+ ASSERT_STATUS_OK(artd_->deleteRuntimeArtifacts(
+ {.packageName = "com.android.foo", .dexPath = "/a/b/*.apk", .isa = "arm64"}, &aidl_return));
+ ASSERT_FALSE(artd_
+ ->deleteRuntimeArtifacts(
+ {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "*"},
+ &aidl_return)
+ .isOk());
for (const std::string& path : removed_files) {
EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path);
@@ -2497,6 +2641,19 @@ TEST_F(ArtdTest, getVdexFileSize) {
EXPECT_EQ(aidl_return, 1);
}
+TEST_F(ArtdTest, getSdmFileSize) {
+ CreateFile(scratch_path_ + "/a/b.arm64.sdm", std::string(1, '*'));
+
+ int64_t aidl_return = -1;
+ ASSERT_TRUE(
+ artd_
+ ->getSdmFileSize(
+ {.dexPath = scratch_path_ + "/a/b.apk", .isa = "arm64", .isInDalvikCache = false},
+ &aidl_return)
+ .isOk());
+ EXPECT_EQ(aidl_return, 1);
+}
+
TEST_F(ArtdTest, getRuntimeArtifactsSize) {
CreateFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art",
std::string(1, '*'));
diff --git a/artd/binder/com/android/server/art/ArtifactsLocation.aidl b/artd/binder/com/android/server/art/ArtifactsLocation.aidl
index 1084456dec..bb1d505c29 100644
--- a/artd/binder/com/android/server/art/ArtifactsLocation.aidl
+++ b/artd/binder/com/android/server/art/ArtifactsLocation.aidl
@@ -27,4 +27,15 @@ enum ArtifactsLocation {
NEXT_TO_DEX = 2,
/** In the dex metadata file. This means the only usable artifact is the VDEX file. */
DM = 3,
+ /**
+ * The OAT and ART files are in the SDM file next to the dex file. The VDEX file is in the DM
+ * file next to the dex file. The SDC file is in the global "dalvik-cache" folder. (This happens
+ * typically when the app is in incremental-fs.)
+ */
+ SDM_DALVIK_CACHE = 4,
+ /**
+ * The OAT and ART files are in the SDM file next to the dex file. The VDEX file is in the DM
+ * file next to the dex file. The SDC file is next to the dex file.
+ */
+ SDM_NEXT_TO_DEX = 5,
}
diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
index 7e9cf9da58..705076d0a6 100644
--- a/artd/binder/com/android/server/art/IArtd.aidl
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -153,6 +153,15 @@ interface IArtd {
int dexoptTrigger);
/**
+ * Creates a secure dex metadata companion (SDC) file for the secure dex metadata (SDM) file, if
+ * the SDM file exists while the SDC file doesn't exist (meaning the SDM file is seen the first
+ * time).
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ void maybeCreateSdc(in com.android.server.art.OutputSecureDexMetadataCompanion outputSdc);
+
+ /**
* Dexopts a dex file for the given instruction set.
*
* Throws fatal and non-fatal errors. When dexopt fails, the non-fatal status includes an error
@@ -200,6 +209,7 @@ interface IArtd {
long cleanup(in List<com.android.server.art.ProfilePath> profilesToKeep,
in List<com.android.server.art.ArtifactsPath> artifactsToKeep,
in List<com.android.server.art.VdexPath> vdexFilesToKeep,
+ in List<com.android.server.art.SecureDexMetadataWithCompanionPaths> SdmSdcFilesToKeep,
in List<com.android.server.art.RuntimeArtifactsPath> runtimeArtifactsToKeep,
boolean keepPreRebootStagedFiles);
@@ -221,6 +231,16 @@ interface IArtd {
boolean isInDalvikCache(@utf8InCpp String dexFile);
/**
+ * Deletes the SDM and SDC files and returns the released space, in bytes.
+ *
+ * Not supported in Pre-reboot Dexopt mode.
+ *
+ * Throws fatal errors. Logs and ignores non-fatal errors.
+ */
+ long deleteSdmSdcFiles(
+ in com.android.server.art.SecureDexMetadataWithCompanionPaths sdmSdcPaths);
+
+ /**
* Deletes runtime artifacts and returns the released space, in bytes.
*
* Not supported in Pre-reboot Dexopt mode.
@@ -251,6 +271,16 @@ interface IArtd {
long getVdexFileSize(in com.android.server.art.VdexPath vdexPath);
/**
+ * Returns the size of the SDM 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 getSdmFileSize(in com.android.server.art.SecureDexMetadataWithCompanionPaths sdmPath);
+
+ /**
* Returns the size of the runtime artifacts, in bytes, or 0 if they don't exist or a non-fatal
* error occurred.
*
diff --git a/artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl b/artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl
new file mode 100644
index 0000000000..df5c84df7f
--- /dev/null
+++ b/artd/binder/com/android/server/art/OutputSecureDexMetadataCompanion.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.art;
+
+/**
+ * Represents output secure dex metadata companion (SDC) file.
+ *
+ * @hide
+ */
+parcelable OutputSecureDexMetadataCompanion {
+ /** The path to the output. */
+ com.android.server.art.SecureDexMetadataWithCompanionPaths sdcPath;
+
+ /** The permissions settings of the output. */
+ com.android.server.art.OutputArtifacts.PermissionSettings permissionSettings;
+}
diff --git a/artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl b/artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl
new file mode 100644
index 0000000000..db0571100e
--- /dev/null
+++ b/artd/binder/com/android/server/art/SecureDexMetadataWithCompanionPaths.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.art;
+
+/**
+ * Represents the paths to a secure dex metadata (SDM) file and its companion (SDC) file.
+ *
+ * @hide
+ */
+parcelable SecureDexMetadataWithCompanionPaths {
+ /**
+ * The absolute path starting with '/' to the dex file that the SDM file is next to.
+ */
+ @utf8InCpp String dexPath;
+ /** The instruction set of the dexopt artifacts. */
+ @utf8InCpp String isa;
+ /**
+ * Whether the SDC file in the dalvik-cache folder. This is true typically when the app is in
+ * incremental-fs.
+ *
+ * Only applicable to the SDC file, because the SDM file is installed with the app and therefore
+ * always to the dex file regardlessly.
+ */
+ boolean isInDalvikCache;
+}
diff --git a/artd/path_utils.cc b/artd/path_utils.cc
index 52bae7097e..0f269e2dbb 100644
--- a/artd/path_utils.cc
+++ b/artd/path_utils.cc
@@ -45,6 +45,7 @@ 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::SecureDexMetadataWithCompanionPaths;
using ::aidl::com::android::server::art::VdexPath;
using ::android::base::Error;
using ::android::base::Result;
@@ -107,6 +108,7 @@ std::vector<std::string> ListManagedFiles(const std::string& android_data,
for (const std::string& data_root : {android_data, android_expand + "/*"}) {
// Artifacts for primary dex files.
patterns.push_back(data_root + "/app/*/*/oat/**");
+ patterns.push_back(data_root + "/app/*/*/*.sdm");
for (const char* user_dir : {"/user", "/user_de"}) {
std::string data_dir = data_root + user_dir + "/*/*";
@@ -148,9 +150,17 @@ std::vector<std::string> ListRuntimeArtifactsFiles(
return tools::Glob(patterns, gListRootDir);
}
+static Result<InstructionSet> ValidateAndGetIsa(const std::string& isa_str) {
+ InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
+ if (isa == InstructionSet::kNone) {
+ return Errorf("Instruction set '{}' is invalid", isa_str);
+ }
+ return isa;
+}
+
Result<void> ValidateRuntimeArtifactsPath(const RuntimeArtifactsPath& runtime_artifacts_path) {
OR_RETURN(ValidatePathElement(runtime_artifacts_path.packageName, "packageName"));
- OR_RETURN(ValidatePathElement(runtime_artifacts_path.isa, "isa"));
+ OR_RETURN(ValidateAndGetIsa(runtime_artifacts_path.isa));
OR_RETURN(ValidateDexPath(runtime_artifacts_path.dexPath));
return {};
}
@@ -159,32 +169,36 @@ Result<std::string> BuildArtBinPath(const std::string& binary_name) {
return ART_FORMAT("{}/bin/{}", OR_RETURN(GetArtRootOrError()), binary_name);
}
-Result<RawArtifactsPath> BuildArtifactsPath(const ArtifactsPath& artifacts_path) {
- OR_RETURN(ValidateDexPath(artifacts_path.dexPath));
-
- InstructionSet isa = GetInstructionSetFromString(artifacts_path.isa.c_str());
- if (isa == InstructionSet::kNone) {
- return Errorf("Instruction set '{}' is invalid", artifacts_path.isa);
- }
+Result<std::string> BuildOatPath(const std::string& dex_path,
+ const std::string& isa_str,
+ bool is_in_dalvik_cache) {
+ OR_RETURN(ValidateDexPath(dex_path));
+ InstructionSet isa = OR_RETURN(ValidateAndGetIsa(isa_str));
+ std::string oat_path;
std::string error_msg;
- RawArtifactsPath path;
- if (artifacts_path.isInDalvikCache) {
+ if (is_in_dalvik_cache) {
// Apps' OAT files are never in ART APEX data.
- if (!OatFileAssistant::DexLocationToOatFilename(artifacts_path.dexPath,
+ if (!OatFileAssistant::DexLocationToOatFilename(dex_path,
isa,
/*deny_art_apex_data_files=*/true,
- &path.oat_path,
+ &oat_path,
&error_msg)) {
- return Error() << error_msg;
+ return Errorf("{}", error_msg);
}
} else {
- if (!OatFileAssistant::DexLocationToOdexFilename(
- artifacts_path.dexPath, isa, &path.oat_path, &error_msg)) {
- return Error() << error_msg;
+ if (!OatFileAssistant::DexLocationToOdexFilename(dex_path, isa, &oat_path, &error_msg)) {
+ return Errorf("{}", error_msg);
}
}
+ return oat_path;
+}
+
+Result<RawArtifactsPath> BuildArtifactsPath(const ArtifactsPath& artifacts_path) {
+ RawArtifactsPath path;
+ path.oat_path = OR_RETURN(
+ BuildOatPath(artifacts_path.dexPath, artifacts_path.isa, artifacts_path.isInDalvikCache));
path.vdex_path = ReplaceFileExtension(path.oat_path, kVdexExtension);
path.art_path = ReplaceFileExtension(path.oat_path, kArtExtension);
@@ -303,6 +317,19 @@ Result<std::string> BuildVdexPath(const VdexPath& vdex_path) {
return OR_RETURN(BuildArtifactsPath(vdex_path.get<VdexPath::artifactsPath>())).vdex_path;
}
+Result<std::string> BuildSdmPath(const SecureDexMetadataWithCompanionPaths& sdm_path) {
+ // `sdm_path.isInDalvikCache` is intentionally ignored because it's only applicable to SDC files.
+ OR_RETURN(ValidateDexPath(sdm_path.dexPath));
+ OR_RETURN(ValidateAndGetIsa(sdm_path.isa));
+ return ReplaceFileExtension(sdm_path.dexPath, ART_FORMAT(".{}{}", sdm_path.isa, kSdmExtension));
+}
+
+Result<std::string> BuildSdcPath(const SecureDexMetadataWithCompanionPaths& sdc_path) {
+ std::string oat_path =
+ OR_RETURN(BuildOatPath(sdc_path.dexPath, sdc_path.isa, sdc_path.isInDalvikCache));
+ return ReplaceFileExtension(oat_path, ".sdc");
+}
+
bool PreRebootFlag(const ProfilePath& profile_path) {
switch (profile_path.getTag()) {
case ProfilePath::primaryRefProfilePath:
diff --git a/artd/path_utils.h b/artd/path_utils.h
index 1528d0610b..e31115683b 100644
--- a/artd/path_utils.h
+++ b/artd/path_utils.h
@@ -55,6 +55,10 @@ android::base::Result<void> ValidateRuntimeArtifactsPath(
android::base::Result<std::string> BuildArtBinPath(const std::string& binary_name);
+android::base::Result<std::string> BuildOatPath(const std::string& dex_path,
+ const std::string& isa_str,
+ bool is_in_dalvik_cache);
+
// Returns the absolute paths to files built from the `ArtifactsPath`.
android::base::Result<RawArtifactsPath> BuildArtifactsPath(
const aidl::com::android::server::art::ArtifactsPath& artifacts_path);
@@ -96,6 +100,12 @@ android::base::Result<std::string> BuildProfileOrDmPath(
android::base::Result<std::string> BuildVdexPath(
const aidl::com::android::server::art::VdexPath& vdex_path);
+android::base::Result<std::string> BuildSdmPath(
+ const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& sdm_path);
+
+android::base::Result<std::string> BuildSdcPath(
+ const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& sdc_path);
+
// Takes an argument of type `WritableProfilePath`. Returns the pre-reboot flag by value if the
// argument is const, or by reference otherwise.
template <typename T,
diff --git a/artd/path_utils_test.cc b/artd/path_utils_test.cc
index 116177a7a7..8b50ca3549 100644
--- a/artd/path_utils_test.cc
+++ b/artd/path_utils_test.cc
@@ -255,6 +255,21 @@ TEST_F(PathUtilsTest, BuildVdexPath) {
HasValue("/a/oat/arm64/b.vdex"));
}
+TEST_F(PathUtilsTest, BuildSdmPath) {
+ EXPECT_THAT(BuildSdmPath({.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}),
+ HasValue("/a/b.arm64.sdm"));
+}
+
+TEST_F(PathUtilsTest, BuildSdcPath) {
+ EXPECT_THAT(BuildSdcPath({.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}),
+ HasValue("/a/oat/arm64/b.sdc"));
+}
+
+TEST_F(PathUtilsTest, BuildSdcPathDalvikCache) {
+ EXPECT_THAT(BuildSdcPath({.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = true}),
+ HasValue(android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.sdc"));
+}
+
} // namespace
} // namespace artd
} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_test.cc b/compiler/optimizing/instruction_simplifier_test.cc
index a2e3882c19..448dc32006 100644
--- a/compiler/optimizing/instruction_simplifier_test.cc
+++ b/compiler/optimizing/instruction_simplifier_test.cc
@@ -234,9 +234,9 @@ TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
INSTANTIATE_TEST_SUITE_P(InstructionSimplifierTest,
InstanceOfInstructionSimplifierTestGroup,
- testing::Values(InstanceOfKind::kSelf,
- InstanceOfKind::kUnrelatedLoaded,
- InstanceOfKind::kUnrelatedUnloaded,
- InstanceOfKind::kSupertype));
+ ::testing::Values(InstanceOfKind::kSelf,
+ InstanceOfKind::kUnrelatedLoaded,
+ InstanceOfKind::kUnrelatedUnloaded,
+ InstanceOfKind::kSupertype));
} // namespace art
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index bb73688b89..af039229af 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -1779,17 +1779,17 @@ TEST_P(TwoTypesConversionsTestGroup, MergingConvertedValueStorePhiDeduplication)
}
auto Int32AndSmallerTypesGenerator() {
- return testing::Values(DataType::Type::kInt32,
- DataType::Type::kInt16,
- DataType::Type::kInt8,
- DataType::Type::kUint16,
- DataType::Type::kUint8);
+ return ::testing::Values(DataType::Type::kInt32,
+ DataType::Type::kInt16,
+ DataType::Type::kInt8,
+ DataType::Type::kUint16,
+ DataType::Type::kUint8);
}
-INSTANTIATE_TEST_SUITE_P(
- LoadStoreEliminationTest,
- TwoTypesConversionsTestGroup,
- testing::Combine(Int32AndSmallerTypesGenerator(), Int32AndSmallerTypesGenerator()));
+INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest,
+ TwoTypesConversionsTestGroup,
+ ::testing::Combine(Int32AndSmallerTypesGenerator(),
+ Int32AndSmallerTypesGenerator()));
// // ENTRY
// obj = new Obj();
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 0e0acd11c2..f720b8d911 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -496,16 +496,16 @@ TEST_P(LoopReferenceTypePropagationTestGroup, RunVisitTest) {
INSTANTIATE_TEST_SUITE_P(ReferenceTypePropagationTest,
LoopReferenceTypePropagationTestGroup,
- testing::Combine(testing::Values(ShuffleOrder::kAlmostTopological,
- ShuffleOrder::kReverseTopological,
- ShuffleOrder::kTopological,
- ShuffleOrder::kRandom),
- testing::Values(-1, 10, 40),
- testing::Values(0, 1),
- testing::Values(InitialNullState::kAllNonNull,
- InitialNullState::kAllNull,
- InitialNullState::kHalfNull,
- InitialNullState::kRandom)));
+ ::testing::Combine(::testing::Values(ShuffleOrder::kAlmostTopological,
+ ShuffleOrder::kReverseTopological,
+ ShuffleOrder::kTopological,
+ ShuffleOrder::kRandom),
+ ::testing::Values(-1, 10, 40),
+ ::testing::Values(0, 1),
+ ::testing::Values(InitialNullState::kAllNonNull,
+ InitialNullState::kAllNull,
+ InitialNullState::kHalfNull,
+ InitialNullState::kRandom)));
TEST_P(NonLoopReferenceTypePropagationTestGroup, RunVisitTest) {
RunVisitListTest([&](std::vector<HInstruction*>& lst) { MutateList(lst, GetParam()); });
@@ -513,9 +513,9 @@ TEST_P(NonLoopReferenceTypePropagationTestGroup, RunVisitTest) {
INSTANTIATE_TEST_SUITE_P(ReferenceTypePropagationTest,
NonLoopReferenceTypePropagationTestGroup,
- testing::Values(ShuffleOrder::kAlmostTopological,
- ShuffleOrder::kReverseTopological,
- ShuffleOrder::kTopological,
- ShuffleOrder::kRandom));
+ ::testing::Values(ShuffleOrder::kAlmostTopological,
+ ShuffleOrder::kReverseTopological,
+ ShuffleOrder::kTopological,
+ ShuffleOrder::kRandom));
} // namespace art
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index 0a89ad1299..515361af00 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -42,7 +42,7 @@ namespace art HIDDEN {
static constexpr bool kKeepDisassembledFiles = false;
// We put this into a class as gtests are self-contained, so this helper needs to be in an h-file.
-class AssemblerTestBase : public testing::Test {
+class AssemblerTestBase : public ::testing::Test {
public:
AssemblerTestBase() {}
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index 38eec45611..4f7c3b4452 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -219,13 +219,12 @@ TEST_F(ElfWriterTest, CheckDynamicSection) {
size_t bss_methods_offset,
size_t bss_roots_offset,
size_t dex_section_size,
- /*out*/ size_t *number_of_dynamic_symbols) {
- SCOPED_TRACE(testing::Message() << "rodata_size: " << rodata_size
- << ", text_size: " << text_size
+ /*out*/ size_t* number_of_dynamic_symbols) {
+ SCOPED_TRACE(::testing::Message()
+ << "rodata_size: " << rodata_size << ", text_size: " << text_size
<< ", data_img_rel_ro_size: " << data_img_rel_ro_size
<< ", data_img_rel_ro_app_image_offset: " << data_img_rel_ro_app_image_offset
- << ", bss_size: " << bss_size
- << ", bss_methods_offset: " << bss_methods_offset
+ << ", bss_size: " << bss_size << ", bss_methods_offset: " << bss_methods_offset
<< ", bss_roots_offset: " << bss_roots_offset
<< ", dex_section_size: " << dex_section_size);
diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc
index b2aa619337..a610bde6e9 100644
--- a/dex2oat/linker/multi_oat_relative_patcher_test.cc
+++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc
@@ -27,7 +27,7 @@ namespace linker {
static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
-class MultiOatRelativePatcherTest : public testing::Test {
+class MultiOatRelativePatcherTest : public ::testing::Test {
protected:
class MockPatcher : public RelativePatcher {
public:
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 3e7ba796e0..16f263ec21 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -36,7 +36,7 @@ namespace art {
namespace linker {
// Base class providing infrastructure for architecture-specific tests.
-class RelativePatcherTest : public testing::Test {
+class RelativePatcherTest : public ::testing::Test {
protected:
RelativePatcherTest(InstructionSet instruction_set, const std::string& variant)
: storage_(/*swap_fd=*/ -1),
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 2d8194c46e..24603c1220 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -323,10 +323,8 @@ art_cc_library_static {
srcs: [
"base/testing.cc",
],
- header_libs: [
- "libbase_headers",
- "art_libartbase_headers",
- ],
+ header_libs: ["art_libartbase_headers"],
+ export_header_lib_headers: ["art_libartbase_headers"],
}
art_cc_defaults {
diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc
index 6323a2b97c..d956b4ab5e 100644
--- a/libartbase/base/arena_allocator_test.cc
+++ b/libartbase/base/arena_allocator_test.cc
@@ -23,7 +23,7 @@
namespace art {
-class ArenaAllocatorTest : public testing::Test {
+class ArenaAllocatorTest : public ::testing::Test {
protected:
size_t NumberOfArenas(ArenaAllocator* allocator) {
size_t result = 0u;
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index 2fcf41c14e..41ed98d651 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -34,7 +34,6 @@
#include "android-base/process.h"
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
#include "android-base/unique_fd.h"
#include "art_field-inl.h"
#include "base/file_utils.h"
@@ -163,107 +162,6 @@ android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::s
return android::base::make_scope_guard([=]() { std::filesystem::permissions(path, old_perms); });
}
-std::string CommonArtTestImpl::GetAndroidBuildTop() {
- CHECK(IsHost());
- std::string android_build_top;
-
- // Look at how we were invoked to find the expected directory.
- std::string argv;
- if (android::base::ReadFileToString("/proc/self/cmdline", &argv)) {
- // /proc/self/cmdline is the programs 'argv' with elements delimited by '\0'.
- std::filesystem::path path(argv.substr(0, argv.find('\0')));
- path = std::filesystem::absolute(path);
- // Walk up until we find the one of the well-known directories.
- for (; path.parent_path() != path; path = path.parent_path()) {
- // We are running tests from out/host/linux-x86 on developer machine.
- if (path.filename() == std::filesystem::path("linux-x86")) {
- android_build_top = path.parent_path().parent_path().parent_path();
- break;
- }
- // We are running tests from testcases (extracted from zip) on tradefed.
- // The first path is for remote runs and the second path for local runs.
- if (path.filename() == std::filesystem::path("testcases") ||
- path.filename().string().starts_with("host_testcases")) {
- android_build_top = path.append("art_common");
- break;
- }
- }
- }
- CHECK(!android_build_top.empty());
-
- // Check that the expected directory matches the environment variable.
- const char* android_build_top_from_env = getenv("ANDROID_BUILD_TOP");
- android_build_top = std::filesystem::path(android_build_top).string();
- CHECK(!android_build_top.empty());
- if (android_build_top_from_env != nullptr) {
- if (std::filesystem::weakly_canonical(android_build_top).string() !=
- std::filesystem::weakly_canonical(android_build_top_from_env).string()) {
- android_build_top = android_build_top_from_env;
- }
- } else {
- setenv("ANDROID_BUILD_TOP", android_build_top.c_str(), /*overwrite=*/0);
- }
- if (android_build_top.back() != '/') {
- android_build_top += '/';
- }
- return android_build_top;
-}
-
-std::string CommonArtTestImpl::GetAndroidHostOut() {
- CHECK(IsHost());
-
- // Check that the expected directory matches the environment variable.
- // ANDROID_HOST_OUT is set by envsetup or unset and is the full path to host binaries/libs
- const char* android_host_out_from_env = getenv("ANDROID_HOST_OUT");
- // OUT_DIR is a user-settable ENV_VAR that controls where soong puts build artifacts. It can
- // either be relative to ANDROID_BUILD_TOP or a concrete path.
- const char* android_out_dir = getenv("OUT_DIR");
- // Take account of OUT_DIR setting.
- if (android_out_dir == nullptr) {
- android_out_dir = "out";
- }
- std::string android_host_out;
- if (android_out_dir[0] == '/') {
- android_host_out = (std::filesystem::path(android_out_dir) / "host" / "linux-x86").string();
- } else {
- android_host_out =
- (std::filesystem::path(GetAndroidBuildTop()) / android_out_dir / "host" / "linux-x86")
- .string();
- }
- std::filesystem::path expected(android_host_out);
- if (android_host_out_from_env != nullptr) {
- std::filesystem::path from_env(std::filesystem::weakly_canonical(android_host_out_from_env));
- if (std::filesystem::weakly_canonical(expected).string() != from_env.string()) {
- LOG(WARNING) << "Execution path (" << expected << ") not below ANDROID_HOST_OUT ("
- << from_env << ")! Using env-var.";
- expected = from_env;
- }
- } else {
- setenv("ANDROID_HOST_OUT", android_host_out.c_str(), /*overwrite=*/0);
- }
- return expected.string();
-}
-
-std::string CommonArtTestImpl::GetHostBootClasspathInstallRoot() {
- CHECK(IsHost());
- std::string build_install_root = GetAndroidHostOut() + "/testcases/art_common/out/host/linux-x86";
- // Look for the `apex` subdirectory as a discriminator to check the location.
- if (OS::DirectoryExists((build_install_root + "/apex").c_str())) {
- // This is the path where "m art-host-tests" installs support files for host
- // tests, so use it when the tests are run in a build tree (which is the
- // case when testing locally).
- return build_install_root;
- }
- if (OS::DirectoryExists((GetAndroidRoot() + "/apex").c_str())) {
- // This is the location for host tests in CI when the files are unzipped
- // from art-host-tests.zip.
- return GetAndroidRoot();
- }
- LOG(ERROR) << "Neither location has a boot classpath (forgot \"m art-host-tests\"?): "
- << build_install_root << " or " << GetAndroidRoot();
- return "<no boot classpath found>";
-}
-
void CommonArtTestImpl::SetUpAndroidRootEnvVars() {
if (IsHost()) {
std::string android_host_out = GetAndroidHostOut();
@@ -463,41 +361,6 @@ std::vector<std::string> CommonArtTestImpl::GetLibCoreModuleNames() const {
return art::testing::GetLibCoreModuleNames();
}
-std::vector<std::string> CommonArtTestImpl::GetLibCoreDexFileNames(
- const std::vector<std::string>& modules) const {
- return art::testing::GetLibCoreDexFileNames(
- kIsTargetBuild ? "" : GetHostBootClasspathInstallRoot(), modules);
-}
-
-std::vector<std::string> CommonArtTestImpl::GetLibCoreDexFileNames() const {
- std::vector<std::string> modules = GetLibCoreModuleNames();
- return art::testing::GetLibCoreDexFileNames(
- kIsTargetBuild ? "" : GetHostBootClasspathInstallRoot(), modules);
-}
-
-std::vector<std::string> CommonArtTestImpl::GetLibCoreDexLocations(
- const std::vector<std::string>& modules) const {
- std::string prefix = "";
- if (IsHost()) {
- std::string android_root = GetAndroidRoot();
- std::string build_top = GetAndroidBuildTop();
- CHECK(android_root.starts_with(build_top))
- << " android_root=" << android_root << " build_top=" << build_top;
- prefix = android_root.substr(build_top.size());
- }
- return art::testing::GetLibCoreDexFileNames(prefix, modules);
-}
-
-std::vector<std::string> CommonArtTestImpl::GetLibCoreDexLocations() const {
- std::vector<std::string> modules = GetLibCoreModuleNames();
- return GetLibCoreDexLocations(modules);
-}
-
-std::string CommonArtTestImpl::GetClassPathOption(const char* option,
- const std::vector<std::string>& class_path) {
- return option + android::base::Join(class_path, ':');
-}
-
// Check that for target builds we have ART_TARGET_NATIVETEST_DIR set.
#ifdef ART_TARGET
#ifndef ART_TARGET_NATIVETEST_DIR
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 5da6ac9920..b27199c386 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -32,6 +32,7 @@
#include "base/memory_tool.h"
#include "base/mutex.h"
#include "base/os.h"
+#include "base/testing.h"
#include "base/unix_file/fd_file.h"
#include "dex/art_dex_file_loader.h"
#include "dex/compact_dex_file.h"
@@ -152,19 +153,29 @@ class CommonArtTestImpl {
virtual std::vector<std::string> GetLibCoreModuleNames() const;
// Gets the paths of the libcore dex files for given modules.
- std::vector<std::string> GetLibCoreDexFileNames(const std::vector<std::string>& modules) const;
+ std::vector<std::string> GetLibCoreDexFileNames(const std::vector<std::string>& modules) const {
+ return art::testing::GetLibCoreDexFileNames(modules);
+ }
// Gets the paths of the libcore dex files.
- std::vector<std::string> GetLibCoreDexFileNames() const;
+ std::vector<std::string> GetLibCoreDexFileNames() const {
+ return GetLibCoreDexFileNames(GetLibCoreModuleNames());
+ }
// Gets the on-host or on-device locations of the libcore dex files for given modules.
- std::vector<std::string> GetLibCoreDexLocations(const std::vector<std::string>& modules) const;
+ std::vector<std::string> GetLibCoreDexLocations(const std::vector<std::string>& modules) const {
+ return art::testing::GetLibCoreDexLocations(modules);
+ }
// Gets the on-host or on-device locations of the libcore dex files.
- std::vector<std::string> GetLibCoreDexLocations() const;
+ std::vector<std::string> GetLibCoreDexLocations() const {
+ return GetLibCoreDexLocations(GetLibCoreModuleNames());
+ }
static std::string GetClassPathOption(const char* option,
- const std::vector<std::string>& class_path);
+ const std::vector<std::string>& class_path) {
+ return art::testing::GetClassPathOption(option, class_path);
+ }
// Retuerns the filename for a test dex (i.e. XandY or ManyMethods).
std::string GetTestDexFileName(const char* name) const;
@@ -227,21 +238,15 @@ class CommonArtTestImpl {
static std::string GetAndroidTool(const char* name, InstructionSet isa = InstructionSet::kX86_64);
protected:
- static bool IsHost() {
- return !art::kIsTargetBuild;
- }
+ static bool IsHost() { return art::testing::IsHost(); }
- // Returns ${ANDROID_BUILD_TOP}. Ensure it has tailing /.
- static std::string GetAndroidBuildTop();
+ static std::string GetAndroidBuildTop() { return art::testing::GetAndroidBuildTop(); }
- // Returns ${ANDROID_HOST_OUT}.
- static std::string GetAndroidHostOut();
+ static std::string GetAndroidHostOut() { return art::testing::GetAndroidHostOut(); }
- // Returns the path where boot classpath and boot image files are installed
- // for host tests (by the art_common mk module, typically built through "m
- // art-host-tests"). Different in CI where they are unpacked from the
- // art-host-tests.zip file.
- static std::string GetHostBootClasspathInstallRoot();
+ static std::string GetHostBootClasspathInstallRoot() {
+ return art::testing::GetHostBootClasspathInstallRoot();
+ }
// File location to boot.art, e.g. /apex/com.android.art/javalib/boot.art
static std::string GetCoreArtLocation();
@@ -300,10 +305,10 @@ class CommonArtTestBase : public TestType, public CommonArtTestImpl {
}
};
-using CommonArtTest = CommonArtTestBase<testing::Test>;
+using CommonArtTest = CommonArtTestBase<::testing::Test>;
template <typename Param>
-using CommonArtTestWithParam = CommonArtTestBase<testing::TestWithParam<Param>>;
+using CommonArtTestWithParam = CommonArtTestBase<::testing::TestWithParam<Param>>;
// Returns a list of PIDs of the processes whose process name (the first commandline argument) fully
// matches the given name.
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 2acebb9b4f..4253fa1ce7 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -715,6 +715,15 @@ std::string GetDmFilename(const std::string& dex_location) {
return ReplaceFileExtension(dex_location, kDmExtension);
}
+std::string GetSdmFilename(const std::string& dex_location, InstructionSet isa) {
+ return ReplaceFileExtension(dex_location,
+ StringPrintf("%s%s", GetInstructionSetString(isa), kSdmExtension));
+}
+
+std::string GetSdcFilename(const std::string& oat_location) {
+ return ReplaceFileExtension(oat_location, kSdcExtension);
+}
+
// check for the file in /system, followed by /system_ext
std::string GetSystemOdexFilenameForApex(std::string_view location, InstructionSet isa) {
DCHECK(LocationIsOnApex(location));
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index e8aa5f663a..89f2420aa4 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -39,6 +39,7 @@ static constexpr const char* kVdexExtension = ".vdex";
static constexpr const char* kArtExtension = ".art";
static constexpr const char* kDmExtension = ".dm";
static constexpr const char* kSdmExtension = ".sdm";
+static constexpr const char* kSdcExtension = ".sdc";
// These methods return the Android Root, which is the historical location of
// the Android "system" directory, containing the built Android artifacts. On
@@ -174,6 +175,12 @@ std::string GetVdexFilename(const std::string& oat_filename);
// Returns the dm filename for the given dex location.
std::string GetDmFilename(const std::string& dex_location);
+// Returns the sdm filename for the given dex location.
+std::string GetSdmFilename(const std::string& dex_location, InstructionSet isa);
+
+// Returns the sdc filename for the given oat filename.
+std::string GetSdcFilename(const std::string& oat_filename);
+
// Returns the odex location on /system for a DEX file on /apex. The caller must make sure that
// `location` is on /apex.
std::string GetSystemOdexFilenameForApex(std::string_view location, InstructionSet isa);
diff --git a/libartbase/base/hash_set_test.cc b/libartbase/base/hash_set_test.cc
index b7b289cf65..959ab5ebf5 100644
--- a/libartbase/base/hash_set_test.cc
+++ b/libartbase/base/hash_set_test.cc
@@ -39,7 +39,7 @@ struct IsEmptyFnString {
}
};
-class HashSetTest : public testing::Test {
+class HashSetTest : public ::testing::Test {
public:
HashSetTest() : seed_(97421), unique_number_(0) {
}
diff --git a/libartbase/base/intrusive_forward_list_test.cc b/libartbase/base/intrusive_forward_list_test.cc
index f96fc9d516..095a94ceeb 100644
--- a/libartbase/base/intrusive_forward_list_test.cc
+++ b/libartbase/base/intrusive_forward_list_test.cc
@@ -67,7 +67,7 @@ bool operator<(const IFLTestValue2& lhs, const IFLTestValue2& rhs) {
ASSERT_TRUE(std::equal((expected).begin(), (expected).end(), (value).begin())); \
} while (false)
-class IntrusiveForwardListTest : public testing::Test {
+class IntrusiveForwardListTest : public ::testing::Test {
public:
template <typename ListType>
void IteratorToConstIterator();
diff --git a/libartbase/base/logging_test.cc b/libartbase/base/logging_test.cc
index 1fa3209f7f..985a8eafae 100644
--- a/libartbase/base/logging_test.cc
+++ b/libartbase/base/logging_test.cc
@@ -32,7 +32,7 @@ static void SimpleAborter(const char* msg) {
_exit(1);
}
-class LoggingTest : public testing::Test {
+class LoggingTest : public ::testing::Test {
protected:
LoggingTest() {
// In our abort tests we really don't want the runtime to create a real dump.
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index b3da8fbf52..76a50a59e4 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -899,17 +899,17 @@ TEST_F(MemMapTest, Reservation) {
namespace {
-class DumpMapsOnFailListener : public testing::EmptyTestEventListener {
- void OnTestPartResult(const testing::TestPartResult& result) override {
+class DumpMapsOnFailListener : public ::testing::EmptyTestEventListener {
+ void OnTestPartResult(const ::testing::TestPartResult& result) override {
switch (result.type()) {
- case testing::TestPartResult::kFatalFailure:
+ case ::testing::TestPartResult::kFatalFailure:
art::PrintFileToLog("/proc/self/maps", android::base::LogSeverity::ERROR);
break;
// TODO: Could consider logging on EXPECT failures.
- case testing::TestPartResult::kNonFatalFailure:
- case testing::TestPartResult::kSkip:
- case testing::TestPartResult::kSuccess:
+ case ::testing::TestPartResult::kNonFatalFailure:
+ case ::testing::TestPartResult::kSkip:
+ case ::testing::TestPartResult::kSuccess:
break;
}
}
@@ -921,5 +921,5 @@ class DumpMapsOnFailListener : public testing::EmptyTestEventListener {
extern "C"
__attribute__((visibility("default"))) __attribute__((used))
void ArtTestGlobalInit() {
- testing::UnitTest::GetInstance()->listeners().Append(new DumpMapsOnFailListener());
+ ::testing::UnitTest::GetInstance()->listeners().Append(new DumpMapsOnFailListener());
}
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index bcc4da4e06..3581615514 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -31,7 +31,7 @@ using test::CounterValue;
using test::GetBuckets;
using test::TestBackendBase;
-class MetricsTest : public testing::Test {};
+class MetricsTest : public ::testing::Test {};
TEST_F(MetricsTest, SimpleCounter) {
MetricsCounter<DatumId::kClassVerificationTotalTime> test_counter;
diff --git a/libartbase/base/testing.cc b/libartbase/base/testing.cc
index 3cd2836876..6ec207eded 100644
--- a/libartbase/base/testing.cc
+++ b/libartbase/base/testing.cc
@@ -14,19 +14,123 @@
* limitations under the License.
*/
+#include "testing.h"
+
#include <string>
#include <vector>
+#include "android-base/file.h"
#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "base/file_utils.h"
#include "base/globals.h"
+#include "base/os.h"
namespace art {
namespace testing {
-namespace {
+std::string GetAndroidBuildTop() {
+ CHECK(IsHost());
+ std::string android_build_top;
+
+ // Look at how we were invoked to find the expected directory.
+ std::string argv;
+ if (android::base::ReadFileToString("/proc/self/cmdline", &argv)) {
+ // /proc/self/cmdline is the programs 'argv' with elements delimited by '\0'.
+ std::filesystem::path path(argv.substr(0, argv.find('\0')));
+ path = std::filesystem::absolute(path);
+ // Walk up until we find the one of the well-known directories.
+ for (; path.parent_path() != path; path = path.parent_path()) {
+ // We are running tests from out/host/linux-x86 on developer machine.
+ if (path.filename() == std::filesystem::path("linux-x86")) {
+ android_build_top = path.parent_path().parent_path().parent_path();
+ break;
+ }
+ // We are running tests from testcases (extracted from zip) on tradefed.
+ // The first path is for remote runs and the second path for local runs.
+ if (path.filename() == std::filesystem::path("testcases") ||
+ path.filename().string().starts_with("host_testcases")) {
+ android_build_top = path.append("art_common");
+ break;
+ }
+ }
+ }
+ CHECK(!android_build_top.empty());
+
+ // Check that the expected directory matches the environment variable.
+ const char* android_build_top_from_env = getenv("ANDROID_BUILD_TOP");
+ android_build_top = std::filesystem::path(android_build_top).string();
+ CHECK(!android_build_top.empty());
+ if (android_build_top_from_env != nullptr) {
+ if (std::filesystem::weakly_canonical(android_build_top).string() !=
+ std::filesystem::weakly_canonical(android_build_top_from_env).string()) {
+ android_build_top = android_build_top_from_env;
+ }
+ } else {
+ setenv("ANDROID_BUILD_TOP", android_build_top.c_str(), /*overwrite=*/0);
+ }
+ if (android_build_top.back() != '/') {
+ android_build_top += '/';
+ }
+ return android_build_top;
+}
+
+std::string GetAndroidHostOut() {
+ CHECK(IsHost());
+
+ // Check that the expected directory matches the environment variable.
+ // ANDROID_HOST_OUT is set by envsetup or unset and is the full path to host binaries/libs
+ const char* android_host_out_from_env = getenv("ANDROID_HOST_OUT");
+ // OUT_DIR is a user-settable ENV_VAR that controls where soong puts build artifacts. It can
+ // either be relative to ANDROID_BUILD_TOP or a concrete path.
+ const char* android_out_dir = getenv("OUT_DIR");
+ // Take account of OUT_DIR setting.
+ if (android_out_dir == nullptr) {
+ android_out_dir = "out";
+ }
+ std::string android_host_out;
+ if (android_out_dir[0] == '/') {
+ android_host_out = (std::filesystem::path(android_out_dir) / "host" / "linux-x86").string();
+ } else {
+ android_host_out =
+ (std::filesystem::path(GetAndroidBuildTop()) / android_out_dir / "host" / "linux-x86")
+ .string();
+ }
+ std::filesystem::path expected(android_host_out);
+ if (android_host_out_from_env != nullptr) {
+ std::filesystem::path from_env(std::filesystem::weakly_canonical(android_host_out_from_env));
+ if (std::filesystem::weakly_canonical(expected).string() != from_env.string()) {
+ LOG(WARNING) << "Execution path (" << expected << ") not below ANDROID_HOST_OUT (" << from_env
+ << ")! Using env-var.";
+ expected = from_env;
+ }
+ } else {
+ setenv("ANDROID_HOST_OUT", android_host_out.c_str(), /*overwrite=*/0);
+ }
+ return expected.string();
+}
+
+std::string GetHostBootClasspathInstallRoot() {
+ CHECK(IsHost());
+ std::string build_install_root = GetAndroidHostOut() + "/testcases/art_common/out/host/linux-x86";
+ // Look for the `apex` subdirectory as a discriminator to check the location.
+ if (OS::DirectoryExists((build_install_root + "/apex").c_str())) {
+ // This is the path where "m art-host-tests" installs support files for host
+ // tests, so use it when the tests are run in a build tree (which is the
+ // case when testing locally).
+ return build_install_root;
+ }
+ if (OS::DirectoryExists((GetAndroidRoot() + "/apex").c_str())) {
+ // This is the location for host tests in CI when the files are unzipped
+ // from art-host-tests.zip.
+ return GetAndroidRoot();
+ }
+ LOG(ERROR) << "Neither location has a boot classpath (forgot \"m art-host-tests\"?): "
+ << build_install_root << " or " << GetAndroidRoot();
+ return "<no boot classpath found>";
+}
-std::string GetDexFileName(const std::string& jar_prefix, const std::string& prefix) {
+static std::string GetDexFileName(const std::string& jar_prefix, const std::string& prefix) {
const char* apexPath =
(jar_prefix == "conscrypt") ?
kAndroidConscryptApexDefaultPath :
@@ -35,7 +139,15 @@ std::string GetDexFileName(const std::string& jar_prefix, const std::string& pre
"%s%s/javalib/%s.jar", prefix.c_str(), apexPath, jar_prefix.c_str());
}
-} // namespace
+static std::vector<std::string> GetPrefixedDexFileNames(const std::string& prefix,
+ const std::vector<std::string>& modules) {
+ std::vector<std::string> result;
+ result.reserve(modules.size());
+ for (const std::string& module : modules) {
+ result.push_back(GetDexFileName(module, prefix));
+ }
+ return result;
+}
std::vector<std::string> GetLibCoreModuleNames(bool core_only) {
// Note: This must start with the CORE_IMG_JARS in Android.common_path.mk because that's what we
@@ -59,23 +171,25 @@ std::vector<std::string> GetLibCoreModuleNames(bool core_only) {
return modules;
}
-std::vector<std::string> GetLibCoreDexFileNames(const std::string& prefix,
- const std::vector<std::string>& modules) {
- std::vector<std::string> result;
- result.reserve(modules.size());
- for (const std::string& module : modules) {
- result.push_back(GetDexFileName(module, prefix));
- }
- return result;
+std::vector<std::string> GetLibCoreDexFileNames(const std::vector<std::string>& modules) {
+ return GetPrefixedDexFileNames(kIsTargetBuild ? "" : GetHostBootClasspathInstallRoot(), modules);
}
std::vector<std::string> GetLibCoreDexFileNames(const std::string& prefix, bool core_only) {
std::vector<std::string> modules = GetLibCoreModuleNames(core_only);
- return GetLibCoreDexFileNames(prefix, modules);
+ return GetPrefixedDexFileNames(prefix, modules);
}
std::vector<std::string> GetLibCoreDexLocations(const std::vector<std::string>& modules) {
- return GetLibCoreDexFileNames(/*prefix=*/"", modules);
+ std::string prefix = "";
+ if (IsHost()) {
+ std::string android_root = GetAndroidRoot();
+ std::string build_top = GetAndroidBuildTop();
+ CHECK(android_root.starts_with(build_top))
+ << " android_root=" << android_root << " build_top=" << build_top;
+ prefix = android_root.substr(build_top.size());
+ }
+ return GetPrefixedDexFileNames(prefix, modules);
}
std::vector<std::string> GetLibCoreDexLocations(bool core_only) {
@@ -83,5 +197,9 @@ std::vector<std::string> GetLibCoreDexLocations(bool core_only) {
return GetLibCoreDexLocations(modules);
}
+std::string GetClassPathOption(const char* option, const std::vector<std::string>& class_path) {
+ return option + android::base::Join(class_path, ':');
+}
+
} // namespace testing
} // namespace art
diff --git a/libartbase/base/testing.h b/libartbase/base/testing.h
index 88d0aee6e0..55d7428436 100644
--- a/libartbase/base/testing.h
+++ b/libartbase/base/testing.h
@@ -22,20 +22,41 @@
#include <string>
#include <vector>
+#include "base/globals.h"
+
namespace art {
namespace testing {
+inline bool IsHost() { return !art::kIsTargetBuild; }
+
+// Returns ${ANDROID_BUILD_TOP}. Ensure it has tailing /.
+std::string GetAndroidBuildTop();
+
+// Returns ${ANDROID_HOST_OUT}.
+std::string GetAndroidHostOut();
+
+// Returns the path where boot classpath and boot image files are installed
+// for host tests (by the art_common mk module, typically built through "m
+// art-host-tests"). Different in CI where they are unpacked from the
+// art-host-tests.zip file.
+std::string GetHostBootClasspathInstallRoot();
+
// Note: "libcore" here means art + conscrypt + icu.
// Gets the names of the libcore modules.
// If `core_only` is true, only returns the names of CORE_IMG_JARS in Android.common_path.mk.
std::vector<std::string> GetLibCoreModuleNames(bool core_only = false);
-// Gets the paths of the libcore dex files for given modules.
-std::vector<std::string> GetLibCoreDexFileNames(const std::string& prefix,
- const std::vector<std::string>& modules);
+// Gets the paths of the libcore dex files for given modules, prefixed appropriately for host or
+// target tests.
+std::vector<std::string> GetLibCoreDexFileNames(const std::vector<std::string>& modules);
+
+// Gets the paths of the libcore module dex files, prefixed appropriately for host or target tests.
+inline std::vector<std::string> GetLibCoreDexFileNames() {
+ return GetLibCoreDexFileNames(GetLibCoreModuleNames());
+}
-// Gets the paths of the libcore dex files.
+// Gets the paths of the libcore dex files, prefixed by the given string.
// If `core_only` is true, only returns the filenames of CORE_IMG_JARS in Android.common_path.mk.
std::vector<std::string> GetLibCoreDexFileNames(const std::string& prefix, bool core_only = false);
@@ -46,6 +67,8 @@ std::vector<std::string> GetLibCoreDexLocations(const std::vector<std::string>&
// If `core_only` is true, only returns the filenames of CORE_IMG_JARS in Android.common_path.mk.
std::vector<std::string> GetLibCoreDexLocations(bool core_only = false);
+std::string GetClassPathOption(const char* option, const std::vector<std::string>& class_path);
+
} // namespace testing
} // namespace art
diff --git a/libartbase/base/time_utils.h b/libartbase/base/time_utils.h
index dd73b1c951..ddabb1289f 100644
--- a/libartbase/base/time_utils.h
+++ b/libartbase/base/time_utils.h
@@ -26,6 +26,8 @@
#include <cstdint>
#include <string>
+#include "android-base/logging.h"
+
namespace art {
enum TimeUnit {
@@ -123,6 +125,13 @@ void NanoSleep(uint64_t ns);
// time corresponding to the indicated clock value plus the supplied offset.
void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts);
+// Converts `timespec` to nanoseconds. The return value can be negative, which should be interpreted
+// as a time before the epoch.
+static constexpr int64_t TimeSpecToNs(timespec ts) {
+ DCHECK_GE(ts.tv_nsec, 0); // According to POSIX.
+ return static_cast<int64_t>(ts.tv_sec) * INT64_C(1000000000) + ts.tv_nsec;
+}
+
} // namespace art
#endif // ART_LIBARTBASE_BASE_TIME_UTILS_H_
diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc
index 374edc96e7..3de3bd91a2 100644
--- a/libartbase/base/unix_file/fd_file_test.cc
+++ b/libartbase/base/unix_file/fd_file_test.cc
@@ -357,7 +357,7 @@ TEST_F(FdFileTest, CopySparseFullCopy) {
TEST_DISABLED_FOR_HOST();
auto verify_fullcopy = [&](size_t empty_prefix, size_t empty_suffix) {
- SCOPED_TRACE(testing::Message() << "prefix:" << empty_prefix << ", suffix:" << empty_suffix);
+ SCOPED_TRACE(::testing::Message() << "prefix:" << empty_prefix << ", suffix:" << empty_suffix);
std::unique_ptr<art::ScratchFile> src;
ASSERT_NO_FATAL_FAILURE(CreateSparseSourceFile(empty_prefix, empty_suffix, src));
@@ -435,9 +435,9 @@ TEST_F(FdFileTest, CopySparsePartialCopy) {
size_t copy_end_offset) {
// The copy starts <copy_start_offset> from the start of the source file.
// The copy ends <copy_end_offset> from the end of the source file.
- SCOPED_TRACE(testing::Message() << "prefix:" << empty_prefix << ", suffix:" << empty_suffix
- << ", copy_start_offset:" << copy_start_offset << ", copy_end_offset:"
- << copy_end_offset);
+ SCOPED_TRACE(::testing::Message() << "prefix:" << empty_prefix << ", suffix:" << empty_suffix
+ << ", copy_start_offset:" << copy_start_offset
+ << ", copy_end_offset:" << copy_end_offset);
std::unique_ptr<art::ScratchFile> src;
ASSERT_NO_FATAL_FAILURE(CreateSparseSourceFile(empty_prefix, empty_suffix, src));
diff --git a/libartbase/base/unix_file/random_access_file_test.h b/libartbase/base/unix_file/random_access_file_test.h
index 0592256291..abcc161aab 100644
--- a/libartbase/base/unix_file/random_access_file_test.h
+++ b/libartbase/base/unix_file/random_access_file_test.h
@@ -25,7 +25,7 @@
namespace unix_file {
-class RandomAccessFileTest : public testing::Test {
+class RandomAccessFileTest : public ::testing::Test {
protected:
virtual ~RandomAccessFileTest() {
}
diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc
index a422fc5369..88d8f07741 100644
--- a/libartbase/base/utils_test.cc
+++ b/libartbase/base/utils_test.cc
@@ -22,7 +22,7 @@
namespace art {
-class UtilsTest : public testing::Test {};
+class UtilsTest : public ::testing::Test {};
TEST_F(UtilsTest, PrettySize) {
EXPECT_EQ("1024MB", PrettySize(1 * GB));
@@ -114,10 +114,10 @@ TEST_F(UtilsTest, Split) {
}
TEST_F(UtilsTest, GetProcessStatus) {
- EXPECT_THAT(GetProcessStatus("Name"),
- testing::AnyOf(
- "art_libartbase_", // Test binary name: `art_libartbase_test`.
- "art_standalone_")); // Test binary name: `art_standalone_libartbase_test`.
+ EXPECT_THAT(
+ GetProcessStatus("Name"),
+ ::testing::AnyOf("art_libartbase_", // Test binary name: `art_libartbase_test`.
+ "art_standalone_")); // Test binary name: `art_standalone_libartbase_test`.
EXPECT_EQ("R (running)", GetProcessStatus("State"));
EXPECT_EQ("<unknown>", GetProcessStatus("tate"));
EXPECT_EQ("<unknown>", GetProcessStatus("e"));
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
index 89607219cc..29ce22bcc4 100644
--- a/libartpalette/Android.bp
+++ b/libartpalette/Android.bp
@@ -141,8 +141,7 @@ art_cc_defaults {
},
}
-// Version of ART gtest `art_libartpalette_tests` for host.
-// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
+// Version of API coverage test for host.
art_cc_test {
name: "art_libartpalette_tests",
defaults: [
@@ -153,17 +152,41 @@ art_cc_test {
device_supported: false,
}
-// Standalone version of ART gtest `art_libartpalette_tests`, not bundled with the ART APEX on
-// target.
+// MCTS test for API coverage. This test starts a VM to check the JNI
+// notification callbacks, so it should not use art_standalone_gtest_defaults,
+// which statically links a runtime via libart-gtest.
art_cc_test {
name: "art_standalone_libartpalette_tests",
defaults: [
- "art_standalone_gtest_defaults",
+ "art_standalone_test_defaults",
"art_libartpalette_tests_defaults",
],
+ static_libs: [
+ "libartbase-testing",
+ "libartpalette",
+ ],
+ shared_libs: [
+ "liblog",
+ // Bypass stubs to get access to the platform-only JniInvocation APIs.
+ // They're not NDK APIs, but have the same stability requirements.
+ "libnativehelper#impl",
+ ],
test_config_template: ":art-gtests-target-standalone-cts-template",
test_suites: [
"cts",
+ "general-tests",
"mcts-art",
+ "mts-art",
],
+
+ // Duplicated from art_standalone_gtest_defaults
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
}
diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc
index 744d6a8d30..a72e523365 100644
--- a/libartpalette/apex/palette_test.cc
+++ b/libartpalette/apex/palette_test.cc
@@ -23,12 +23,13 @@
#include <cstring>
-#include "base/common_art_test.h"
+#include "base/testing.h"
#include "gtest/gtest.h"
#ifdef ART_TARGET_ANDROID
#include "android-modules-utils/sdk_level.h"
#include "android/api-level.h"
+#include "nativehelper/JniInvocation.h"
#endif
namespace {
@@ -58,7 +59,7 @@ bool PaletteDebugStoreIsSupported() {
} // namespace
-class PaletteClientTest : public testing::Test {};
+class PaletteClientTest : public ::testing::Test {};
TEST_F(PaletteClientTest, SchedPriority) {
int32_t tid = GetTid();
@@ -92,16 +93,25 @@ TEST_F(PaletteClientTest, Ashmem) {
#endif
}
-class PaletteClientJniTest : public art::CommonArtTest {};
-
-TEST_F(PaletteClientJniTest, JniInvocation) {
+TEST_F(PaletteClientTest, JniInvocation) {
+#ifndef ART_TARGET_ANDROID
+ // On host we need to use the runtime linked into the test to start a VM (e.g.
+ // by inheriting CommonArtTest), while on device it needs to launch the
+ // runtime through libnativehelper. Let's not bother on host since this test
+ // is only for native API coverage on device.
+ GTEST_SKIP() << "Will only spin up a VM on Android";
+#else
bool enabled;
EXPECT_EQ(PALETTE_STATUS_OK, PaletteShouldReportJniInvocations(&enabled));
+ // Load the default JNI_CreateJavaVM implementation, i.e., libart.so.
+ JniInvocation jni_invocation;
+ ASSERT_TRUE(jni_invocation.Init(/*library=*/ nullptr));
+
std::string boot_class_path_string =
- GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames());
- std::string boot_class_path_locations_string =
- GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations());
+ art::testing::GetClassPathOption("-Xbootclasspath:", art::testing::GetLibCoreDexFileNames());
+ std::string boot_class_path_locations_string = art::testing::GetClassPathOption(
+ "-Xbootclasspath-locations:", art::testing::GetLibCoreDexLocations());
JavaVMOption options[] = {
{.optionString = boot_class_path_string.c_str(), .extraInfo = nullptr},
@@ -123,6 +133,7 @@ TEST_F(PaletteClientJniTest, JniInvocation) {
PaletteNotifyEndJniInvocation(env);
EXPECT_EQ(JNI_OK, jvm->DestroyJavaVM());
+#endif
}
TEST_F(PaletteClientTest, SetTaskProfiles) {
diff --git a/libartservice/service/java/com/android/server/art/AidlUtils.java b/libartservice/service/java/com/android/server/art/AidlUtils.java
index ad73d6d4c3..0932087289 100644
--- a/libartservice/service/java/com/android/server/art/AidlUtils.java
+++ b/libartservice/service/java/com/android/server/art/AidlUtils.java
@@ -223,6 +223,26 @@ public final class AidlUtils {
}
@NonNull
+ public static SecureDexMetadataWithCompanionPaths buildSecureDexMetadataWithCompanionPaths(
+ @NonNull String dexPath, @NonNull String isa, boolean isInDalvikCache) {
+ var paths = new SecureDexMetadataWithCompanionPaths();
+ paths.dexPath = dexPath;
+ paths.isa = isa;
+ paths.isInDalvikCache = isInDalvikCache;
+ return paths;
+ }
+
+ @NonNull
+ public static OutputSecureDexMetadataCompanion buildOutputSecureDexMetadataCompanion(
+ @NonNull String dexPath, @NonNull String isa, boolean isInDalvikCache,
+ @NonNull PermissionSettings permissionSettings) {
+ var outputSdc = new OutputSecureDexMetadataCompanion();
+ outputSdc.sdcPath = buildSecureDexMetadataWithCompanionPaths(dexPath, isa, isInDalvikCache);
+ outputSdc.permissionSettings = permissionSettings;
+ return outputSdc;
+ }
+
+ @NonNull
public static WritableProfilePath toWritableProfilePath(@NonNull ProfilePath profile) {
switch (profile.getTag()) {
case ProfilePath.primaryRefProfilePath:
@@ -288,4 +308,11 @@ public final class AidlUtils {
+ "got " + profile.getTag());
}
}
+
+ @NonNull
+ public static String toString(@NonNull SecureDexMetadataWithCompanionPaths paths) {
+ return String.format(
+ "SecureDexMetadataWithCompanionPaths[dexPath = %s, isa = %s, isInDalvikCache = %b]",
+ paths.dexPath, paths.isa, paths.isInDalvikCache);
+ }
}
diff --git a/libartservice/service/java/com/android/server/art/ArtFileManager.java b/libartservice/service/java/com/android/server/art/ArtFileManager.java
index 754b9ec1dd..534952a304 100644
--- a/libartservice/service/java/com/android/server/art/ArtFileManager.java
+++ b/libartservice/service/java/com/android/server/art/ArtFileManager.java
@@ -45,7 +45,6 @@ import dalvik.system.DexFile;
import com.google.auto.value.AutoValue;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -102,6 +101,7 @@ public class ArtFileManager {
public WritableArtifactLists getWritableArtifacts(@NonNull PackageState pkgState,
@NonNull AndroidPackage pkg, @NonNull Options options) throws RemoteException {
List<ArtifactsPath> artifacts = new ArrayList<>();
+ List<SecureDexMetadataWithCompanionPaths> sdmFiles = new ArrayList<>();
List<RuntimeArtifactsPath> runtimeArtifacts = new ArrayList<>();
if (options.forPrimaryDex()) {
@@ -110,6 +110,9 @@ public class ArtFileManager {
for (Abi abi : Utils.getAllAbis(pkgState)) {
artifacts.add(AidlUtils.buildArtifactsPathAsInput(
dexInfo.dexPath(), abi.isa(), isInDalvikCache));
+ // SDM files are only for primary dex files.
+ sdmFiles.add(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ dexInfo.dexPath(), abi.isa(), isInDalvikCache));
// Runtime images are only generated for primary dex files.
runtimeArtifacts.add(AidlUtils.buildRuntimeArtifactsPath(
pkgState.getPackageName(), dexInfo.dexPath(), abi.isa()));
@@ -126,7 +129,7 @@ public class ArtFileManager {
}
}
- return WritableArtifactLists.create(artifacts, runtimeArtifacts);
+ return new WritableArtifactLists(artifacts, sdmFiles, runtimeArtifacts);
}
/** Returns artifacts that are usable, regardless of whether they are writable. */
@@ -135,6 +138,7 @@ public class ArtFileManager {
@NonNull PackageState pkgState, @NonNull AndroidPackage pkg) throws RemoteException {
List<ArtifactsPath> artifacts = new ArrayList<>();
List<VdexPath> vdexFiles = new ArrayList<>();
+ List<SecureDexMetadataWithCompanionPaths> sdmFiles = new ArrayList<>();
List<RuntimeArtifactsPath> runtimeArtifacts = new ArrayList<>();
var options = ArtFileManager.Options.builder()
@@ -159,9 +163,30 @@ public class ArtFileManager {
} else {
artifacts.add(thisArtifacts);
}
+ } else if (result.artifactsLocation == ArtifactsLocation.SDM_DALVIK_CACHE
+ || result.artifactsLocation == ArtifactsLocation.SDM_NEXT_TO_DEX) {
+ sdmFiles.add(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ dexInfo.dexPath(), abi.isa(),
+ result.artifactsLocation == ArtifactsLocation.SDM_DALVIK_CACHE));
+ }
+
+ if (result.artifactsLocation != ArtifactsLocation.NONE_OR_ERROR) {
// Runtime images are only generated for primary dex files.
if (dexInfo instanceof DetailedPrimaryDexInfo
&& !DexFile.isOptimizedCompilerFilter(result.compilerFilter)) {
+ // Those not added to the list are definitely unusable, but those added to
+ // the list are not necessarily usable. For example, runtime artifacts can
+ // be outdated when the corresponding dex file is updated, but they may
+ // still show up in this list.
+ //
+ // However, this is not a severe problem. For `ArtManagerLocal.cleanup`, the
+ // worst result is only that we are keeping more runtime artifacts than
+ // needed. For `ArtManagerLocal.getArtManagedFileStats`, this is an edge
+ // case because the API call is transitively initiated by the app itself,
+ // and the runtime refreshes unusable runtime artifacts as soon as the app
+ // starts.
+ //
+ // TODO(jiakaiz): Improve this.
runtimeArtifacts.add(AidlUtils.buildRuntimeArtifactsPath(
pkgState.getPackageName(), dexInfo.dexPath(), abi.isa()));
}
@@ -176,7 +201,7 @@ public class ArtFileManager {
}
}
- return UsableArtifactLists.create(artifacts, vdexFiles, runtimeArtifacts);
+ return new UsableArtifactLists(artifacts, vdexFiles, sdmFiles, runtimeArtifacts);
}
@NonNull
@@ -209,7 +234,7 @@ public class ArtFileManager {
}
}
- return ProfileLists.create(refProfiles, curProfiles);
+ return new ProfileLists(refProfiles, curProfiles);
}
@NonNull
@@ -221,71 +246,17 @@ public class ArtFileManager {
: mInjector.getDexUseManager().getSecondaryDexInfo(pkgState.getPackageName());
}
- @Immutable
- @AutoValue
- @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava.
- public abstract static class WritableArtifactLists {
- protected WritableArtifactLists() {}
-
- public static @NonNull WritableArtifactLists create(@NonNull List<ArtifactsPath> artifacts,
- @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {
- return new AutoValue_ArtFileManager_WritableArtifactLists(
- Collections.unmodifiableList(artifacts),
- Collections.unmodifiableList(runtimeArtifacts));
- }
-
- public abstract @NonNull List<ArtifactsPath> artifacts();
- public abstract @NonNull List<RuntimeArtifactsPath> runtimeArtifacts();
- }
-
- @Immutable
- @AutoValue
- @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava.
- public abstract static class UsableArtifactLists {
- protected UsableArtifactLists() {}
-
- public static @NonNull UsableArtifactLists create(@NonNull List<ArtifactsPath> artifacts,
- @NonNull List<VdexPath> vdexFiles,
- @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {
- return new AutoValue_ArtFileManager_UsableArtifactLists(
- Collections.unmodifiableList(artifacts),
- Collections.unmodifiableList(vdexFiles),
- Collections.unmodifiableList(runtimeArtifacts));
- }
-
- public abstract @NonNull List<ArtifactsPath> artifacts();
- public abstract @NonNull List<VdexPath> vdexFiles();
-
- // Those not added to the list are definitely unusable, but those added to the list are not
- // necessarily usable. For example, runtime artifacts can be outdated when the corresponding
- // dex file is updated, but they may still show up in this list.
- //
- // However, this is not a severe problem. For `ArtManagerLocal.cleanup`, the worst result is
- // only that we are keeping more runtime artifacts than needed. For
- // `ArtManagerLocal.getArtManagedFileStats`, this is an edge case because the API call is
- // transitively initiated by the app itself, and the runtime refreshes unusable runtime
- // artifacts as soon as the app starts.
- //
- // TODO(jiakaiz): Improve this.
- public abstract @NonNull List<RuntimeArtifactsPath> runtimeArtifacts();
- }
-
- @Immutable
- @AutoValue
- @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava.
- public abstract static class ProfileLists {
- protected ProfileLists() {}
-
- public static @NonNull ProfileLists create(
- @NonNull List<ProfilePath> refProfiles, @NonNull List<ProfilePath> curProfiles) {
- return new AutoValue_ArtFileManager_ProfileLists(
- Collections.unmodifiableList(refProfiles),
- Collections.unmodifiableList(curProfiles));
- }
+ public record WritableArtifactLists(@NonNull List<ArtifactsPath> artifacts,
+ @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles,
+ @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {}
- public abstract @NonNull List<ProfilePath> refProfiles();
- public abstract @NonNull List<ProfilePath> curProfiles();
+ public record UsableArtifactLists(@NonNull List<ArtifactsPath> artifacts,
+ @NonNull List<VdexPath> vdexFiles,
+ @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles,
+ @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {}
+ public record ProfileLists(
+ @NonNull List<ProfilePath> refProfiles, @NonNull List<ProfilePath> curProfiles) {
public @NonNull List<ProfilePath> allProfiles() {
List<ProfilePath> profiles = new ArrayList<>();
profiles.addAll(refProfiles());
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index 735da25fbb..993cb2c558 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -202,8 +202,8 @@ public final class ArtManagerLocal {
}
/**
- * Deletes dexopt artifacts of a package, including the artifacts for primary dex files and the
- * ones for secondary dex files. This includes VDEX, ODEX, and ART files.
+ * Deletes dexopt artifacts (including cloud dexopt artifacts) of a package, for primary dex
+ * files and for secondary dex files. This includes VDEX, ODEX, ART, SDM, and SDC files.
*
* Also deletes runtime artifacts of the package, though they are not dexopt artifacts.
*
@@ -232,6 +232,9 @@ public final class ArtManagerLocal {
for (RuntimeArtifactsPath runtimeArtifacts : list.runtimeArtifacts()) {
freedBytes += mInjector.getArtd().deleteRuntimeArtifacts(runtimeArtifacts);
}
+ for (SecureDexMetadataWithCompanionPaths sdmSdcFiles : list.sdmFiles()) {
+ freedBytes += mInjector.getArtd().deleteSdmSdcFiles(sdmSdcFiles);
+ }
return DeleteResult.create(freedBytes);
} catch (RemoteException e) {
Utils.logArtdException(e);
@@ -390,13 +393,17 @@ public final class ArtManagerLocal {
}
/**
- * Resets the dexopt state of the package as if the package is newly installed.
+ * Resets the dexopt state of the package as if the package is newly installed without cloud
+ * dexopt artifacts (SDM files).
*
- * More specifically, it clears reference profiles, current profiles, any code compiled from
- * those local profiles, and runtime artifacts. If there is an external profile (e.g., a cloud
- * profile), the code compiled from that profile will be kept.
+ * More specifically,
+ * - It clears current profiles, reference profiles, and all dexopt artifacts (including cloud
+ * dexopt artifacts).
+ * - If there is an external profile (e.g., a cloud profile), the reference profile will be
+ * re-created from that profile, and dexopt artifacts will be regenerated for that profile.
*
- * For secondary dex files, it also clears all dexopt artifacts.
+ * For secondary dex files, it clears all profiles and dexopt artifacts without regeneration
+ * because secondary dex files are supposed to be unknown at install time.
*
* @hide
*/
@@ -1056,6 +1063,10 @@ public final class ArtManagerLocal {
for (RuntimeArtifactsPath runtimeArtifacts : artifactLists.runtimeArtifacts()) {
artifactsSize += artd.getRuntimeArtifactsSize(runtimeArtifacts);
}
+ for (SecureDexMetadataWithCompanionPaths sdmFile : artifactLists.sdmFiles()) {
+ // We don't count SDC files because they are presumed to be tiny.
+ artifactsSize += artd.getSdmFileSize(sdmFile);
+ }
ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg,
ArtFileManager.Options.builder()
@@ -1119,6 +1130,7 @@ public final class ArtManagerLocal {
// - The dexopt artifacts, if they are up-to-date and the app is not hibernating.
// - Only the VDEX part of the dexopt artifacts, if the dexopt artifacts are outdated
// but the VDEX part is still usable and the app is not hibernating.
+ // - The SDM and SDC files, if they are up-to-date and the app is not hibernating.
// - The runtime artifacts, if dexopt artifacts are fully or partially usable and the
// usable parts don't contain AOT-compiled code. (This logic must be aligned with the
// one that determines when runtime images can be loaded in
@@ -1126,6 +1138,7 @@ public final class ArtManagerLocal {
List<ProfilePath> profilesToKeep = new ArrayList<>();
List<ArtifactsPath> artifactsToKeep = new ArrayList<>();
List<VdexPath> vdexFilesToKeep = new ArrayList<>();
+ List<SecureDexMetadataWithCompanionPaths> sdmSdcFilesToKeep = new ArrayList<>();
List<RuntimeArtifactsPath> runtimeArtifactsToKeep = new ArrayList<>();
for (PackageState pkgState : snapshot.getPackageStates().values()) {
@@ -1146,11 +1159,12 @@ public final class ArtManagerLocal {
mInjector.getArtFileManager().getUsableArtifacts(pkgState, pkg);
artifactsToKeep.addAll(artifactLists.artifacts());
vdexFilesToKeep.addAll(artifactLists.vdexFiles());
+ sdmSdcFilesToKeep.addAll(artifactLists.sdmFiles());
runtimeArtifactsToKeep.addAll(artifactLists.runtimeArtifacts());
}
}
return mInjector.getArtd().cleanup(profilesToKeep, artifactsToKeep, vdexFilesToKeep,
- runtimeArtifactsToKeep,
+ sdmSdcFilesToKeep, runtimeArtifactsToKeep,
SdkLevel.isAtLeastV() && mInjector.getPreRebootDexoptJob().hasStarted());
} catch (RemoteException e) {
Utils.logArtdException(e);
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
index f83f5cf083..a3b2fba42a 100644
--- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -967,13 +967,16 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
pw.println(" -f Force dexopt, also when the compiler filter being applied is not");
pw.println(" better than that of the current dexopt artifacts for a package.");
pw.println(" --reset Reset the dexopt state of the package as if the package is newly");
- pw.println(" installed.");
- pw.println(" More specifically, it clears current profiles, reference profiles");
- pw.println(" from local profiles, and any code compiled from those local profiles.");
- pw.println(" If there is an external profile (e.g., a cloud profile), the reference");
- pw.println(" profile from that profile and the code compiled from that profile will");
- pw.println(" be kept.");
- pw.println(" For secondary dex files, it also clears all dexopt artifacts.");
+ pw.println(" installed without cloud dexopt artifacts (SDM files).");
+ pw.println(" More specifically,");
+ pw.println(" - It clears current profiles, reference profiles, and all dexopt");
+ pw.println(" artifacts (including cloud dexopt artifacts).");
+ pw.println(" - If there is an external profile (e.g., a cloud profile), the");
+ pw.println(" reference profile will be re-created from that profile, and dexopt");
+ pw.println(" artifacts will be regenerated for that profile.");
+ pw.println(" For secondary dex files, it clears all profiles and dexopt artifacts");
+ pw.println(" without regeneration because secondary dex files are supposed to be");
+ pw.println(" unknown at install time.");
pw.println(" When this flag is set, all the other flags are ignored.");
pw.println(" -v Verbose mode. This mode prints detailed results.");
pw.println(" --force-merge-profile Force merge profiles even if the difference between");
@@ -1000,7 +1003,7 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
pw.println();
pw.println("delete-dexopt PACKAGE_NAME");
pw.println(" Delete the dexopt artifacts of both primary dex files and secondary dex");
- pw.println(" files of a package.");
+ pw.println(" files of a package, including cloud dexopt artifacts (SDM files).");
pw.println();
pw.println("bg-dexopt-job [--cancel | --disable | --enable]");
pw.println(" Control the background dexopt job.");
diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
index 9c7f45de24..92a0dab04d 100644
--- a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
@@ -67,8 +67,6 @@ import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
@@ -668,23 +666,6 @@ public class DexUseManagerLocal {
@NonNull String loadingPackageName, boolean isolatedProcess,
@NonNull String classLoaderContext, @NonNull String abiName, long lastUsedAtMs) {
DexLoader loader = DexLoader.create(loadingPackageName, isolatedProcess);
- // This is to avoid a loading package from using up the SecondaryDexUse entries for another
- // package (up to the MAX_SECONDARY_DEX_FILES_PER_OWNER limit).
- // Note that we are using system_server's permission to check the existence. This is fine
- // with the assumption that the file must be world readable to be used by other apps.
- // We could use artd's permission to check the existence, and then there wouldn't be any
- // permission issue, but that requires bringing up the artd service, which may be too
- // expensive.
- // TODO(jiakaiz): Check if the assumption is true.
- // This doesn't apply to secondary dex files that aren't used by other apps, but we
- // don't care about the loading package messing up its own SecondaryDexUse
- // entries.
- // Also note that the check doesn't follow symlinks because GMSCore creates symlinks to
- // its secondary dex files, while system_server doesn't have the permission to follow them.
- if (isLoaderOtherApp(loader, owningPackageName) && !mInjector.pathExists(dexPath)) {
- AsLog.w("Not recording non-existent secondary dex file '" + dexPath + "'");
- return;
- }
synchronized (mLock) {
PackageDexUse packageDexUse = mDexUse.mPackageDexUseByOwningPackageName.computeIfAbsent(
owningPackageName, k -> new PackageDexUse());
@@ -1402,10 +1383,6 @@ public class DexUseManagerLocal {
return System.currentTimeMillis();
}
- public boolean pathExists(String path) {
- return Files.exists(Paths.get(path), LinkOption.NOFOLLOW_LINKS);
- }
-
@NonNull
public String getFilename() {
return FILENAME;
diff --git a/libartservice/service/java/com/android/server/art/Dexopter.java b/libartservice/service/java/com/android/server/art/Dexopter.java
index cc1b66b9ba..1c6abe6c41 100644
--- a/libartservice/service/java/com/android/server/art/Dexopter.java
+++ b/libartservice/service/java/com/android/server/art/Dexopter.java
@@ -113,6 +113,8 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
continue;
}
+ onDexoptStart(dexInfo);
+
String compilerFilter = adjustCompilerFilter(mParams.getCompilerFilter(), dexInfo);
DexMetadataInfo dmInfo =
mInjector.getDexMetadataHelper().getDexMetadataInfo(buildDmPath(dexInfo));
@@ -199,14 +201,15 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
long sizeBeforeBytes = 0;
Dex2OatResult dex2OatResult = Dex2OatResult.notRun();
@DexoptResult.DexoptResultExtendedStatusFlags int extendedStatusFlags = 0;
+ DexoptTarget<DexInfoType> target = null;
try {
- var target = DexoptTarget.<DexInfoType>builder()
- .setDexInfo(dexInfo)
- .setIsa(abi.isa())
- .setIsInDalvikCache(isInDalvikCache)
- .setCompilerFilter(compilerFilter)
- .setDmPath(dmInfo.dmPath())
- .build();
+ target = DexoptTarget.<DexInfoType>builder()
+ .setDexInfo(dexInfo)
+ .setIsa(abi.isa())
+ .setIsInDalvikCache(isInDalvikCache)
+ .setCompilerFilter(compilerFilter)
+ .setDmPath(dmInfo.dmPath())
+ .build();
var options = GetDexoptNeededOptions.builder()
.setProfileMerged(profileMerged)
.setFlags(mParams.getFlags())
@@ -316,6 +319,9 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
AsLog.i(String.format("Dexopt result: [packageName = %s] %s",
mPkgState.getPackageName(), result));
results.add(result);
+
+ onDexoptTargetResult(target, status);
+
if (status != DexoptResult.DEXOPT_SKIPPED
&& status != DexoptResult.DEXOPT_PERFORMED) {
succeeded = false;
@@ -626,7 +632,10 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
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.
+ case ArtifactsLocation.SDM_DALVIK_CACHE:
+ case ArtifactsLocation.SDM_NEXT_TO_DEX:
+ // In these cases, the VDEX file is in the DM file. The whole DM file is passed to
+ // dex2oat as a separate flag whenever it exists.
return null;
default:
// This should never happen as the value is got from artd.
@@ -734,6 +743,18 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
*/
@Nullable protected abstract DexMetadataPath buildDmPath(@NonNull DexInfoType dexInfo);
+ /**
+ * Called at an early stage during dexopt of every dex file, even before dexopt is skipped by
+ * the noop compiler filter.
+ */
+ protected void onDexoptStart(@NonNull DexInfoType dexInfo) throws RemoteException {}
+
+ /**
+ * Called once for every dex file and every ABI when dexopt has a result.
+ */
+ protected void onDexoptTargetResult(@NonNull DexoptTarget<DexInfoType> target,
+ @DexoptResult.DexoptResultStatus int status) throws RemoteException {}
+
@AutoValue
abstract static class DexoptTarget<DexInfoType extends DetailedDexInfo> {
abstract @NonNull DexInfoType dexInfo();
diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java
index eb200592aa..94ab94e213 100644
--- a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java
+++ b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java
@@ -38,6 +38,7 @@ import com.android.modules.utils.pm.PackageStateModulesUtils;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
@@ -178,6 +179,44 @@ public class PrimaryDexopter extends Dexopter<DetailedPrimaryDexInfo> {
return AidlUtils.buildDexMetadataPath(dexInfo.dexPath());
}
+ @Override
+ protected void onDexoptStart(@NonNull DetailedPrimaryDexInfo dexInfo) throws RemoteException {
+ if (!mInjector.isPreReboot() && android.content.pm.Flags.cloudCompilationPm()) {
+ boolean isInDalvikCache = isInDalvikCache();
+ for (Abi abi : getAllAbis(dexInfo)) {
+ maybeCreateSdc(dexInfo, abi.isa(), isInDalvikCache);
+ }
+ }
+ }
+
+ private void maybeCreateSdc(@NonNull DetailedPrimaryDexInfo dexInfo, @NonNull String isa,
+ boolean isInDalvikCache) throws RemoteException {
+ // SDC file doesn't contain sensitive data, so it can always to public.
+ PermissionSettings permissionSettings =
+ getPermissionSettings(dexInfo, true /* canBePublic */);
+ OutputSecureDexMetadataCompanion outputSdc =
+ AidlUtils.buildOutputSecureDexMetadataCompanion(
+ dexInfo.dexPath(), isa, isInDalvikCache, permissionSettings);
+
+ try {
+ mInjector.getArtd().maybeCreateSdc(outputSdc);
+ } catch (ServiceSpecificException e) {
+ AsLog.e("Failed to create sdc for " + AidlUtils.toString(outputSdc.sdcPath), e);
+ }
+ }
+
+ @Override
+ protected void onDexoptTargetResult(@NonNull DexoptTarget<DetailedPrimaryDexInfo> target,
+ @DexoptResult.DexoptResultStatus int status) throws RemoteException {
+ // An optimization to release disk space as soon as possible. The SDM and SDC files would be
+ // deleted by the file GC anyway if not deleted here.
+ if (status == DexoptResult.DEXOPT_PERFORMED && !mInjector.isPreReboot()) {
+ mInjector.getArtd().deleteSdmSdcFiles(
+ AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ target.dexInfo().dexPath(), target.isa(), target.isInDalvikCache()));
+ }
+ }
+
private boolean isSharedLibrary() {
return PackageStateModulesUtils.isLoadableInOtherProcesses(mPkgState, true /* codeOnly */);
}
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index a791211705..754208a05f 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -333,6 +333,15 @@ public class ArtManagerLocalTest {
verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput(
"/data/user/0/foo/not_found.apk", "arm64", false /* isInDalvikCache */)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache)));
+
verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath(
PKG_NAME_1, "/somewhere/app/foo/base.apk", "arm64")));
verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath(
@@ -344,6 +353,7 @@ public class ArtManagerLocalTest {
// Verify that there are no more calls than the ones above.
verify(mArtd, times(6)).deleteArtifacts(any());
+ verify(mArtd, times(4)).deleteSdmSdcFiles(any());
verify(mArtd, times(4)).deleteRuntimeArtifacts(any());
}
@@ -572,6 +582,15 @@ public class ArtManagerLocalTest {
verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput(
"/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache)));
+ verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache)));
+
verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath(
PKG_NAME_1, "/somewhere/app/foo/base.apk", "arm64")));
verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath(
@@ -1268,6 +1287,7 @@ public class ArtManagerLocalTest {
"arm64", true /* isInDalvikCache */)),
inAnyOrderDeepEquals(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput(
"/somewhere/app/foo/split_0.apk", "arm", false /* isInDalvikCache */))),
+ inAnyOrderDeepEquals() /* sdmSdcFilesToKeep */,
inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath(
PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"),
AidlUtils.buildRuntimeArtifactsPath(
@@ -1276,6 +1296,60 @@ public class ArtManagerLocalTest {
}
@Test
+ public void testCleanupDmAndSdm() throws Exception {
+ when(mPreRebootDexoptJob.hasStarted()).thenReturn(false);
+
+ // It should keep the SDM file, but not runtime images.
+ doReturn(createGetDexoptStatusResult(
+ "speed-profile", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any());
+
+ // It should keep the SDM file, but not runtime images.
+ doReturn(createGetDexoptStatusResult(
+ "speed-profile", "cloud", "location", ArtifactsLocation.SDM_DALVIK_CACHE))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any());
+
+ // It should keep the SDM file and runtime images.
+ doReturn(createGetDexoptStatusResult(
+ "verify", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any());
+
+ // It should only keep runtime images.
+ doReturn(createGetDexoptStatusResult("verify", "vdex", "location", ArtifactsLocation.DM))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any());
+
+ // This file is uninteresting in this test.
+ doReturn(createGetDexoptStatusResult(
+ "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR))
+ .when(mArtd)
+ .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any());
+
+ when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1));
+ mArtManagerLocal.cleanup(mSnapshot);
+
+ verify(mArtd).cleanup(any() /* profilesToKeep */,
+ inAnyOrderDeepEquals() /* artifactsToKeep */,
+ inAnyOrderDeepEquals() /* vdexFilesToKeep */,
+ inAnyOrderDeepEquals(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm64",
+ false /* isInDalvikCache */),
+ AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm", true /* isInDalvikCache */),
+ AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm64",
+ false /* isInDalvikCache */)),
+ inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath(
+ PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"),
+ AidlUtils.buildRuntimeArtifactsPath(
+ PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm")),
+ eq(false) /* keepPreRebootStagedFiles */);
+ }
+
+ @Test
public void testGetArtManagedFileStatsSystem() throws Exception {
testGetArtManagedFileStats(true /* isSystemOrRootOrShell */);
}
@@ -1426,6 +1500,64 @@ public class ArtManagerLocalTest {
}
@Test
+ public void testGetArtManagedFileStatsDmAndSdm() throws Exception {
+ // It should count the SDM file, but not runtime images.
+ doReturn(createGetDexoptStatusResult(
+ "speed-profile", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any());
+
+ // It should count the SDM file, but not runtime images.
+ doReturn(createGetDexoptStatusResult(
+ "speed-profile", "cloud", "location", ArtifactsLocation.SDM_DALVIK_CACHE))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any());
+
+ // It should count the SDM file and runtime images.
+ doReturn(createGetDexoptStatusResult(
+ "verify", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any());
+
+ // It should only count runtime images.
+ doReturn(createGetDexoptStatusResult("verify", "vdex", "location", ArtifactsLocation.DM))
+ .when(mArtd)
+ .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any());
+
+ // This file is uninteresting in this test.
+ doReturn(createGetDexoptStatusResult(
+ "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR))
+ .when(mArtd)
+ .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any());
+
+ // These are counted as TYPE_DEXOPT_ARTIFACT.
+ doReturn(1l << 0).when(mArtd).getSdmFileSize(
+ deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm64", false /* isInDalvikCache */)));
+ doReturn(1l << 1).when(mArtd).getSdmFileSize(
+ deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm", true /* isInDalvikCache */)));
+ doReturn(1l << 2).when(mArtd).getSdmFileSize(
+ deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm64", false /* isInDalvikCache */)));
+ doReturn(1l << 3).when(mArtd).getRuntimeArtifactsSize(
+ deepEq(AidlUtils.buildRuntimeArtifactsPath(
+ PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64")));
+ doReturn(1l << 4).when(mArtd).getRuntimeArtifactsSize(
+ deepEq(AidlUtils.buildRuntimeArtifactsPath(
+ PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm")));
+
+ ArtManagedFileStats stats = mArtManagerLocal.getArtManagedFileStats(mSnapshot, PKG_NAME_1);
+ assertThat(stats.getTotalSizeBytesByType(ArtManagedFileStats.TYPE_DEXOPT_ARTIFACT))
+ .isEqualTo((1l << 0) + (1l << 1) + (1l << 2) + (1l << 3) + (1l << 4));
+
+ verify(mArtd, never()).getArtifactsSize(any());
+ verify(mArtd, never()).getVdexFileSize(any());
+ verify(mArtd, times(3)).getSdmFileSize(any());
+ verify(mArtd, times(2)).getRuntimeArtifactsSize(any());
+ }
+
+ @Test
public void testCommitPreRebootStagedFiles() throws Exception {
when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1));
diff --git a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
index f0002b80d7..1627c5eeef 100644
--- a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
+++ b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
@@ -177,7 +177,6 @@ public class DexUseManagerTest {
lenient().when(mInjector.getArtd()).thenReturn(mArtd);
lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(0l);
- lenient().when(mInjector.pathExists(any())).thenReturn(true);
lenient().when(mInjector.getFilename()).thenReturn(mTempFile.getPath());
lenient()
.when(mInjector.createScheduledExecutor())
@@ -909,12 +908,11 @@ public class DexUseManagerTest {
}
@Test
- public void testExistingExternalSecondaryDexPath() throws Exception {
+ public void testSecondaryDexPath() throws Exception {
mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS); // Save.
long oldFileSize = mTempFile.length();
String existingDexPath = mCeDir + "/foo.apk";
- when(mInjector.pathExists(existingDexPath)).thenReturn(true);
mDexUseManager.notifyDexContainersLoaded(
mSnapshot, LOADING_PKG_NAME, Map.of(existingDexPath, "PCL[]"));
@@ -923,35 +921,6 @@ public class DexUseManagerTest {
}
@Test
- public void testNonexistingExternalSecondaryDexPath() throws Exception {
- mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS); // Save.
- long oldFileSize = mTempFile.length();
-
- String nonexistingDexPath = mCeDir + "/foo.apk";
- when(mInjector.pathExists(nonexistingDexPath)).thenReturn(false);
- mDexUseManager.notifyDexContainersLoaded(
- mSnapshot, LOADING_PKG_NAME, Map.of(nonexistingDexPath, "PCL[]"));
-
- mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS); // Save.
- assertThat(mTempFile.length()).isEqualTo(oldFileSize);
- }
-
- @Test
- public void testInternalSecondaryDexPath() throws Exception {
- mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS); // Save.
- long oldFileSize = mTempFile.length();
-
- String nonexistingDexPath = mCeDir + "/foo.apk";
- lenient().when(mInjector.pathExists(nonexistingDexPath)).thenReturn(false);
- mDexUseManager.notifyDexContainersLoaded(
- mSnapshot, OWNING_PKG_NAME, Map.of(nonexistingDexPath, "PCL[]"));
- verify(mArtd, never()).getDexFileVisibility(nonexistingDexPath);
-
- mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS); // Save.
- assertThat(mTempFile.length()).isGreaterThan(oldFileSize);
- }
-
- @Test
public void testLimitSecondaryDexFiles() throws Exception {
for (int n = 0; n < MAX_SECONDARY_DEX_FILES_PER_OWNER_FOR_TESTING - 1; ++n) {
mDexUseManager.notifyDexContainersLoaded(mSnapshot, LOADING_PKG_NAME,
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
index 10f9f59e91..9c3f87e92e 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
@@ -202,6 +202,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
params.mIsPreReboot = true;
params.mExpectedOutputIsPreReboot = true;
params.mExpectedDeletesRuntimeArtifacts = false;
+ params.mExpectedDeletesSdmSdcFiles = false;
list.add(params);
params = new Params();
@@ -385,6 +386,17 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
PKG_NAME, "/somewhere/app/foo/split_0.apk", "arm")));
}
+ if (mParams.mExpectedDeletesSdmSdcFiles) {
+ // Only delete SDM and SDC files for successful dexopt operations, namely the first one
+ // and the fourth one.
+ doReturn(1l).when(mArtd).deleteSdmSdcFiles(
+ deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/base.apk", "arm64", mParams.mIsInDalvikCache)));
+ doReturn(1l).when(mArtd).deleteSdmSdcFiles(
+ deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths(
+ "/somewhere/app/foo/split_0.apk", "arm", mParams.mIsInDalvikCache)));
+ }
+
assertThat(mPrimaryDexopter.dexopt())
.comparingElementsUsing(TestingUtils.<DexContainerFileDexoptResult>deepEquality())
.containsExactly(
@@ -416,6 +428,10 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
if (!mParams.mExpectedDeletesRuntimeArtifacts) {
verify(mArtd, times(0)).deleteRuntimeArtifacts(any());
}
+
+ if (!mParams.mExpectedDeletesSdmSdcFiles) {
+ verify(mArtd, times(0)).deleteSdmSdcFiles(any());
+ }
}
private static class Params {
@@ -451,6 +467,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
public boolean mExpectedIsHiddenApiPolicyEnabled = true;
public boolean mExpectedOutputIsPreReboot = false;
public boolean mExpectedDeletesRuntimeArtifacts = true;
+ public boolean mExpectedDeletesSdmSdcFiles = true;
public String toString() {
return String.format("isInDalvikCache=%b,"
@@ -477,7 +494,8 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
+ "expectedIsDebuggable=%b,"
+ "expectedIsHiddenApiPolicyEnabled=%b,"
+ "expectedOutputIsPreReboot=%b,"
- + "expectedDeleteRuntimeArtifacts=%b",
+ + "expectedDeletesRuntimeArtifacts=%b,"
+ + "expectedDeletesSdmSdcFiles=%b",
mIsInDalvikCache, mHiddenApiEnforcementPolicy, mIsVmSafeMode, mIsDebuggable,
mIsSystemUi, mIsLauncher, mIsUseEmbeddedDex, mIsSanboxSdkLib,
mRequestedCompilerFilter, mCallbackReturnedCompilerFilter, mForce,
@@ -485,7 +503,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
mForceCompilerFilter, mAlwaysDebuggable, mExpectedCallbackInputCompilerFilter,
mExpectedCompilerFilter, mExpectedDexoptTrigger, mExpectedIsDebuggable,
mExpectedIsHiddenApiPolicyEnabled, mExpectedOutputIsPreReboot,
- mExpectedDeletesRuntimeArtifacts);
+ mExpectedDeletesRuntimeArtifacts, mExpectedDeletesSdmSdcFiles);
}
}
}
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
index 02ae2fe218..b032538bbb 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
@@ -16,6 +16,7 @@
package com.android.server.art;
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
import static com.android.server.art.testing.TestingUtils.deepEq;
@@ -62,6 +63,7 @@ import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import java.util.zip.ZipFile;
@SmallTest
@@ -156,50 +158,52 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
mUsedEmbeddedProfiles = new ArrayList<>();
}
- @Test
- public void testDexoptInputVdex() throws Exception {
- // null.
- doReturn(dexoptIsNeeded(ArtifactsLocation.NONE_OR_ERROR))
+ private void checkDexoptInputVdex(
+ @ArtifactsLocation int location, Supplier<VdexPath> inputVdexMatcher) throws Exception {
+ doReturn(dexoptIsNeeded(location))
.when(mArtd)
.getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), any(), anyInt());
- doReturn(mArtdDexoptResult)
- .when(mArtd)
- .dexopt(any(), eq(mDexPath), eq("arm64"), any(), any(), any(), isNull(), any(),
- anyInt(), any(), any());
- // ArtifactsPath, isInDalvikCache=true.
- doReturn(dexoptIsNeeded(ArtifactsLocation.DALVIK_CACHE))
- .when(mArtd)
- .getDexoptNeeded(eq(mDexPath), eq("arm"), any(), any(), anyInt());
- doReturn(mArtdDexoptResult)
- .when(mArtd)
- .dexopt(any(), eq(mDexPath), eq("arm"), any(), any(), any(),
- deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput(
- mDexPath, "arm", true /* isInDalvikCache */))),
- any(), anyInt(), any(), any());
+ List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt();
+ verifyStatusAllOk(results);
+ verify(mArtd).dexopt(any(), eq(mDexPath), eq("arm64"), any(), any(), any(),
+ inputVdexMatcher.get(), any(), anyInt(), any(), any());
+ }
- // ArtifactsPath, isInDalvikCache=false.
- doReturn(dexoptIsNeeded(ArtifactsLocation.NEXT_TO_DEX))
- .when(mArtd)
- .getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), any(), anyInt());
- doReturn(mArtdDexoptResult)
- .when(mArtd)
- .dexopt(any(), eq(mSplit0DexPath), eq("arm64"), any(), any(), any(),
- deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput(
- mSplit0DexPath, "arm64", false /* isInDalvikCache */))),
- any(), anyInt(), any(), any());
+ @Test
+ public void testDexoptInputVdexNoneOrError() throws Exception {
+ checkDexoptInputVdex(ArtifactsLocation.NONE_OR_ERROR, () -> isNull());
+ }
- // DexMetadataPath.
- doReturn(dexoptIsNeeded(ArtifactsLocation.DM))
- .when(mArtd)
- .getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), any(), anyInt());
- doReturn(mArtdDexoptResult)
- .when(mArtd)
- .dexopt(any(), eq(mSplit0DexPath), eq("arm"), any(), any(), any(), isNull(), any(),
- anyInt(), any(), any());
+ @Test
+ public void testDexoptInputVdexDalvikCache() throws Exception {
+ checkDexoptInputVdex(ArtifactsLocation.DALVIK_CACHE, () -> {
+ return deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput(
+ mDexPath, "arm64", true /* isInDalvikCache */)));
+ });
+ }
- List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt();
- verifyStatusAllOk(results);
+ @Test
+ public void testDexoptInputVdexNextToDex() throws Exception {
+ checkDexoptInputVdex(ArtifactsLocation.NEXT_TO_DEX, () -> {
+ return deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput(
+ mDexPath, "arm64", false /* isInDalvikCache */)));
+ });
+ }
+
+ @Test
+ public void testDexoptInputVdexDm() throws Exception {
+ checkDexoptInputVdex(ArtifactsLocation.DM, () -> isNull());
+ }
+
+ @Test
+ public void testDexoptInputVdexSdmDalvikCache() throws Exception {
+ checkDexoptInputVdex(ArtifactsLocation.SDM_DALVIK_CACHE, () -> isNull());
+ }
+
+ @Test
+ public void testDexoptInputVdexSdmNextToDex() throws Exception {
+ checkDexoptInputVdex(ArtifactsLocation.SDM_NEXT_TO_DEX, () -> isNull());
}
@Test
@@ -945,6 +949,52 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
}
}
+ @Test
+ public void testMaybeCreateSdc() throws Exception {
+ mDexoptParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX)
+ .build();
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ mPrimaryDexopter.dexopt();
+
+ FsPermission dirFsPermission = AidlUtils.buildFsPermission(Process.SYSTEM_UID /* uid */,
+ Process.SYSTEM_UID /* gid */, false /* isOtherReadable */,
+ true /* isOtherExecutable */);
+ FsPermission fileFsPermission = AidlUtils.buildFsPermission(
+ Process.SYSTEM_UID /* uid */, SHARED_GID /* gid */, true /* isOtherReadable */);
+ PermissionSettings permissionSettings = AidlUtils.buildPermissionSettings(
+ dirFsPermission, fileFsPermission, null /* seContext */);
+
+ verify(mArtd).maybeCreateSdc(deepEq(AidlUtils.buildOutputSecureDexMetadataCompanion(
+ mDexPath, "arm64", false /* isInDalvikCache */, permissionSettings)));
+ verify(mArtd).maybeCreateSdc(deepEq(AidlUtils.buildOutputSecureDexMetadataCompanion(
+ mDexPath, "arm", false /* isInDalvikCache */, permissionSettings)));
+ verify(mArtd).maybeCreateSdc(deepEq(AidlUtils.buildOutputSecureDexMetadataCompanion(
+ mSplit0DexPath, "arm64", false /* isInDalvikCache */, permissionSettings)));
+ verify(mArtd).maybeCreateSdc(deepEq(AidlUtils.buildOutputSecureDexMetadataCompanion(
+ mSplit0DexPath, "arm", false /* isInDalvikCache */, permissionSettings)));
+ }
+
+ @Test
+ public void testMaybeCreateSdcCompilerFilterSkip() throws Exception {
+ mDexoptParams = new DexoptParams.Builder("install")
+ .setCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP)
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX)
+ .build();
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd, times(4)).maybeCreateSdc(any());
+ verify(mArtd, never())
+ .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(),
+ any());
+ }
+
private void checkDexoptWithProfile(IArtd artd, String dexPath, String isa, ProfilePath profile,
boolean isOtherReadable) throws Exception {
artd.dexopt(argThat(artifacts
diff --git a/libartservice/service/native/service_test.cc b/libartservice/service/native/service_test.cc
index d1d429edd7..b0864b7769 100644
--- a/libartservice/service/native/service_test.cc
+++ b/libartservice/service/native/service_test.cc
@@ -30,7 +30,7 @@ using ::android::base::testing::WithMessage;
using std::literals::operator""s; // NOLINT
-class ArtServiceTest : public testing::Test {};
+class ArtServiceTest : public ::testing::Test {};
TEST_F(ArtServiceTest, ValidatePathElementOk) {
EXPECT_THAT(ValidatePathElement("com.android.foo", "packageName"), Ok());
@@ -110,7 +110,7 @@ TEST_F(ArtServiceTest, ValidateDexPathNul) {
class ArtServiceGcTest : public CommonRuntimeTest {};
TEST_F(ArtServiceGcTest, GetGarbageCollector) {
- EXPECT_THAT(GetGarbageCollector(), testing::HasSubstr("CollectorType"));
+ EXPECT_THAT(GetGarbageCollector(), ::testing::HasSubstr("CollectorType"));
}
} // namespace
diff --git a/libarttools/art_exec_test.cc b/libarttools/art_exec_test.cc
index d6e0ba4802..c729b5dc0f 100644
--- a/libarttools/art_exec_test.cc
+++ b/libarttools/art_exec_test.cc
@@ -69,10 +69,10 @@ bool GetCap(pid_t pid, cap_flag_t flag, cap_value_t value) {
return flag_value == CAP_SET;
}
-class ArtExecTest : public testing::Test {
+class ArtExecTest : public ::testing::Test {
protected:
void SetUp() override {
- testing::Test::SetUp();
+ ::testing::Test::SetUp();
if (!kIsTargetAndroid) {
GTEST_SKIP() << "art_exec is for device only";
}
diff --git a/libarttools/cmdline_builder_test.cc b/libarttools/cmdline_builder_test.cc
index 1acf2e3f0b..04b746fa8b 100644
--- a/libarttools/cmdline_builder_test.cc
+++ b/libarttools/cmdline_builder_test.cc
@@ -28,7 +28,7 @@ namespace {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
-class CmdlineBuilderTest : public testing::Test {
+class CmdlineBuilderTest : public ::testing::Test {
protected:
CmdlineBuilder args_;
};
diff --git a/libarttools/system_properties_test.cc b/libarttools/system_properties_test.cc
index acffd9727c..6d5db58573 100644
--- a/libarttools/system_properties_test.cc
+++ b/libarttools/system_properties_test.cc
@@ -30,7 +30,7 @@ class MockSystemProperties : public SystemProperties {
MOCK_METHOD(std::string, GetProperty, (const std::string& key), (const, override));
};
-class SystemPropertiesTest : public testing::Test {
+class SystemPropertiesTest : public ::testing::Test {
protected:
MockSystemProperties system_properties_;
};
diff --git a/libdexfile/dex/code_item_accessors_test.cc b/libdexfile/dex/code_item_accessors_test.cc
index 9f20fc2495..bd1ddc958d 100644
--- a/libdexfile/dex/code_item_accessors_test.cc
+++ b/libdexfile/dex/code_item_accessors_test.cc
@@ -26,7 +26,7 @@
namespace art {
-class CodeItemAccessorsTest : public testing::Test {};
+class CodeItemAccessorsTest : public ::testing::Test {};
std::unique_ptr<const DexFile> CreateFakeDex(bool compact_dex, std::vector<uint8_t>* data) {
data->resize(MemMap::GetPageSize());
diff --git a/libdexfile/dex/descriptors_names_test.cc b/libdexfile/dex/descriptors_names_test.cc
index f3ec3ed0f3..1b0654a770 100644
--- a/libdexfile/dex/descriptors_names_test.cc
+++ b/libdexfile/dex/descriptors_names_test.cc
@@ -20,7 +20,7 @@
namespace art {
-class DescriptorsNamesTest : public testing::Test {};
+class DescriptorsNamesTest : public ::testing::Test {};
TEST_F(DescriptorsNamesTest, PrettyDescriptor_ArrayReferences) {
EXPECT_EQ("java.lang.Class[]", PrettyDescriptor("[Ljava/lang/Class;"));
diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc
index 3de1103256..6041ea2077 100644
--- a/libdexfile/dex/dex_file_loader_test.cc
+++ b/libdexfile/dex/dex_file_loader_test.cc
@@ -27,7 +27,7 @@
namespace art {
-class DexFileLoaderTest : public testing::Test {};
+class DexFileLoaderTest : public ::testing::Test {};
static constexpr char kLocationString[] = "/a/dex/file/location";
diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc
index d67d9a938d..8320b94c9e 100644
--- a/libdexfile/dex/dex_file_verifier_test.cc
+++ b/libdexfile/dex/dex_file_verifier_test.cc
@@ -56,7 +56,7 @@ static void FixUpChecksum(uint8_t* dex_file) {
header->checksum_ = adler_checksum;
}
-class DexFileVerifierTest : public testing::Test {
+class DexFileVerifierTest : public ::testing::Test {
protected:
DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
auto container = std::make_shared<MemoryDexFileContainer>(dex_bytes, length);
diff --git a/libdexfile/dex/type_lookup_table_test.cc b/libdexfile/dex/type_lookup_table_test.cc
index 4316be0bd6..27da71cf25 100644
--- a/libdexfile/dex/type_lookup_table_test.cc
+++ b/libdexfile/dex/type_lookup_table_test.cc
@@ -49,14 +49,14 @@ TEST_P(TypeLookupTableTest, Find) {
INSTANTIATE_TEST_CASE_P(FindNonExistingClassWithoutCollisions,
TypeLookupTableTest,
- testing::Values(DescriptorClassDefIdxPair("LAB;", 1U)));
+ ::testing::Values(DescriptorClassDefIdxPair("LAB;", 1U)));
INSTANTIATE_TEST_CASE_P(FindNonExistingClassWithCollisions,
TypeLookupTableTest,
- testing::Values(DescriptorClassDefIdxPair("LDA;", dex::kDexNoIndex)));
+ ::testing::Values(DescriptorClassDefIdxPair("LDA;", dex::kDexNoIndex)));
INSTANTIATE_TEST_CASE_P(FindClassNoCollisions,
TypeLookupTableTest,
- testing::Values(DescriptorClassDefIdxPair("LC;", 2U)));
+ ::testing::Values(DescriptorClassDefIdxPair("LC;", 2U)));
INSTANTIATE_TEST_CASE_P(FindClassWithCollisions,
TypeLookupTableTest,
- testing::Values(DescriptorClassDefIdxPair("LAB;", 1U)));
+ ::testing::Values(DescriptorClassDefIdxPair("LAB;", 1U)));
} // namespace art
diff --git a/libdexfile/dex/utf_test.cc b/libdexfile/dex/utf_test.cc
index 85c74d285c..e8c3313286 100644
--- a/libdexfile/dex/utf_test.cc
+++ b/libdexfile/dex/utf_test.cc
@@ -26,7 +26,7 @@
namespace art {
-class UtfTest : public testing::Test {};
+class UtfTest : public ::testing::Test {};
TEST_F(UtfTest, GetLeadingUtf16Char) {
EXPECT_EQ(0xffff, GetLeadingUtf16Char(0xeeeeffff));
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
index 88ea0e33c8..1b9415db58 100644
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -34,7 +34,7 @@ constexpr const char* kNativeBridgeLibrary8 = "libnativebridge8-test-case.so";
namespace android {
-class NativeBridgeTest : public testing::Test {
+class NativeBridgeTest : public ::testing::Test {
protected:
NativeBridgeTest() : temp_dir_() {
app_data_dir_ = std::string(temp_dir_.path);
diff --git a/libnativeloader/native_loader_api_test.cpp b/libnativeloader/native_loader_api_test.cpp
index aeda1cc2a7..6ef7d36134 100644
--- a/libnativeloader/native_loader_api_test.cpp
+++ b/libnativeloader/native_loader_api_test.cpp
@@ -38,7 +38,7 @@ using ::testing::StrEq;
class NativeLoaderLazyTest : public ::testing::Test {
protected:
void SetUp() override {
- jni_mock = std::make_unique<testing::NiceMock<MockJni>>();
+ jni_mock = std::make_unique<::testing::NiceMock<MockJni>>();
env = std::make_unique<JNIEnv>();
env->functions = CreateJNINativeInterface();
}
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index a07551032c..90810647c3 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -131,7 +131,7 @@ class MockPlatform : public Platform {
ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
ON_CALL(*this, mock_get_exported_namespace(_, _))
- .WillByDefault(testing::Invoke([](bool, const char* name) -> mock_namespace_handle {
+ .WillByDefault(::testing::Invoke([](bool, const char* name) -> mock_namespace_handle {
if (namespaces.find(name) != namespaces.end()) {
return namespaces[name];
}
@@ -274,8 +274,8 @@ class NativeLoaderTest : public ::testing::TestWithParam<bool> {
bool IsBridged() { return GetParam(); }
void SetUp() override {
- mock = std::make_unique<testing::NiceMock<MockPlatform>>(IsBridged());
- jni_mock = std::make_unique<testing::NiceMock<MockJni>>();
+ mock = std::make_unique<::testing::NiceMock<MockPlatform>>(IsBridged());
+ jni_mock = std::make_unique<::testing::NiceMock<MockJni>>();
env = std::make_unique<JNIEnv>();
env->functions = CreateJNINativeInterface();
@@ -379,7 +379,7 @@ TEST_P(NativeLoaderTest, OpenNativeLibraryWithoutClassloaderAndCallerLocation) {
EXPECT_EQ(errmsg, nullptr);
}
-INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, ::testing::Bool());
/////////////////////////////////////////////////////////////////
@@ -439,8 +439,8 @@ class NativeLoaderTest_Create : public NativeLoaderTest {
ON_CALL(*jni_mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
- EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(testing::AnyNumber());
- EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(::testing::AnyNumber());
+ EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(::testing::AnyNumber());
EXPECT_CALL(*mock, mock_create_namespace(
Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
@@ -684,7 +684,7 @@ TEST_P(NativeLoaderTest_Create, TwoApks) {
}
}
-INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, ::testing::Bool());
const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
[](const struct ConfigEntry&) -> Result<bool> { return true; };
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index fa46202327..bc25896c46 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -22,7 +22,7 @@ namespace art {
INSTANTIATE_TEST_SUITE_P(DynamicOrStatic,
OatDumpTest,
- testing::Values(Flavor::kDynamic, Flavor::kStatic));
+ ::testing::Values(Flavor::kDynamic, Flavor::kStatic));
// Disable tests on arm and arm64 as they are taking too long to run. b/27824283.
#define TEST_DISABLED_FOR_ARM_AND_ARM64() \
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 9af33578db..eb713357d9 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -41,7 +41,7 @@ enum class Flavor {
kStatic, // oatdump(d)s, dex2oat(d)s
};
-class OatDumpTest : public CommonRuntimeTest, public testing::WithParamInterface<Flavor> {
+class OatDumpTest : public CommonRuntimeTest, public ::testing::WithParamInterface<Flavor> {
protected:
virtual void SetUp() {
CommonRuntimeTest::SetUp();
diff --git a/odrefresh/odr_metrics_record_test.cc b/odrefresh/odr_metrics_record_test.cc
index 8c24156aa4..1208e188e2 100644
--- a/odrefresh/odr_metrics_record_test.cc
+++ b/odrefresh/odr_metrics_record_test.cc
@@ -132,7 +132,7 @@ TEST_F(OdrMetricsRecordTest, HappyPath) {
TEST_F(OdrMetricsRecordTest, EmptyInput) {
OdrMetricsRecord record{};
- ASSERT_THAT(record.ReadFromFile(file_path_), testing::Not(Ok()));
+ ASSERT_THAT(record.ReadFromFile(file_path_), ::testing::Not(Ok()));
}
TEST_F(OdrMetricsRecordTest, UnexpectedInput) {
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 892f1b0a19..c0a65f27a7 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -397,6 +397,7 @@ cc_defaults {
"oat/oat_file_assistant_context.cc",
"oat/oat_file_manager.cc",
"oat/oat_quick_method_header.cc",
+ "oat/sdc_file.cc",
"oat/stack_map.cc",
"object_lock.cc",
"offsets.cc",
@@ -1129,6 +1130,7 @@ art_cc_defaults {
"native_stack_dump_test.cc",
"oat/oat_file_assistant_test.cc",
"oat/oat_file_test.cc",
+ "oat/sdc_file_test.cc",
"parsed_options_test.cc",
"prebuilt_tools_test.cc",
"proxy_test.cc",
diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc
index 37aad21a40..21d3f13ab7 100644
--- a/runtime/arch/memcmp16_test.cc
+++ b/runtime/arch/memcmp16_test.cc
@@ -30,8 +30,7 @@ class RandGen {
uint32_t val_;
};
-class MemCmp16Test : public testing::Test {
-};
+class MemCmp16Test : public ::testing::Test {};
// A simple implementation to compare against.
// Note: this version is equivalent to the generic one used when no optimized version is available.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 009d705caf..b14eaae51b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2240,8 +2240,16 @@ bool ClassLinker::AddImageSpace(gc::space::ImageSpace* space,
return false;
}
+ const char* oat_apex_versions =
+ oat_header->GetStoreValueByKeyUnsafe(OatHeader::kApexVersionsKey);
+ if (oat_apex_versions == nullptr) {
+ *error_msg = StringPrintf("Missing apex versions in special root in runtime image '%s'",
+ space->GetImageLocation().c_str());
+ return false;
+ }
+
// Validate the apex versions.
- if (!gc::space::ImageSpace::ValidateApexVersions(*oat_header,
+ if (!gc::space::ImageSpace::ValidateApexVersions(oat_apex_versions,
runtime->GetApexVersions(),
space->GetImageLocation(),
error_msg)) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 7c20de30fd..1866583058 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -244,10 +244,10 @@ class CommonRuntimeTestBase : public TestType, public CommonRuntimeTestImpl {
}
};
-using CommonRuntimeTest = CommonRuntimeTestBase<testing::Test>;
+using CommonRuntimeTest = CommonRuntimeTestBase<::testing::Test>;
template <typename Param>
-using CommonRuntimeTestWithParam = CommonRuntimeTestBase<testing::TestWithParam<Param>>;
+using CommonRuntimeTestWithParam = CommonRuntimeTestBase<::testing::TestWithParam<Param>>;
// Sets a CheckJni abort hook to catch failures. Note that this will cause CheckJNI to carry on
// rather than aborting, so be careful!
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 7e8378aa67..53598bcfa0 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -20,13 +20,16 @@
#include <sys/wait.h>
#include <fstream>
+#include <memory>
#include <optional>
#include <string>
#include <vector>
+#include "android-base/file.h"
#include "android-base/result.h"
#include "android-base/strings.h"
#include "base/file_utils.h"
+#include "base/globals.h"
#include "base/macros.h"
#include "base/os.h"
#include "base/stl_util.h"
@@ -40,6 +43,7 @@
#include "gc/space/image_space.h"
#include "gtest/gtest.h"
#include "oat/oat_file_assistant.h"
+#include "oat/sdc_file.h"
#include "runtime.h"
#include "ziparchive/zip_writer.h"
@@ -256,7 +260,9 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe
return WEXITSTATUS(res.status_code);
}
- void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) {
+ void CreateDexMetadata(const std::string& vdex,
+ const std::string& out_dm,
+ bool page_aligned = false) {
// Read the vdex bytes.
std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str()));
std::vector<uint8_t> data(vdex_file->GetLength());
@@ -265,13 +271,55 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe
// Zip the content.
FILE* file = fopen(out_dm.c_str(), "wbe");
ZipWriter writer(file);
- writer.StartEntry("primary.vdex", ZipWriter::kAlign32);
+ writer.StartAlignedEntry(
+ "primary.vdex", /*flags=*/0, /*alignment=*/page_aligned ? kMaxPageSize : 4);
writer.WriteBytes(data.data(), data.size());
writer.FinishEntry();
writer.Finish();
fflush(file);
fclose(file);
}
+
+ void CreateSecureDexMetadata(const std::string& odex,
+ const std::string& art,
+ const std::string& out_sdm) {
+ // Zip the content.
+ std::unique_ptr<File> sdm_file(OS::CreateEmptyFileWriteOnly(out_sdm.c_str()));
+ ASSERT_NE(sdm_file, nullptr);
+ ZipWriter writer(fdopen(sdm_file->Fd(), "wb"));
+
+ std::string odex_data;
+ ASSERT_TRUE(android::base::ReadFileToString(odex, &odex_data));
+ writer.StartAlignedEntry("primary.odex", /*flags=*/0, /*alignment=*/kMaxPageSize);
+ writer.WriteBytes(odex_data.data(), odex_data.size());
+ writer.FinishEntry();
+
+ if (!art.empty()) {
+ std::string art_data;
+ ASSERT_TRUE(android::base::ReadFileToString(art, &art_data));
+ writer.StartAlignedEntry("primary.art", /*flags=*/0, /*alignment=*/kMaxPageSize);
+ writer.WriteBytes(art_data.data(), art_data.size());
+ writer.FinishEntry();
+ }
+
+ writer.Finish();
+ ASSERT_EQ(sdm_file->FlushClose(), 0);
+ }
+
+ void CreateSecureDexMetadataCompanion(const std::string& sdm,
+ const std::string& apex_versions,
+ const std::string& out_sdc) {
+ struct stat sdm_st;
+ ASSERT_EQ(stat(sdm.c_str(), &sdm_st), 0);
+
+ std::unique_ptr<File> sdc_file(OS::CreateEmptyFileWriteOnly(out_sdc.c_str()));
+ ASSERT_NE(sdc_file, nullptr);
+ SdcWriter sdc_writer(std::move(*sdc_file));
+ sdc_writer.SetSdmTimestampNs(TimeSpecToNs(sdm_st.st_mtim));
+ sdc_writer.SetApexVersions(apex_versions);
+ std::string error_msg;
+ ASSERT_TRUE(sdc_writer.Save(&error_msg)) << error_msg;
+ }
};
} // namespace art
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 40e10142d7..6239f7de87 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -202,6 +202,38 @@ void DexoptTest::GenerateOatForTest(const char* dex_location, CompilerFilter::Fi
GenerateOatForTest(dex_location, filter, /*with_alternate_image=*/false);
}
+void DexoptTest::GenerateSdmDmForTest(const std::string& dex_location,
+ const std::string& sdm_location,
+ const std::string& dm_location,
+ CompilerFilter::Filter filter,
+ bool include_app_image,
+ const char* compilation_reason,
+ const std::vector<std::string>& extra_args) {
+ std::string tmp_dir = GetScratchDir() + "/sdm_tmp";
+ ASSERT_EQ(0, mkdir(tmp_dir.c_str(), 0700));
+
+ std::string odex_location = tmp_dir + "/TestDex.odex";
+ std::string vdex_location = tmp_dir + "/TestDex.vdex";
+ std::string art_location;
+
+ std::vector<std::string> extra_args_with_app_image = extra_args;
+ if (include_app_image) {
+ art_location = tmp_dir + "/TestDex.art";
+ extra_args_with_app_image.push_back("--app-image-file=" + art_location);
+ }
+
+ // Generate temporary ODEX, VDEX, and ART files in order to create the SDM and DM files from.
+ ASSERT_NO_FATAL_FAILURE(GenerateOdexForTest(
+ dex_location, odex_location, filter, compilation_reason, extra_args_with_app_image));
+
+ // Create the SDM and DM files.
+ ASSERT_NO_FATAL_FAILURE(CreateSecureDexMetadata(odex_location, art_location, sdm_location));
+ ASSERT_NO_FATAL_FAILURE(CreateDexMetadata(vdex_location, dm_location, /*page_aligned=*/true));
+
+ // Cleanup the temporary files.
+ ASSERT_NO_FATAL_FAILURE(ClearDirectory(tmp_dir.c_str()));
+}
+
void DexoptTest::ReserveImageSpace() {
MemMap::Init();
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
index cf32785c0b..fa18fdd94b 100644
--- a/runtime/dexopt_test.h
+++ b/runtime/dexopt_test.h
@@ -64,6 +64,16 @@ class DexoptTest : public Dex2oatEnvironmentTest {
// Generate a standard oat file in the oat location.
void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter);
+ // Generate sdm and dm files for the purposes of test.
+ // If `include_app_image` is true, generates an app image and includes it in the sdm file.
+ void GenerateSdmDmForTest(const std::string& dex_location,
+ const std::string& sdm_location,
+ const std::string& dm_location,
+ CompilerFilter::Filter filter,
+ bool include_app_image,
+ const char* compilation_reason = nullptr,
+ const std::vector<std::string>& extra_args = {});
+
bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
private:
diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc
index eb21652c19..636383d474 100644
--- a/runtime/exec_utils_test.cc
+++ b/runtime/exec_utils_test.cc
@@ -102,7 +102,7 @@ class NeverFallbackExecUtils : public TestingExecUtils {
}
};
-class ExecUtilsTest : public CommonRuntimeTest, public testing::WithParamInterface<bool> {
+class ExecUtilsTest : public CommonRuntimeTest, public ::testing::WithParamInterface<bool> {
protected:
void SetUp() override {
CommonRuntimeTest::SetUp();
@@ -390,6 +390,6 @@ TEST_P(ExecUtilsTest, ExecNewProcessGroupFalse) {
&error_msg);
}
-INSTANTIATE_TEST_SUITE_P(AlwaysOrNeverFallback, ExecUtilsTest, testing::Values(true, false));
+INSTANTIATE_TEST_SUITE_P(AlwaysOrNeverFallback, ExecUtilsTest, ::testing::Values(true, false));
} // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 97512bc79c..f557f3c8a9 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -26,6 +26,7 @@
#include <optional>
#include <random>
#include <string>
+#include <string_view>
#include <vector>
#include "android-base/logging.h"
@@ -3415,31 +3416,39 @@ void ImageSpace::Dump(std::ostream& os) const {
<< ",name=\"" << GetName() << "\"]";
}
-bool ImageSpace::ValidateApexVersions(const OatHeader& oat_header,
- const std::string& apex_versions,
- const std::string& file_location,
+bool ImageSpace::ValidateApexVersions(const OatFile& oat_file,
+ std::string_view runtime_apex_versions,
std::string* error_msg) {
// For a boot image, the key value store only exists in the first OAT file. Skip other OAT files.
- if (oat_header.GetKeyValueStoreSize() == 0) {
+ if (oat_file.GetOatHeader().GetKeyValueStoreSize() == 0) {
return true;
}
- const char* oat_apex_versions = oat_header.GetStoreValueByKey(OatHeader::kApexVersionsKey);
- if (oat_apex_versions == nullptr) {
+ std::optional<std::string_view> oat_apex_versions = oat_file.GetApexVersions();
+ if (!oat_apex_versions.has_value()) {
*error_msg = StringPrintf("ValidateApexVersions failed to get APEX versions from oat file '%s'",
- file_location.c_str());
+ oat_file.GetLocation().c_str());
return false;
}
+
+ return ValidateApexVersions(
+ *oat_apex_versions, runtime_apex_versions, oat_file.GetLocation(), error_msg);
+}
+
+bool ImageSpace::ValidateApexVersions(std::string_view oat_apex_versions,
+ std::string_view runtime_apex_versions,
+ const std::string& file_location,
+ std::string* error_msg) {
// For a boot image, it can be generated from a subset of the bootclasspath.
// For an app image, some dex files get compiled with a subset of the bootclasspath.
// For such cases, the OAT APEX versions will be a prefix of the runtime APEX versions.
- if (!apex_versions.starts_with(oat_apex_versions)) {
- *error_msg = StringPrintf(
- "ValidateApexVersions found APEX versions mismatch between oat file '%s' and the runtime "
- "(Oat file: '%s', Runtime: '%s')",
- file_location.c_str(),
+ if (!runtime_apex_versions.starts_with(oat_apex_versions)) {
+ *error_msg = ART_FORMAT(
+ "ValidateApexVersions found APEX versions mismatch between oat file '{}' and the runtime "
+ "(Oat file: '{}', Runtime: '{}')",
+ file_location,
oat_apex_versions,
- apex_versions.c_str());
+ runtime_apex_versions);
return false;
}
return true;
@@ -3455,10 +3464,7 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file,
ArrayRef<const std::string> dex_filenames,
ArrayRef<File> dex_files,
const std::string& apex_versions) {
- if (!ValidateApexVersions(oat_file.GetOatHeader(),
- apex_versions,
- oat_file.GetLocation(),
- error_msg)) {
+ if (!ValidateApexVersions(oat_file, apex_versions, error_msg)) {
return false;
}
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 266b1d5925..4d0ce8181e 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -260,9 +260,13 @@ class ImageSpace : public MemMapSpace {
const std::string& image_location,
bool boot_image_extension = false);
- // Returns true if the APEX versions in the OAT header match the given APEX versions.
- static bool ValidateApexVersions(const OatHeader& oat_header,
- const std::string& apex_versions,
+ // Returns true if the APEX versions of the OAT file match the given APEX versions.
+ static bool ValidateApexVersions(const OatFile& oat_file,
+ std::string_view runtime_apex_versions,
+ std::string* error_msg);
+
+ static bool ValidateApexVersions(std::string_view oat_apex_versions,
+ std::string_view runtime_apex_versions,
const std::string& file_location,
std::string* error_msg);
diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc
index 83568351b3..759c5de4ef 100644
--- a/runtime/gc/space/space_create_test.cc
+++ b/runtime/gc/space/space_create_test.cc
@@ -351,10 +351,10 @@ TEST_P(SpaceCreateTest, AllocAndFreeListTestBody) {
INSTANTIATE_TEST_CASE_P(CreateRosAllocSpace,
SpaceCreateTest,
- testing::Values(kMallocSpaceRosAlloc));
+ ::testing::Values(kMallocSpaceRosAlloc));
INSTANTIATE_TEST_CASE_P(CreateDlMallocSpace,
SpaceCreateTest,
- testing::Values(kMallocSpaceDlMalloc));
+ ::testing::Values(kMallocSpaceDlMalloc));
} // namespace space
} // namespace gc
diff --git a/runtime/jit/jit_memory_region_test.cc b/runtime/jit/jit_memory_region_test.cc
index 449255a7f7..b17e01fff9 100644
--- a/runtime/jit/jit_memory_region_test.cc
+++ b/runtime/jit/jit_memory_region_test.cc
@@ -52,7 +52,7 @@ static void registerSignalHandler() {
sigaction(SIGSEGV, &sa, nullptr);
}
-class TestZygoteMemory : public testing::Test {
+class TestZygoteMemory : public ::testing::Test {
public:
void BasicTest() {
// Zygote JIT memory only works on kernels that don't segfault on flush.
diff --git a/runtime/metrics/reporter_test.cc b/runtime/metrics/reporter_test.cc
index 039e43ca48..b3a0c025ca 100644
--- a/runtime/metrics/reporter_test.cc
+++ b/runtime/metrics/reporter_test.cc
@@ -399,7 +399,7 @@ TEST_F(MetricsReporterTest, CompilerFilter) {
}
// Test class for period spec parsing
-class ReportingPeriodSpecTest : public testing::Test {
+class ReportingPeriodSpecTest : public ::testing::Test {
public:
void VerifyFalse(const std::string& spec_str) {
Verify(spec_str, false, false, false, {});
diff --git a/runtime/oat/oat.cc b/runtime/oat/oat.cc
index 840825cea8..9882b0fa51 100644
--- a/runtime/oat/oat.cc
+++ b/runtime/oat/oat.cc
@@ -373,7 +373,7 @@ const uint8_t* OatHeader::GetKeyValueStore() const {
return key_value_store_;
}
-const char* OatHeader::GetStoreValueByKey(const char* key) const {
+const char* OatHeader::GetStoreValueByKeyUnsafe(const char* key) const {
std::string_view key_view(key);
uint32_t offset = 0;
diff --git a/runtime/oat/oat.h b/runtime/oat/oat.h
index 0061c90da9..2069b569e3 100644
--- a/runtime/oat/oat.h
+++ b/runtime/oat/oat.h
@@ -164,7 +164,14 @@ class EXPORT PACKED(4) OatHeader {
uint32_t GetKeyValueStoreSize() const;
const uint8_t* GetKeyValueStore() const;
- const char* GetStoreValueByKey(const char* key) const;
+ const char* GetStoreValueByKeyUnsafe(const char* key) const;
+
+ const char* GetStoreValueByKey(const char* key) const {
+ // Do not get apex versions from the oat header directly. Use `OatFile::GetApexVersions`
+ // instead.
+ DCHECK_NE(std::string_view(key), kApexVersionsKey);
+ return GetStoreValueByKeyUnsafe(key);
+ }
// Returns the next key-value pair, at the given offset. On success, updates `offset`.
// The expected use case is to start the iteration with an offset initialized to zero and
diff --git a/runtime/oat/oat_file.cc b/runtime/oat/oat_file.cc
index 1ecad4abf9..ec845cd0c1 100644
--- a/runtime/oat/oat_file.cc
+++ b/runtime/oat/oat_file.cc
@@ -20,25 +20,34 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <cstddef>
#include <cstdint>
+#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <memory>
#include <optional>
#include <sstream>
+#include <string>
#include <type_traits>
+#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "arch/instruction_set_features.h"
#include "art_method.h"
+#include "base/array_ref.h"
#include "base/bit_vector.h"
#include "base/file_utils.h"
+#include "base/globals.h"
#include "base/logging.h" // For VLOG_IS_ON.
+#include "base/macros.h"
#include "base/mem_map.h"
#include "base/os.h"
#include "base/pointer_size.h"
#include "base/stl_util.h"
#include "base/systrace.h"
+#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "base/zip_archive.h"
@@ -59,6 +68,7 @@
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "oat.h"
+#include "oat/sdc_file.h"
#include "oat_file-inl.h"
#include "oat_file_manager.h"
#include "runtime-inl.h"
@@ -174,6 +184,14 @@ class OatFileBase : public OatFile {
/*inout*/ MemMap* reservation, // Where to load if not null.
/*out*/ std::string* error_msg);
+ template <typename kOatFileBaseSubType>
+ static OatFileBase* OpenOatFileFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg);
+
protected:
OatFileBase(const std::string& filename, bool executable) : OatFile(filename, executable) {}
@@ -316,6 +334,61 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd,
return ret.release();
}
+template <typename kOatFileBaseSubType>
+OatFileBase* OatFileBase::OpenOatFileFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg) {
+ std::string elf_filename = sdm_filename + kZipSeparator + "primary.odex";
+ std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(elf_filename, executable));
+
+ struct stat sdm_st;
+ if (stat(sdm_filename.c_str(), &sdm_st) != 0) {
+ *error_msg = ART_FORMAT("Failed to stat sdm file '{}': {}", sdm_filename, strerror(errno));
+ return nullptr;
+ }
+
+ std::unique_ptr<SdcReader> sdc_reader = SdcReader::Load(sdc_filename, error_msg);
+ if (sdc_reader == nullptr) {
+ return nullptr;
+ }
+ if (sdc_reader->GetSdmTimestampNs() != TimeSpecToNs(sdm_st.st_mtim)) {
+ // The sdm file had been replaced after the sdc file was created.
+ *error_msg = ART_FORMAT("Obsolete sdc file '{}'", sdc_filename);
+ return nullptr;
+ }
+ // The apex-versions value in the sdc file, written by ART Service, is the value of
+ // `Runtime::GetApexVersions` at the time where the sdm file was first seen on device. We use it
+ // to override the APEX versions in the oat header. This is for detecting samegrade placebos.
+ ret->override_apex_versions_ = sdc_reader->GetApexVersions();
+
+ if (!ret->Load(elf_filename, executable, /*low_4gb=*/false, /*reservation=*/nullptr, error_msg)) {
+ return nullptr;
+ }
+
+ if (!ret->ComputeFields(elf_filename, error_msg)) {
+ return nullptr;
+ }
+
+ ret->PreSetup(elf_filename);
+
+ ret->vdex_ = VdexFile::OpenFromDm(dm_filename, ret->vdex_begin_, ret->vdex_end_, error_msg);
+ if (ret->vdex_ == nullptr) {
+ return nullptr;
+ }
+
+ if (!ret->Setup(/*zip_fd=*/-1,
+ ArrayRef<const std::string>(&dex_filename, /*size=*/1u),
+ /*dex_files=*/{},
+ error_msg)) {
+ return nullptr;
+ }
+
+ return ret.release();
+}
+
bool OatFileBase::LoadVdex(const std::string& vdex_filename, bool low_4gb, std::string* error_msg) {
vdex_ = VdexFile::OpenAtAddress(vdex_begin_,
vdex_end_ - vdex_begin_,
@@ -1330,11 +1403,11 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
return false;
#else
{
- UniqueCPtr<char> absolute_path(realpath(elf_filename.c_str(), nullptr));
- if (absolute_path == nullptr) {
- *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
- return false;
- }
+ // `elf_filename` is in the format of `/path/to/oat` or `/path/to/zip!/primary.odex`. We can
+ // reuse `GetDexCanonicalLocation` to resolve the real path of the part before "!" even though
+ // `elf_filename` does not refer to a dex file.
+ static_assert(std::string_view(kZipSeparator).starts_with(DexFileLoader::kMultiDexSeparator));
+ std::string absolute_path = DexFileLoader::GetDexCanonicalLocation(elf_filename.c_str());
#ifdef ART_TARGET_ANDROID
android_dlextinfo extinfo = {};
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD; // Force-load, don't reuse handle
@@ -1350,9 +1423,9 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
}
if (strncmp(kAndroidArtApexDefaultPath,
- absolute_path.get(),
+ absolute_path.c_str(),
sizeof(kAndroidArtApexDefaultPath) - 1) != 0 ||
- absolute_path.get()[sizeof(kAndroidArtApexDefaultPath) - 1] != '/') {
+ absolute_path.c_str()[sizeof(kAndroidArtApexDefaultPath) - 1] != '/') {
// Use the system namespace for OAT files outside the ART APEX. Search
// paths and links don't matter here, but permitted paths do, and the
// system namespace is configured to allow loading from all appropriate
@@ -1361,7 +1434,7 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
extinfo.library_namespace = GetSystemLinkerNamespace();
}
- dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
+ dlopen_handle_ = android_dlopen_ext(absolute_path.c_str(), RTLD_NOW, &extinfo);
if (reservation != nullptr && dlopen_handle_ != nullptr) {
// Find used pages from the reservation.
struct dl_iterate_context {
@@ -1435,7 +1508,7 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
return false;
}
MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
- dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
+ dlopen_handle_ = dlopen(absolute_path.c_str(), RTLD_NOW);
if (dlopen_handle_ != nullptr) {
if (!host_dlopen_handles_.insert(dlopen_handle_).second) {
dlclose(dlopen_handle_);
@@ -2034,6 +2107,38 @@ OatFile* OatFile::OpenFromVdex(int zip_fd,
return OatFileBackedByVdex::Open(zip_fd, std::move(vdex_file), location, context, error_msg);
}
+OatFile* OatFile::OpenFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg) {
+ ScopedTrace trace("Open sdm file " + sdm_filename);
+ CHECK(!sdm_filename.empty());
+ CHECK(!sdc_filename.empty());
+ CHECK(!dm_filename.empty());
+ CHECK(!dex_filename.empty());
+
+ // Check if the dm file exists, to fail fast. The dm file contains the vdex that is essential for
+ // using the odex in the sdm file.
+ if (!OS::FileExists(dm_filename.c_str())) {
+ *error_msg =
+ ART_FORMAT("Not loading sdm file because dm file '{}' does not exist", dm_filename);
+ return nullptr;
+ }
+
+ // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is
+ // disabled.
+ OatFile* with_dlopen = OatFileBase::OpenOatFileFromSdm<DlOpenOatFile>(
+ sdm_filename, sdc_filename, dm_filename, dex_filename, executable, error_msg);
+ if (with_dlopen != nullptr) {
+ return with_dlopen;
+ }
+
+ return OatFileBase::OpenOatFileFromSdm<ElfOatFile>(
+ sdm_filename, sdc_filename, dm_filename, dex_filename, executable, error_msg);
+}
+
OatFile::OatFile(const std::string& location, bool is_executable)
: location_(location),
vdex_(nullptr),
@@ -2586,4 +2691,13 @@ bool OatFile::IsBackedByVdexOnly() const {
return oat_dex_files_storage_.size() >= 1 && oat_dex_files_storage_[0]->IsBackedByVdexOnly();
}
+std::optional<std::string_view> OatFile::GetApexVersions() const {
+ if (override_apex_versions_.has_value()) {
+ return override_apex_versions_;
+ }
+ const char* oat_apex_versions =
+ GetOatHeader().GetStoreValueByKeyUnsafe(OatHeader::kApexVersionsKey);
+ return oat_apex_versions != nullptr ? std::make_optional(oat_apex_versions) : std::nullopt;
+}
+
} // namespace art
diff --git a/runtime/oat/oat_file.h b/runtime/oat/oat_file.h
index 9e66e1d4a7..33645fa4ba 100644
--- a/runtime/oat/oat_file.h
+++ b/runtime/oat/oat_file.h
@@ -19,6 +19,7 @@
#include <list>
#include <memory>
+#include <optional>
#include <string>
#include <string_view>
#include <vector>
@@ -186,6 +187,13 @@ class OatFile {
ClassLoaderContext* context,
std::string* error_msg);
+ static OatFile* OpenFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg);
+
// Set the start of the app image.
// Needed for initializing app image relocations in the .data.img.rel.ro section.
void SetAppImageBegin(uint8_t* app_image_begin) const {
@@ -426,6 +434,8 @@ class OatFile {
// Returns the mapping info of `dex_file` if found in the BcpBssInfo, or nullptr otherwise.
const BssMappingInfo* FindBcpMappingInfo(const DexFile* dex_file) const;
+ std::optional<std::string_view> GetApexVersions() const;
+
protected:
OatFile(const std::string& filename, bool executable);
@@ -518,6 +528,9 @@ class OatFile {
// by the `dex_filenames` parameter, in case the OatFile does not embed the dex code.
std::vector<std::unique_ptr<const DexFile>> external_dex_files_;
+ // If set, overrides the APEX versions in the header.
+ std::optional<std::string> override_apex_versions_ = std::nullopt;
+
friend class gc::collector::FakeOatFile; // For modifying begin_ and end_.
friend class OatClass;
friend class art::OatDexFile;
diff --git a/runtime/oat/oat_file_assistant.cc b/runtime/oat/oat_file_assistant.cc
index f5d6832cff..53f412908d 100644
--- a/runtime/oat/oat_file_assistant.cc
+++ b/runtime/oat/oat_file_assistant.cc
@@ -191,6 +191,12 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
oat_file_name,
/*is_oat_location=*/true,
/*use_fd=*/false));
+ info_list_.push_back(
+ std::make_unique<OatFileInfoBackedBySdm>(this,
+ GetSdmFilename(dex_location_, isa),
+ /*is_oat_location=*/true,
+ GetDmFilename(dex_location_),
+ GetSdcFilename(oat_file_name)));
}
if (!odex_file_name.empty()) {
@@ -202,6 +208,12 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
zip_fd,
vdex_fd,
oat_fd));
+ info_list_.push_back(
+ std::make_unique<OatFileInfoBackedBySdm>(this,
+ GetSdmFilename(dex_location_, isa),
+ /*is_oat_location=*/false,
+ GetDmFilename(dex_location_),
+ GetSdcFilename(odex_file_name)));
}
// When there is no odex/oat available (e.g., they are both out of date), we look for a useable
@@ -324,7 +336,8 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_fil
OatFileInfo& info = GetBestInfo();
DexOptNeeded dexopt_needed = info.GetDexOptNeeded(
target_compiler_filter, GetDexOptTrigger(target_compiler_filter, profile_changed, downgrade));
- if (dexopt_needed != kNoDexOptNeeded && info.GetType() == OatFileType::kDm) {
+ if (dexopt_needed != kNoDexOptNeeded &&
+ (info.GetType() == OatFileType::kDm || info.GetType() == OatFileType::kSdm)) {
// The usable vdex file is in the DM file. This information cannot be encoded in the integer.
// Return kDex2OatFromScratch so that neither the vdex in the "oat" location nor the vdex in the
// "odex" location will be picked by installd.
@@ -491,10 +504,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile&
return kOatBootImageOutOfDate;
}
if (!gc::space::ImageSpace::ValidateApexVersions(
- file.GetOatHeader(),
- GetOatFileAssistantContext()->GetApexVersions(),
- file.GetLocation(),
- error_msg)) {
+ file, GetOatFileAssistantContext()->GetApexVersions(), error_msg)) {
return kOatBootImageOutOfDate;
}
}
@@ -959,6 +969,10 @@ bool OatFileAssistant::OatFileInfoBackedByOat::FileExists() const {
return use_fd_ || OatFileInfo::FileExists();
}
+bool OatFileAssistant::OatFileInfoBackedBySdm::FileExists() const {
+ return OatFileInfo::FileExists() && OS::FileExists(sdc_filename_.c_str());
+}
+
bool OatFileAssistant::OatFileInfoBackedByVdex::FileExists() const {
return use_fd_ || OatFileInfo::FileExists();
}
@@ -1018,6 +1032,21 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedByOat::LoadFile(
}
}
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedBySdm::LoadFile(
+ std::string* error_msg) const {
+ bool executable = oat_file_assistant_->load_executable_;
+ if (executable && oat_file_assistant_->only_load_trusted_executable_) {
+ executable = LocationIsTrusted(filename_, /*trust_art_apex_data_files=*/true);
+ }
+
+ return std::unique_ptr<OatFile>(OatFile::OpenFromSdm(filename_,
+ sdc_filename_,
+ dm_filename_,
+ oat_file_assistant_->dex_location_,
+ executable,
+ error_msg));
+}
+
std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedByVdex::LoadFile(
std::string* error_msg) const {
// Check to see if there is a vdex file we can make use of.
@@ -1292,7 +1321,13 @@ bool OatFileAssistant::ZipFileOnlyContainsUncompressedDex() {
OatFileAssistant::Location OatFileAssistant::GetLocation(OatFileInfo& info) {
if (info.IsUseable()) {
- if (info.GetType() == OatFileType::kDm) {
+ if (info.GetType() == OatFileType::kSdm) {
+ if (info.IsOatLocation()) {
+ return kLocationSdmOat;
+ } else {
+ return kLocationSdmOdex;
+ }
+ } else if (info.GetType() == OatFileType::kDm) {
return kLocationDm;
} else if (info.IsOatLocation()) {
return kLocationOat;
diff --git a/runtime/oat/oat_file_assistant.h b/runtime/oat/oat_file_assistant.h
index bcb0c8e9db..80eabba34b 100644
--- a/runtime/oat/oat_file_assistant.h
+++ b/runtime/oat/oat_file_assistant.h
@@ -123,8 +123,14 @@ class OatFileAssistant {
kLocationOat = 1,
// In the "oat" folder next to the dex file.
kLocationOdex = 2,
- // In the DM file. This means the only usable file is the vdex file.
+ // In the dm file. This means the only usable file is the vdex file.
kLocationDm = 3,
+ // The oat and art files are in the sdm file next to the dex file. The vdex file is in the dm
+ // file next to the dex file. The sdc file is in the global "dalvik-cache" folder.
+ kLocationSdmOat = 4,
+ // The oat and art files are in the sdm file next to the dex file. The vdex file is in the dm
+ // file next to the dex file. The sdc file is next to the dex file.
+ kLocationSdmOdex = 5,
};
// Represents the status of the current oat file and/or vdex file.
@@ -381,6 +387,7 @@ class OatFileAssistant {
enum class OatFileType {
kNone,
kOat,
+ kSdm,
kVdex,
kDm,
};
@@ -529,6 +536,33 @@ class OatFileAssistant {
const int oat_fd_;
};
+ class OatFileInfoBackedBySdm : public OatFileInfo {
+ public:
+ OatFileInfoBackedBySdm(OatFileAssistant* oat_file_assistant,
+ const std::string& sdm_filename,
+ bool is_oat_location,
+ const std::string& dm_filename,
+ const std::string& sdc_filename)
+ : OatFileInfo(oat_file_assistant, sdm_filename, is_oat_location),
+ dm_filename_(dm_filename),
+ sdc_filename_(sdc_filename) {}
+
+ OatFileType GetType() override { return OatFileType::kSdm; }
+
+ const char* GetLocationDebugString() override {
+ return IsOatLocation() ? "sdm with sdc in dalvik-cache" : "sdm with sdc next to the dex file";
+ }
+
+ bool FileExists() const override;
+
+ protected:
+ std::unique_ptr<OatFile> LoadFile(std::string* error_msg) const override;
+
+ private:
+ const std::string dm_filename_;
+ const std::string sdc_filename_;
+ };
+
class OatFileInfoBackedByVdex : public OatFileInfo {
public:
OatFileInfoBackedByVdex(OatFileAssistant* oat_file_assistant,
diff --git a/runtime/oat/oat_file_assistant_context.h b/runtime/oat/oat_file_assistant_context.h
index 82b79edef0..28d5b49dc2 100644
--- a/runtime/oat/oat_file_assistant_context.h
+++ b/runtime/oat/oat_file_assistant_context.h
@@ -76,7 +76,7 @@ class OatFileAssistantContext {
const std::vector<std::string>* GetBcpChecksums(size_t bcp_index, std::string* error_msg);
// Returns a string that represents the apex versions of boot classpath jars. See
// `Runtime::apex_versions_` for the encoding format.
- const std::string& GetApexVersions();
+ EXPORT const std::string& GetApexVersions();
private:
std::unique_ptr<RuntimeOptions> runtime_options_;
diff --git a/runtime/oat/oat_file_assistant_test.cc b/runtime/oat/oat_file_assistant_test.cc
index b1f196a23a..c3e2f63e24 100644
--- a/runtime/oat/oat_file_assistant_test.cc
+++ b/runtime/oat/oat_file_assistant_test.cc
@@ -33,6 +33,7 @@
#include "android-base/strings.h"
#include "arch/instruction_set.h"
#include "art_field-inl.h"
+#include "base/file_utils.h"
#include "base/os.h"
#include "base/utils.h"
#include "class_linker.h"
@@ -52,7 +53,7 @@ namespace art HIDDEN {
class OatFileAssistantBaseTest : public DexoptTest {};
class OatFileAssistantTest : public OatFileAssistantBaseTest,
- public testing::WithParamInterface<bool> {
+ public ::testing::WithParamInterface<bool> {
public:
void SetUp() override {
DexoptTest::SetUp();
@@ -2183,6 +2184,310 @@ TEST_P(OatFileAssistantTest, ShouldRecompileForImageFromSpeedProfile) {
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
}
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDC file is in odex
+// location.
+// Expect: The best artifact location should be kLocationSdmOdex. Dexopt should be performed only if
+// the compiler filter is better than "speed-profile".
+//
+// The legacy version should return kDex2OatFromScratch if the target compiler filter is better than
+// "verify".
+TEST_P(OatFileAssistantTest, SdmUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDC file is in oat
+// location.
+// Expect: The best artifact location should be kLocationSdmOat.
+TEST_P(OatFileAssistantTest, SdmUpToDateSdcInOatLocation) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg))
+ << error_msg;
+ std::string sdc_location = GetSdcFilename(oat_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOat);
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDM file contains no
+// ART file.
+// Expect: The best artifact location should be kLocationSdmOdex.
+TEST_P(OatFileAssistantTest, SdmUpToDateNoArt) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/false,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file. Meanwhile, we have an ODEX
+// file that is also up to date.
+// Expect: The ODEX file is preferred over the SDM file. The best artifact location should be
+// kLocationOdex.
+TEST_P(OatFileAssistantTest, SdmAndOdexUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ ASSERT_NO_FATAL_FAILURE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeedProfile,
+ /*compilation_reason=*/"bg-dexopt"));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "bg-dexopt",
+ "up-to-date",
+ OatFileAssistant::kLocationOdex);
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file. Meanwhile, we have a VDEX
+// file that is also up to date.
+// Expect: The SDM file is preferred over the VDEX file. The best artifact location should be
+// kLocationSdmOdex.
+TEST_P(OatFileAssistantTest, SdmAndVdexUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ ASSERT_NO_FATAL_FAILURE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeedProfile,
+ /*compilation_reason=*/"bg-dexopt"));
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+}
+
+// Case: We have SDM, DM, and SDC files for a compressed DEX file.
+// Expect: The SDM file is still picked. Dexopt should be performed if the compiler filter is
+// "speed-profile" or above.
+TEST_P(OatFileAssistantTest, SdmUpToDateCompressedDex) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud",
+ /*extra_args=*/{"--copy-dex-files=false"}));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, but the SDC file contains the
+// wrong APEX versions.
+// Expect: The SDM file is rejected, while the DM file is still picked. Dexopt should be performed
+// if the compiler filter is better than "verify".
+TEST_P(OatFileAssistantTest, SdmApexVersionMismatch) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, "wrong-apex-version", sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(
+ &oat_file_assistant, "verify", "vdex", "up-to-date", OatFileAssistant::kLocationDm);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpaceProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpaceProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
class CollectDexCacheVisitor : public DexCacheVisitor {
public:
explicit CollectDexCacheVisitor(
@@ -2593,6 +2898,8 @@ TEST_P(OatFileAssistantTest, ValidateBootClassPathChecksums) {
// - Oat file corrupted after status check, before reload unexecutable
// because it's unrelocated and no dex2oat
-INSTANTIATE_TEST_SUITE_P(WithOrWithoutRuntime, OatFileAssistantTest, testing::Values(true, false));
+INSTANTIATE_TEST_SUITE_P(WithOrWithoutRuntime,
+ OatFileAssistantTest,
+ ::testing::Values(true, false));
} // namespace art
diff --git a/runtime/oat/sdc_file.cc b/runtime/oat/sdc_file.cc
new file mode 100644
index 0000000000..e141aea85e
--- /dev/null
+++ b/runtime/oat/sdc_file.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sdc_file.h"
+
+#include <cstdint>
+#include <memory>
+#include <regex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+#include "android-base/file.h"
+#include "android-base/parseint.h"
+#include "android-base/scopeguard.h"
+#include "base/macros.h"
+#include "base/utils.h"
+
+namespace art HIDDEN {
+
+using ::android::base::ParseInt;
+using ::android::base::ReadFileToString;
+using ::android::base::WriteStringToFd;
+
+std::unique_ptr<SdcReader> SdcReader::Load(const std::string& filename, std::string* error_msg) {
+ std::unique_ptr<SdcReader> reader(new SdcReader());
+
+ // The sdc file is supposed to be small, so read fully into memory for simplicity.
+ if (!ReadFileToString(filename, &reader->content_)) {
+ *error_msg = ART_FORMAT("Failed to load sdc file '{}': {}", filename, strerror(errno));
+ return nullptr;
+ }
+
+ std::vector<std::string_view> lines;
+ Split(reader->content_, '\n', &lines);
+ std::unordered_map<std::string_view, std::string_view> map;
+ for (std::string_view line : lines) {
+ size_t pos = line.find('=');
+ if (pos == std::string_view::npos || pos == 0) {
+ *error_msg = ART_FORMAT("Malformed line '{}' in sdc file '{}'", line, filename);
+ return nullptr;
+ }
+ if (!map.try_emplace(line.substr(0, pos), line.substr(pos + 1)).second) {
+ *error_msg = ART_FORMAT("Duplicate key '{}' in sdc file '{}'", line.substr(0, pos), filename);
+ return nullptr;
+ }
+ }
+
+ decltype(map)::iterator it;
+ if ((it = map.find("sdm-timestamp-ns")) == map.end()) {
+ *error_msg = ART_FORMAT("Missing key 'sdm-timestamp-ns' in sdc file '{}'", filename);
+ return nullptr;
+ }
+ if (!ParseInt(std::string(it->second), &reader->sdm_timestamp_ns_, /*min=*/INT64_C(1))) {
+ *error_msg = ART_FORMAT("Invalid 'sdm-timestamp-ns' {}", it->second);
+ return nullptr;
+ }
+
+ if ((it = map.find("apex-versions")) == map.end()) {
+ *error_msg = ART_FORMAT("Missing key 'apex-versions' in sdc file '{}'", filename);
+ return nullptr;
+ }
+ if (!std::regex_match(it->second.begin(), it->second.end(), std::regex("[0-9/]*"))) {
+ *error_msg = ART_FORMAT("Invalid 'apex-versions' {}", it->second);
+ return nullptr;
+ }
+ reader->apex_versions_ = it->second;
+
+ if (map.size() > 2) {
+ *error_msg = ART_FORMAT("Malformed sdc file '{}'. Unrecognized keys", filename);
+ return nullptr;
+ }
+
+ return reader;
+}
+
+bool SdcWriter::Save(std::string* error_msg) {
+ auto cleanup = android::base::make_scope_guard([this] { (void)file_.FlushClose(); });
+ if (sdm_timestamp_ns_ <= 0) {
+ *error_msg = ART_FORMAT("Invalid 'sdm-timestamp-ns' {}", sdm_timestamp_ns_);
+ return false;
+ }
+ DCHECK_EQ(file_.GetLength(), 0);
+ std::string content =
+ ART_FORMAT("sdm-timestamp-ns={}\napex-versions={}\n", sdm_timestamp_ns_, apex_versions_);
+ if (!WriteStringToFd(content, file_.Fd())) {
+ *error_msg = ART_FORMAT("Failed to write sdc file '{}': {}", file_.GetPath(), strerror(errno));
+ return false;
+ }
+ int res = file_.FlushClose();
+ if (res != 0) {
+ *error_msg =
+ ART_FORMAT("Failed to flush close sdc file '{}': {}", file_.GetPath(), strerror(-res));
+ return false;
+ }
+ cleanup.Disable();
+ return true;
+}
+
+} // namespace art
diff --git a/runtime/oat/sdc_file.h b/runtime/oat/sdc_file.h
new file mode 100644
index 0000000000..f4d7a5d116
--- /dev/null
+++ b/runtime/oat/sdc_file.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OAT_SDC_FILE_H_
+#define ART_RUNTIME_OAT_SDC_FILE_H_
+
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/os.h"
+
+namespace art HIDDEN {
+
+// A helper class to read a secure dex metadata companion (SDC) file.
+//
+// Secure dex metadata companion (SDC) file is a file type that augments a secure dex metadata (SDM)
+// file with additional metadata.
+//
+// 1. There may be exactly one SDC file accompanying each SDM file. An SDC file without a
+// corresponding SDM file, or with a mismatching SDM timestamp, is garbage.
+// 2. They are always local on device.
+// 3. They are only read and written by the ART module.
+// 4. A later version of the ART module must be able to understand the contents.
+//
+// It is a text file in the format of:
+// key1=value1\n
+// key2=value2\n
+// ...
+// Repeated keys are not allowed. This is an extensible format, so versioning is not needed.
+//
+// In principle, ART Service generates an SDC file for an SDM file during installation.
+// Specifically, during dexopt, which typically takes place during installation, if there is an SDM
+// file while the corresponding SDC file is missing (meaning the SDM file is newly installed) or
+// stale (meaning the SDM file is newly replaced), ART Service will generate a new SDC file. This
+// means an SDM file without a corresponding SDC file is a transient state and is valid from ART
+// Service's perspective.
+//
+// From the runtime's perspective, an SDM file without a corresponding SDC file is incomplete. That
+// means:
+// - At app execution time, the runtime ignores an SDM file without a corresponding SDC.
+// - ART Service's file GC, which uses the runtime's judgement, considers an SDM file without a
+// corresponding SDC invalid and may clean it up. This may race with a package installation before
+// the SDC is created, but it's rare and the effect is recoverable, so it's considered acceptable.
+class EXPORT SdcReader {
+ public:
+ static std::unique_ptr<SdcReader> Load(const std::string& filename, std::string* error_msg);
+
+ // The mtime of the SDM file on device, in nanoseconds.
+ // This is for detecting obsolete SDC files.
+ int64_t GetSdmTimestampNs() const { return sdm_timestamp_ns_; }
+
+ // The value of `Runtime::GetApexVersions` at the time where the SDM file was first seen on
+ // device. This is for detecting samegrade placebos.
+ std::string_view GetApexVersions() const { return apex_versions_; }
+
+ private:
+ SdcReader() = default;
+
+ std::string content_;
+ int64_t sdm_timestamp_ns_;
+ std::string_view apex_versions_;
+};
+
+// A helper class to write a secure dex metadata companion (SDC) file.
+class EXPORT SdcWriter {
+ public:
+ // Takes ownership of the file.
+ explicit SdcWriter(File&& file) : file_(std::move(file)) {}
+
+ // See `SdcReader::GetSdmTimestampNs`.
+ void SetSdmTimestampNs(int64_t value) { sdm_timestamp_ns_ = value; }
+
+ // See `SdcReader::GetApexVersions`.
+ void SetApexVersions(std::string_view value) { apex_versions_ = value; }
+
+ bool Save(std::string* error_msg);
+
+ private:
+ File file_;
+ int64_t sdm_timestamp_ns_ = 0;
+ std::string apex_versions_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_SDC_FILE_H_
diff --git a/runtime/oat/sdc_file_test.cc b/runtime/oat/sdc_file_test.cc
new file mode 100644
index 0000000000..eb1d3d6cb2
--- /dev/null
+++ b/runtime/oat/sdc_file_test.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sdc_file.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "android-base/file.h"
+#include "base/common_art_test.h"
+#include "base/macros.h"
+#include "base/os.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace art HIDDEN {
+
+using ::android::base::ReadFileToString;
+using ::android::base::WriteStringToFile;
+using ::testing::HasSubstr;
+using ::testing::StartsWith;
+
+class SdcFileTestBase : public CommonArtTest {
+ protected:
+ void SetUp() override {
+ CommonArtTest::SetUp();
+
+ scratch_dir_ = std::make_unique<ScratchDir>();
+ test_file_ = scratch_dir_->GetPath() + "test.sdc";
+ }
+
+ void TearDown() override {
+ scratch_dir_.reset();
+ CommonArtTest::TearDown();
+ }
+
+ std::unique_ptr<ScratchDir> scratch_dir_;
+ std::string test_file_;
+};
+
+class SdcReaderTest : public SdcFileTestBase {};
+
+TEST_F(SdcReaderTest, Success) {
+ ASSERT_TRUE(WriteStringToFile(
+ "sdm-timestamp-ns=987654321000000003\napex-versions=/12345678/12345679\n", test_file_));
+
+ std::string error_msg;
+ std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg);
+ ASSERT_NE(reader, nullptr) << error_msg;
+
+ EXPECT_EQ(reader->GetApexVersions(), "/12345678/12345679");
+ EXPECT_EQ(reader->GetSdmTimestampNs(), INT64_C(987654321000000003));
+}
+
+TEST_F(SdcReaderTest, NotFound) {
+ std::string error_msg;
+ std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg);
+ ASSERT_EQ(reader, nullptr);
+
+ EXPECT_THAT(error_msg, StartsWith("Failed to load sdc file"));
+}
+
+TEST_F(SdcReaderTest, MissingApexVersions) {
+ ASSERT_TRUE(WriteStringToFile("sdm-timestamp-ns=987654321\n", test_file_));
+
+ std::string error_msg;
+ std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg);
+ ASSERT_EQ(reader, nullptr);
+
+ EXPECT_THAT(error_msg, StartsWith("Missing key 'apex-versions' in sdc file"));
+}
+
+TEST_F(SdcReaderTest, InvalidSdmTimestamp) {
+ ASSERT_TRUE(
+ WriteStringToFile("sdm-timestamp-ns=0\napex-versions=/12345678/12345679\n", test_file_));
+
+ std::string error_msg;
+ std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg);
+ ASSERT_EQ(reader, nullptr);
+
+ EXPECT_THAT(error_msg, HasSubstr("Invalid 'sdm-timestamp-ns'"));
+}
+
+TEST_F(SdcReaderTest, InvalidApexVersions) {
+ ASSERT_TRUE(WriteStringToFile("sdm-timestamp-ns=987654321\napex-versions=abc\n", test_file_));
+
+ std::string error_msg;
+ std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg);
+ ASSERT_EQ(reader, nullptr);
+
+ EXPECT_THAT(error_msg, HasSubstr("Invalid 'apex-versions'"));
+}
+
+TEST_F(SdcReaderTest, UnrecognizedKey) {
+ ASSERT_TRUE(WriteStringToFile(
+ "sdm-timestamp-ns=987654321\napex-versions=/12345678/12345679\nwrong-key=12345678\n",
+ test_file_));
+
+ std::string error_msg;
+ std::unique_ptr<SdcReader> reader = SdcReader::Load(test_file_, &error_msg);
+ ASSERT_EQ(reader, nullptr);
+
+ EXPECT_THAT(error_msg, HasSubstr("Unrecognized keys"));
+}
+
+class SdcWriterTest : public SdcFileTestBase {};
+
+TEST_F(SdcWriterTest, Success) {
+ std::unique_ptr<File> file(OS::CreateEmptyFileWriteOnly(test_file_.c_str()));
+ ASSERT_NE(file, nullptr);
+ SdcWriter writer(std::move(*file));
+
+ writer.SetApexVersions("/12345678/12345679");
+ writer.SetSdmTimestampNs(987654321l);
+
+ std::string error_msg;
+ ASSERT_TRUE(writer.Save(&error_msg)) << error_msg;
+
+ std::string content;
+ ASSERT_TRUE(ReadFileToString(test_file_, &content));
+
+ EXPECT_EQ(content, "sdm-timestamp-ns=987654321\napex-versions=/12345678/12345679\n");
+}
+
+TEST_F(SdcWriterTest, SaveFailed) {
+ ASSERT_TRUE(WriteStringToFile("", test_file_));
+
+ std::unique_ptr<File> file(OS::OpenFileForReading(test_file_.c_str()));
+ ASSERT_NE(file, nullptr);
+ SdcWriter writer(
+ File(file->Release(), file->GetPath(), /*check_usage=*/false, /*read_only_mode=*/false));
+
+ writer.SetApexVersions("/12345678/12345679");
+ writer.SetSdmTimestampNs(987654321l);
+
+ std::string error_msg;
+ EXPECT_FALSE(writer.Save(&error_msg));
+
+ EXPECT_THAT(error_msg, StartsWith("Failed to write sdc file"));
+}
+
+TEST_F(SdcWriterTest, InvalidSdmTimestamp) {
+ std::unique_ptr<File> file(OS::CreateEmptyFileWriteOnly(test_file_.c_str()));
+ ASSERT_NE(file, nullptr);
+ SdcWriter writer(std::move(*file));
+
+ writer.SetApexVersions("/12345678/12345679");
+
+ std::string error_msg;
+ EXPECT_FALSE(writer.Save(&error_msg));
+
+ EXPECT_THAT(error_msg, StartsWith("Invalid 'sdm-timestamp-ns'"));
+}
+
+} // namespace art
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
index 3a68381117..238aea3152 100644
--- a/sigchainlib/sigchain_test.cc
+++ b/sigchainlib/sigchain_test.cc
@@ -104,7 +104,8 @@ static void TestSignalBlocking(const std::function<void()>& fn) {
fn();
- if (testing::Test::HasFatalFailure()) return;
+ if (::testing::Test::HasFatalFailure())
+ return;
ASSERT_EQ(0, RealSigprocmask(SIG_SETMASK, nullptr, &mask));
ASSERT_FALSE(sigismember64(&mask, SIGSEGV));
}
@@ -266,7 +267,7 @@ DISABLE_HWASAN void fault_address_tag_impl() {
auto* tagged_null = reinterpret_cast<int*>(0x2bULL << 56);
EXPECT_EXIT(
- { [[maybe_unused]] volatile int load = *tagged_null; }, testing::ExitedWithCode(0), "");
+ { [[maybe_unused]] volatile int load = *tagged_null; }, ::testing::ExitedWithCode(0), "");
// Our sigaction implementation always implements the "clear unknown bits"
// semantics for oldact.sa_flags regardless of kernel version so we rely on it
@@ -275,9 +276,10 @@ DISABLE_HWASAN void fault_address_tag_impl() {
ASSERT_EQ(0, sigaction(SIGSEGV, &action, nullptr));
ASSERT_EQ(0, sigaction(SIGSEGV, nullptr, &action));
if (action.sa_flags & SA_EXPOSE_TAGBITS) {
- EXPECT_EXIT({ [[maybe_unused]] volatile int load = *tagged_null; },
- testing::ExitedWithCode(0x2b),
- "");
+ EXPECT_EXIT(
+ { [[maybe_unused]] volatile int load = *tagged_null; },
+ ::testing::ExitedWithCode(0x2b),
+ "");
}
}
#endif
diff --git a/test/Android.bp b/test/Android.bp
index d3084fe7d2..76559a92c4 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -363,6 +363,10 @@ art_cc_defaults {
},
},
static_libs: [
+ // This dependency links the whole runtime statically into the test. Note that the boot
+ // classpath is not (normally) bundled with the test, so if the runtime is used to actually
+ // start a VM it may load the boot classpath from the device. Depending on the test
+ // configuration, that may not be in sync with the statically linked runtime.
"libart-gtest",
],
version_script: ":art-standalone-gtest-version",
diff --git a/test/default_run.py b/test/default_run.py
index 85cf4a97ad..21673f79a3 100755
--- a/test/default_run.py
+++ b/test/default_run.py
@@ -920,19 +920,11 @@ def default_run(ctx, args, **kwargs):
if SIMPLEPERF:
dalvikvm_cmdline = f"simpleperf record {dalvikvm_cmdline} && simpleperf report"
- def sanitize_dex2oat_cmdline(cmdline: str) -> str:
- args = []
- for arg in cmdline.split(" "):
- if arg == "--class-loader-context=&":
- arg = r"--class-loader-context=\&"
- args.append(arg)
- return " ".join(args)
-
# Remove whitespace.
- dex2oat_cmdline = sanitize_dex2oat_cmdline(dex2oat_cmdline)
+ dex2oat_cmdline = re.sub(" +", " ", dex2oat_cmdline)
dalvikvm_cmdline = re.sub(" +", " ", dalvikvm_cmdline)
dm_cmdline = re.sub(" +", " ", dm_cmdline)
- vdex_cmdline = sanitize_dex2oat_cmdline(vdex_cmdline)
+ vdex_cmdline = re.sub(" +", " ", vdex_cmdline)
profman_cmdline = re.sub(" +", " ", profman_cmdline)
# Use an empty ASAN_OPTIONS to enable defaults.
diff --git a/test/generate-boot-image/generate-boot-image.cc b/test/generate-boot-image/generate-boot-image.cc
index 454b63185a..539670fa85 100644
--- a/test/generate-boot-image/generate-boot-image.cc
+++ b/test/generate-boot-image/generate-boot-image.cc
@@ -46,7 +46,6 @@ using ::android::base::ParseBool;
using ::android::base::ParseBoolResult;
using ::android::base::StringPrintf;
using ::art::testing::GetLibCoreDexFileNames;
-using ::art::testing::GetLibCoreDexLocations;
constexpr const char* kUsage = R"(
A commandline tool to generate a primary boot image for testing.
@@ -141,7 +140,7 @@ int GenerateBootImage(const Options& options) {
if (options.android_root_for_location) {
dex_locations = dex_files;
} else {
- dex_locations = GetLibCoreDexLocations(options.core_only);
+ dex_locations = GetLibCoreDexFileNames(/*prefix=*/"", options.core_only);
}
args.push_back("--runtime-arg");
args.push_back("-Xbootclasspath:" + Join(dex_files, ":"));
diff --git a/test/standalone_test_lib_check.cc b/test/standalone_test_lib_check.cc
index 426a302f54..72c835a658 100644
--- a/test/standalone_test_lib_check.cc
+++ b/test/standalone_test_lib_check.cc
@@ -180,6 +180,6 @@ TEST(StandaloneTestAllowedLibDeps, test) {
disallowed_libs.push_back(dyn_lib_dep);
}
- EXPECT_THAT(disallowed_libs, testing::IsEmpty())
+ EXPECT_THAT(disallowed_libs, ::testing::IsEmpty())
<< path_to_self.value() << " has disallowed shared library dependencies.";
}
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 6d987487f5..6fee7e881a 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -29,7 +29,8 @@
description: "Timeouts on host with gcstress and debug.",
result: EXEC_FAILED,
modes: [host],
- names: ["jsr166.ForkJoinPoolTest#testIsQuiescent",
+ names: ["jsr166.ConcurrentSkipListSetTest#testRecursiveSubSets",
+ "jsr166.ForkJoinPoolTest#testIsQuiescent",
"jsr166.ScheduledExecutorSubclassTest#testTimedInvokeAll4",
"jsr166.StampedLockTest#testWriteAfterReadLock",
"jsr166.StampedLockTest#testReadTryLock_Interruptible",
@@ -37,6 +38,7 @@
"jsr166.StampedLockTest#testReadLockInterruptibly",
"jsr166.StampedLockTest#testWriteLockInterruptibly",
"jsr166.TimeUnitTest#testConvert",
+ "jsr166.TreeMapTest#testRecursiveSubMaps",
"jsr166.TreeSetTest#testRecursiveSubSets",
"libcore.java.lang.OldThreadTest#test_getState",
"libcore.java.lang.StringTest#testFastPathString_wellFormedUtf8Sequence",
@@ -53,7 +55,8 @@
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_removeJ",
"org.apache.harmony.tests.java.lang.ProcessManagerTest#testSleep",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
- "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"
+ "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext",
+ "test.java.util.Collections.RacingCollections#main"
]
},
{
diff --git a/tools/luci/config/generated/cr-buildbucket.cfg b/tools/luci/config/generated/cr-buildbucket.cfg
index fd7bfdaf48..cef584cf10 100644
--- a/tools/luci/config/generated/cr-buildbucket.cfg
+++ b/tools/luci/config/generated/cr-buildbucket.cfg
@@ -11,7 +11,7 @@ buckets {
group: "project-art-admins"
}
acls {
- group: "all"
+ group: "googlers"
}
swarming {
builders {
@@ -898,7 +898,7 @@ buckets {
group: "project-art-admins"
}
acls {
- group: "all"
+ group: "googlers"
}
constraints {
pools: "luci.art.ci"
diff --git a/tools/luci/config/generated/luci-logdog.cfg b/tools/luci/config/generated/luci-logdog.cfg
index 01a391261d..9d817a1e14 100644
--- a/tools/luci/config/generated/luci-logdog.cfg
+++ b/tools/luci/config/generated/luci-logdog.cfg
@@ -4,6 +4,6 @@
# For the schema of this file, see ProjectConfig message:
# https://config.luci.app/schemas/projects:luci-logdog.cfg
-reader_auth_groups: "all"
+reader_auth_groups: "googlers"
writer_auth_groups: "luci-logdog-chromium-writers"
archive_gs_bucket: "chromium-luci-logdog"
diff --git a/tools/luci/config/generated/luci-scheduler.cfg b/tools/luci/config/generated/luci-scheduler.cfg
index 4888f69517..a2b426ee19 100644
--- a/tools/luci/config/generated/luci-scheduler.cfg
+++ b/tools/luci/config/generated/luci-scheduler.cfg
@@ -439,6 +439,6 @@ acl_sets {
granted_to: "group:project-art-admins"
}
acls {
- granted_to: "group:all"
+ granted_to: "group:googlers"
}
}
diff --git a/tools/luci/config/generated/project.cfg b/tools/luci/config/generated/project.cfg
index 3a46212fd6..5619b9ab83 100644
--- a/tools/luci/config/generated/project.cfg
+++ b/tools/luci/config/generated/project.cfg
@@ -5,9 +5,9 @@
# https://config.luci.app/schemas/projects:project.cfg
name: "art"
-access: "group:all"
+access: "group:googlers"
lucicfg {
- version: "1.43.16"
+ version: "1.44.1"
package_dir: ".."
config_dir: "generated"
entry_point: "main.star"
diff --git a/tools/luci/config/generated/realms.cfg b/tools/luci/config/generated/realms.cfg
index d439ea168c..ade0991042 100644
--- a/tools/luci/config/generated/realms.cfg
+++ b/tools/luci/config/generated/realms.cfg
@@ -12,15 +12,15 @@ realms {
}
bindings {
role: "role/buildbucket.reader"
- principals: "group:all"
+ principals: "group:googlers"
}
bindings {
role: "role/configs.reader"
- principals: "group:all"
+ principals: "group:googlers"
}
bindings {
role: "role/logdog.reader"
- principals: "group:all"
+ principals: "group:googlers"
}
bindings {
role: "role/logdog.writer"
@@ -32,7 +32,7 @@ realms {
}
bindings {
role: "role/scheduler.reader"
- principals: "group:all"
+ principals: "group:googlers"
}
bindings {
role: "role/swarming.poolOwner"
@@ -44,7 +44,7 @@ realms {
}
bindings {
role: "role/swarming.poolViewer"
- principals: "group:all"
+ principals: "group:googlers"
}
bindings {
role: "role/swarming.taskTriggerer"
diff --git a/tools/luci/config/main.star b/tools/luci/config/main.star
index 2c530ba229..42ee495bbc 100755
--- a/tools/luci/config/main.star
+++ b/tools/luci/config/main.star
@@ -47,7 +47,6 @@ luci.project(
scheduler = "luci-scheduler.appspot.com",
swarming = "chromium-swarm.appspot.com",
acls = [
- # Publicly readable.
acl.entry(
roles = [
acl.BUILDBUCKET_READER,
@@ -55,7 +54,7 @@ luci.project(
acl.PROJECT_CONFIGS_READER,
acl.SCHEDULER_READER,
],
- groups = "all",
+ groups = "googlers",
),
acl.entry(
roles = [
@@ -76,7 +75,7 @@ luci.project(
),
luci.binding(
roles = "role/swarming.poolViewer",
- groups = "all",
+ groups = "googlers",
),
],
)