summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jiakai Zhang <jiakaiz@google.com> 2025-03-21 03:40:36 -0700
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2025-03-21 03:40:36 -0700
commita7652079683fc97ef40b793312c39a613523262c (patch)
tree55ac1df9ea1d449630016889fbf480dfb528777c
parent09ac8285d7ad56b01982c02fdcc81741a3697ade (diff)
parentaa405ccd29b7317dfe499fa63cc9fdaef2f049fb (diff)
Add an artd method to create an SDC file. am: aa405ccd29
Original change: https://android-review.googlesource.com/c/platform/art/+/3540979 Change-Id: I37d556aa308550170f8cf8ec8a0e8ccd2b99944e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--artd/artd.cc87
-rw-r--r--artd/artd.h4
-rw-r--r--artd/artd_test.cc150
-rw-r--r--artd/binder/com/android/server/art/IArtd.aidl9
-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.cc58
-rw-r--r--artd/path_utils.h10
-rw-r--r--artd/path_utils_test.cc15
-rw-r--r--libartbase/base/time_utils.h9
-rw-r--r--runtime/oat/oat_file_assistant_context.h2
-rw-r--r--runtime/oat/sdc_file.cc15
-rw-r--r--runtime/oat/sdc_file.h15
-rw-r--r--runtime/oat/sdc_file_test.cc26
14 files changed, 378 insertions, 91 deletions
diff --git a/artd/artd.cc b/artd/artd.cc
index d781750306..974712eccf 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,6 +116,7 @@ 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;
@@ -269,20 +271,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 +992,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 +1087,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));
}
diff --git a/artd/artd.h b/artd/artd.h
index 7426901688..ba34ac4ebf 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -217,6 +217,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,
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
index 76167c58fb..10116c7994 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;
@@ -2413,6 +2500,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 +2519,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);
diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
index 7e9cf9da58..12932a345b 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
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..bb752d00c8 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;
@@ -148,9 +149,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 +168,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 +316,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/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/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/sdc_file.cc b/runtime/oat/sdc_file.cc
index 35a759e576..59b9655ac2 100644
--- a/runtime/oat/sdc_file.cc
+++ b/runtime/oat/sdc_file.cc
@@ -16,6 +16,7 @@
#include "sdc_file.h"
+#include <cstdint>
#include <memory>
#include <regex>
#include <string>
@@ -60,12 +61,12 @@ std::unique_ptr<SdcReader> SdcReader::Load(const std::string filename, std::stri
}
decltype(map)::iterator it;
- if ((it = map.find("sdm-timestamp")) == map.end()) {
- *error_msg = ART_FORMAT("Missing key 'sdm-timestamp' in sdc file '{}'", filename);
+ 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_, /*min=*/1l)) {
- *error_msg = ART_FORMAT("Invalid 'sdm-timestamp' {}", it->second);
+ 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;
}
@@ -89,13 +90,13 @@ std::unique_ptr<SdcReader> SdcReader::Load(const std::string filename, std::stri
bool SdcWriter::Save(std::string* error_msg) {
auto cleanup = android::base::make_scope_guard([this] { (void)file_.FlushClose(); });
- if (sdm_timestamp_ <= 0) {
- *error_msg = ART_FORMAT("Invalid 'sdm-timestamp' {}", sdm_timestamp_);
+ 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={}\napex-versions={}\n", sdm_timestamp_, apex_versions_);
+ 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;
diff --git a/runtime/oat/sdc_file.h b/runtime/oat/sdc_file.h
index 18c34a3527..73917f5316 100644
--- a/runtime/oat/sdc_file.h
+++ b/runtime/oat/sdc_file.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_OAT_SDC_FILE_H_
#define ART_RUNTIME_OAT_SDC_FILE_H_
-#include <ctime>
#include <memory>
#include <string>
#include <string_view>
@@ -44,13 +43,13 @@ namespace art HIDDEN {
// key2=value2\n
// ...
// Repeated keys are not allowed. This is an extensible format, so versioning is not needed.
-class SdcReader {
+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.
+ // The mtime of the SDM file on device, in nanoseconds.
// This is for detecting obsolete SDC files.
- time_t GetSdmTimestamp() const { return sdm_timestamp_; }
+ 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.
@@ -60,7 +59,7 @@ class SdcReader {
SdcReader() = default;
std::string content_;
- time_t sdm_timestamp_;
+ int64_t sdm_timestamp_ns_;
std::string_view apex_versions_;
};
@@ -70,8 +69,8 @@ class EXPORT SdcWriter {
// Takes ownership of the file.
explicit SdcWriter(File&& file) : file_(std::move(file)) {}
- // See `SdcReader::GetSdmTimestamp`.
- void SetSdmTimestamp(time_t value) { sdm_timestamp_ = value; }
+ // See `SdcReader::GetSdmTimestampNs`.
+ void SetSdmTimestampNs(int64_t value) { sdm_timestamp_ns_ = value; }
// See `SdcReader::GetApexVersions`.
void SetApexVersions(std::string_view value) { apex_versions_ = value; }
@@ -80,7 +79,7 @@ class EXPORT SdcWriter {
private:
File file_;
- time_t sdm_timestamp_ = 0;
+ int64_t sdm_timestamp_ns_ = 0;
std::string apex_versions_;
};
diff --git a/runtime/oat/sdc_file_test.cc b/runtime/oat/sdc_file_test.cc
index f3bf1bc36d..eb1d3d6cb2 100644
--- a/runtime/oat/sdc_file_test.cc
+++ b/runtime/oat/sdc_file_test.cc
@@ -16,6 +16,7 @@
#include "sdc_file.h"
+#include <cstdint>
#include <memory>
#include <string>
@@ -54,15 +55,15 @@ class SdcFileTestBase : public CommonArtTest {
class SdcReaderTest : public SdcFileTestBase {};
TEST_F(SdcReaderTest, Success) {
- ASSERT_TRUE(
- WriteStringToFile("sdm-timestamp=987654321\napex-versions=/12345678/12345679\n", test_file_));
+ 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->GetSdmTimestamp(), 987654321l);
+ EXPECT_EQ(reader->GetSdmTimestampNs(), INT64_C(987654321000000003));
}
TEST_F(SdcReaderTest, NotFound) {
@@ -74,7 +75,7 @@ TEST_F(SdcReaderTest, NotFound) {
}
TEST_F(SdcReaderTest, MissingApexVersions) {
- ASSERT_TRUE(WriteStringToFile("sdm-timestamp=987654321\n", test_file_));
+ 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);
@@ -84,17 +85,18 @@ TEST_F(SdcReaderTest, MissingApexVersions) {
}
TEST_F(SdcReaderTest, InvalidSdmTimestamp) {
- ASSERT_TRUE(WriteStringToFile("sdm-timestamp=0\napex-versions=/12345678/12345679\n", test_file_));
+ 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'"));
+ EXPECT_THAT(error_msg, HasSubstr("Invalid 'sdm-timestamp-ns'"));
}
TEST_F(SdcReaderTest, InvalidApexVersions) {
- ASSERT_TRUE(WriteStringToFile("sdm-timestamp=987654321\napex-versions=abc\n", test_file_));
+ 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);
@@ -105,7 +107,7 @@ TEST_F(SdcReaderTest, InvalidApexVersions) {
TEST_F(SdcReaderTest, UnrecognizedKey) {
ASSERT_TRUE(WriteStringToFile(
- "sdm-timestamp=987654321\napex-versions=/12345678/12345679\nwrong-key=12345678\n",
+ "sdm-timestamp-ns=987654321\napex-versions=/12345678/12345679\nwrong-key=12345678\n",
test_file_));
std::string error_msg;
@@ -123,7 +125,7 @@ TEST_F(SdcWriterTest, Success) {
SdcWriter writer(std::move(*file));
writer.SetApexVersions("/12345678/12345679");
- writer.SetSdmTimestamp(987654321l);
+ writer.SetSdmTimestampNs(987654321l);
std::string error_msg;
ASSERT_TRUE(writer.Save(&error_msg)) << error_msg;
@@ -131,7 +133,7 @@ TEST_F(SdcWriterTest, Success) {
std::string content;
ASSERT_TRUE(ReadFileToString(test_file_, &content));
- EXPECT_EQ(content, "sdm-timestamp=987654321\napex-versions=/12345678/12345679\n");
+ EXPECT_EQ(content, "sdm-timestamp-ns=987654321\napex-versions=/12345678/12345679\n");
}
TEST_F(SdcWriterTest, SaveFailed) {
@@ -143,7 +145,7 @@ TEST_F(SdcWriterTest, SaveFailed) {
File(file->Release(), file->GetPath(), /*check_usage=*/false, /*read_only_mode=*/false));
writer.SetApexVersions("/12345678/12345679");
- writer.SetSdmTimestamp(987654321l);
+ writer.SetSdmTimestampNs(987654321l);
std::string error_msg;
EXPECT_FALSE(writer.Save(&error_msg));
@@ -161,7 +163,7 @@ TEST_F(SdcWriterTest, InvalidSdmTimestamp) {
std::string error_msg;
EXPECT_FALSE(writer.Save(&error_msg));
- EXPECT_THAT(error_msg, StartsWith("Invalid 'sdm-timestamp'"));
+ EXPECT_THAT(error_msg, StartsWith("Invalid 'sdm-timestamp-ns'"));
}
} // namespace art