summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jiakai Zhang <jiakaiz@google.com> 2025-03-14 16:54:03 +0000
committer Jiakai Zhang <jiakaiz@google.com> 2025-03-21 09:37:10 -0700
commit0f7d94bd99bb554d663a4a527155eee6192780ee (patch)
tree4c3a8211872d9a0ed91cebe00136b07c3ca877e4
parent0e235bd9b79c82e3962792eecb17ceb4bbc24089 (diff)
Count SDM files into TYPE_DEXOPT_ARTIFACT in ART-managed file stats.
SDM files contain ODEX and ART files, which are dexopt artifacts. We don't need to count the DM files containing VDEX files because the storage manager counts them itself. This CL implements the extension to getUsableArtifacts to achieve this, which is also necessary for the file GC in the next CL. Also: - Fix a bug that the runtime images were not counted if the artifact location is ArtifactsLocation.DM. - Replace @AutoValue classes with records, to simplify the code. Bug: 377474232 Test: atest ArtServiceTests Test: atest art_standalone_artd_tests Change-Id: If1d05d69051d09ebd7f10e6f487cd7892590921c
-rw-r--r--artd/artd.cc9
-rw-r--r--artd/artd.h5
-rw-r--r--artd/artd_test.cc13
-rw-r--r--artd/binder/com/android/server/art/IArtd.aidl10
-rw-r--r--libartservice/service/java/com/android/server/art/ArtFileManager.java100
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagerLocal.java4
-rw-r--r--libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java58
7 files changed, 132 insertions, 67 deletions
diff --git a/artd/artd.cc b/artd/artd.cc
index 974712eccf..68574b2ce8 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -120,6 +120,7 @@ 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;
@@ -1454,6 +1455,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 ba34ac4ebf..affb180b08 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"
@@ -264,6 +265,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 10116c7994..d6e383daf3 100644
--- a/artd/artd_test.cc
+++ b/artd/artd_test.cc
@@ -2579,6 +2579,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/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
index 12932a345b..8b6ba8b94c 100644
--- a/artd/binder/com/android/server/art/IArtd.aidl
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -260,6 +260,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/libartservice/service/java/com/android/server/art/ArtFileManager.java b/libartservice/service/java/com/android/server/art/ArtFileManager.java
index 754b9ec1dd..8e382aa1d8 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;
@@ -126,7 +125,7 @@ public class ArtFileManager {
}
}
- return WritableArtifactLists.create(artifacts, runtimeArtifacts);
+ return new WritableArtifactLists(artifacts, runtimeArtifacts);
}
/** Returns artifacts that are usable, regardless of whether they are writable. */
@@ -135,6 +134,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 +159,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 +197,7 @@ public class ArtFileManager {
}
}
- return UsableArtifactLists.create(artifacts, vdexFiles, runtimeArtifacts);
+ return new UsableArtifactLists(artifacts, vdexFiles, sdmFiles, runtimeArtifacts);
}
@NonNull
@@ -209,7 +230,7 @@ public class ArtFileManager {
}
}
- return ProfileLists.create(refProfiles, curProfiles);
+ return new ProfileLists(refProfiles, curProfiles);
}
@NonNull
@@ -221,71 +242,16 @@ 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<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..acd6063f11 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -1056,6 +1056,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()
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index a791211705..b17475480e 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -1426,6 +1426,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));