summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jiakai Zhang <jiakaiz@google.com> 2024-11-07 15:59:21 +0000
committer Jiakai Zhang <jiakaiz@google.com> 2024-11-26 22:36:40 +0000
commita3d9ad8d83692399056838c5adda3142a9adb491 (patch)
tree44235569ddde90cf2b70a1c648761ccd9a55457d
parent86d746b7357fda4838479ef82ae0193aed0f8750 (diff)
Add APIs for ART managed install files.
Bug: 377887025 Test: atest ArtServiceTests Test: atest art_standalone_artd_tests Change-Id: I0836a0523dd80b24b0a0452efec023282237824f
-rw-r--r--artd/artd_test.cc1
-rw-r--r--artd/binder/com/android/server/art/ArtConstants.aidl7
-rw-r--r--libartbase/base/file_utils.h1
-rw-r--r--libartservice/service/api/system-server-current.txt6
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagedInstallFileHelper.java94
-rw-r--r--libartservice/service/java/com/android/server/art/DexMetadataHelper.java5
-rw-r--r--libartservice/service/java/com/android/server/art/Utils.java13
-rw-r--r--libartservice/service/javatests/com/android/server/art/ArtManagedInstallFileHelperTest.java81
-rw-r--r--libartservice/service/javatests/com/android/server/art/UtilsTest.java15
9 files changed, 219 insertions, 4 deletions
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
index 27dfbb34c5..0a06a0917d 100644
--- a/artd/artd_test.cc
+++ b/artd/artd_test.cc
@@ -597,6 +597,7 @@ class ArtdTest : public CommonArtTest {
TEST_F(ArtdTest, ConstantsAreInSync) {
EXPECT_STREQ(ArtConstants::REASON_VDEX, kReasonVdex);
EXPECT_STREQ(ArtConstants::DEX_METADATA_FILE_EXT, kDmExtension);
+ EXPECT_STREQ(ArtConstants::SECURE_DEX_METADATA_FILE_EXT, kSdmExtension);
EXPECT_STREQ(ArtConstants::DEX_METADATA_PROFILE_ENTRY,
ProfileCompilationInfo::kDexMetadataProfileEntry);
EXPECT_STREQ(ArtConstants::DEX_METADATA_VDEX_ENTRY, VdexFile::kVdexNameInDmFile);
diff --git a/artd/binder/com/android/server/art/ArtConstants.aidl b/artd/binder/com/android/server/art/ArtConstants.aidl
index 6e438f7efc..53fa75dead 100644
--- a/artd/binder/com/android/server/art/ArtConstants.aidl
+++ b/artd/binder/com/android/server/art/ArtConstants.aidl
@@ -49,6 +49,13 @@ parcelable ArtConstants {
const @utf8InCpp String PROFILE_FILE_EXT = ".prof";
/**
+ * The file extension of the secure dex metadata file.
+ *
+ * Keep in sync with {@code kSdmExtension} in art/libartbase/base/file_utils.h.
+ */
+ const @utf8InCpp String SECURE_DEX_METADATA_FILE_EXT = ".sdm";
+
+ /**
* The name of the profile entry in the dex metadata file.
*
* Keep in sync with {@code ProfileCompilationInfo::kDexMetadataProfileEntry} in
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 8a25fe1d9f..7f52d03497 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -38,6 +38,7 @@ static constexpr const char* kOdexExtension = ".odex";
static constexpr const char* kVdexExtension = ".vdex";
static constexpr const char* kArtExtension = ".art";
static constexpr const char* kDmExtension = ".dm";
+static constexpr const char* kSdmExtension = ".sdm";
// These methods return the Android Root, which is the historical location of
// the Android "system" directory, containing the built Android artifacts. On
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
index b563e7df5c..b7a3739370 100644
--- a/libartservice/service/api/system-server-current.txt
+++ b/libartservice/service/api/system-server-current.txt
@@ -1,6 +1,12 @@
// Signature format: 2.0
package com.android.server.art {
+ @FlaggedApi("com.android.art.flags.art_service_v3") public final class ArtManagedInstallFileHelper {
+ method @FlaggedApi("com.android.art.flags.art_service_v3") @NonNull public static java.util.List<java.lang.String> filterPathsForApk(@NonNull java.util.List<java.lang.String>, @NonNull String);
+ method @FlaggedApi("com.android.art.flags.art_service_v3") @NonNull public static String getTargetPathForApk(@NonNull String, @NonNull String);
+ method @FlaggedApi("com.android.art.flags.art_service_v3") public static boolean isArtManaged(@NonNull String);
+ }
+
public final class ArtManagerLocal {
ctor @Deprecated public ArtManagerLocal();
ctor public ArtManagerLocal(@NonNull android.content.Context);
diff --git a/libartservice/service/java/com/android/server/art/ArtManagedInstallFileHelper.java b/libartservice/service/java/com/android/server/art/ArtManagedInstallFileHelper.java
new file mode 100644
index 0000000000..dd040165e3
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/ArtManagedInstallFileHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.art.flags.Flags;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class for <i>ART-managed install files</i> (files installed by Package Manager
+ * and managed by ART).
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ART_SERVICE_V3)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+public final class ArtManagedInstallFileHelper {
+ private static final List<String> FILE_TYPES = List.of(ArtConstants.DEX_METADATA_FILE_EXT,
+ ArtConstants.PROFILE_FILE_EXT, ArtConstants.SECURE_DEX_METADATA_FILE_EXT);
+
+ private ArtManagedInstallFileHelper() {}
+
+ /**
+ * Returns whether the file at the given path is an <i>ART-managed install file</i>. This
+ * is a pure string operation on the input and does not involve any I/O.
+ */
+ @FlaggedApi(Flags.FLAG_ART_SERVICE_V3)
+ public static boolean isArtManaged(@NonNull String path) {
+ return FILE_TYPES.stream().anyMatch(ext -> path.endsWith(ext));
+ }
+
+ /**
+ * Returns the subset of the given paths that are paths to the <i>ART-managed install files</i>
+ * corresponding to the given APK path. This is a pure string operation on the inputs and does
+ * not involve any I/O.
+ *
+ * Note that the files in different directories than the APK are not considered corresponding to
+ * the APK.
+ */
+ @FlaggedApi(Flags.FLAG_ART_SERVICE_V3)
+ public static @NonNull List<String> filterPathsForApk(
+ @NonNull List<String> paths, @NonNull String apkPath) {
+ Set<String> candidates = FILE_TYPES.stream()
+ .map(ext -> Utils.replaceFileExtension(apkPath, ext))
+ .collect(Collectors.toSet());
+ return paths.stream().filter(path -> candidates.contains(path)).toList();
+ }
+
+ /**
+ * Rewrites the path to the <i>ART-managed install file</i> so that it corresponds to the given
+ * APK path. This is a pure string operation on the inputs and does not involve any I/O.
+ *
+ * Note that the result path is always in the same directory as the APK, in order to correspond
+ * to the APK.
+ *
+ * @throws IllegalArgumentException if {@code originalPath} does not represent an <i>ART-managed
+ * install file</i>
+ */
+ @FlaggedApi(Flags.FLAG_ART_SERVICE_V3)
+ public static @NonNull String getTargetPathForApk(
+ @NonNull String originalPath, @NonNull String apkPath) {
+ for (String ext : FILE_TYPES) {
+ if (originalPath.endsWith(ext)) {
+ return Utils.replaceFileExtension(apkPath, ext);
+ }
+ }
+ throw new IllegalArgumentException(
+ "Illegal ART managed install file path '" + originalPath + "'");
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/DexMetadataHelper.java b/libartservice/service/java/com/android/server/art/DexMetadataHelper.java
index f5b2a81251..9742c5f40e 100644
--- a/libartservice/service/java/com/android/server/art/DexMetadataHelper.java
+++ b/libartservice/service/java/com/android/server/art/DexMetadataHelper.java
@@ -86,10 +86,7 @@ public class DexMetadataHelper {
@NonNull
public static String getDmPath(@NonNull DexMetadataPath dmPath) {
- String dexPath = dmPath.dexPath;
- int pos = dexPath.lastIndexOf(".");
- return (pos != -1 ? dexPath.substring(0, pos) : dexPath)
- + ArtConstants.DEX_METADATA_FILE_EXT;
+ return Utils.replaceFileExtension(dmPath.dexPath, ArtConstants.DEX_METADATA_FILE_EXT);
}
private static @DexMetadata.Type int getType(@NonNull ZipFile zipFile) {
diff --git a/libartservice/service/java/com/android/server/art/Utils.java b/libartservice/service/java/com/android/server/art/Utils.java
index 600671fcb2..e042c3e106 100644
--- a/libartservice/service/java/com/android/server/art/Utils.java
+++ b/libartservice/service/java/com/android/server/art/Utils.java
@@ -501,6 +501,19 @@ public final class Utils {
return path.length() == prefixLen || path.charAt(prefixLen) == '/';
}
+ /**
+ * Replaces the file extension of the given path with the given new extension.
+ *
+ * @param path the path to replace the extension for
+ * @param newExtension the new extension, including the leading dot
+ */
+ @NonNull
+ public static String replaceFileExtension(@NonNull String path, @NonNull String newExtension) {
+ int pos = path.lastIndexOf('.');
+ int slashPos = path.indexOf('/', pos);
+ return ((pos != -1 && slashPos == -1) ? path.substring(0, pos) : path) + newExtension;
+ }
+
@AutoValue
public abstract static class Abi {
static @NonNull Abi create(
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagedInstallFileHelperTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagedInstallFileHelperTest.java
new file mode 100644
index 0000000000..4b13716eaa
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagedInstallFileHelperTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ArtManagedInstallFileHelperTest {
+ @Test
+ public void testIsArtManaged() throws Exception {
+ assertThat(ArtManagedInstallFileHelper.isArtManaged("/foo/bar.dm")).isTrue();
+ assertThat(ArtManagedInstallFileHelper.isArtManaged("/foo/bar.prof")).isTrue();
+ assertThat(ArtManagedInstallFileHelper.isArtManaged("/foo/bar.sdm")).isTrue();
+ assertThat(ArtManagedInstallFileHelper.isArtManaged("/foo/bar.abc")).isFalse();
+ }
+
+ @Test
+ public void testFilterPathsForApk() throws Exception {
+ assertThat(ArtManagedInstallFileHelper.filterPathsForApk(
+ List.of("/foo/bar.dm", "/foo/bar.prof", "/foo/bar.sdm", "/foo/bar.abc",
+ "/foo/baz.dm"),
+ "/foo/bar.apk"))
+ .containsExactly("/foo/bar.dm", "/foo/bar.prof", "/foo/bar.sdm");
+
+ // Filenames don't match.
+ assertThat(ArtManagedInstallFileHelper.filterPathsForApk(
+ List.of("/foo/bar.dm", "/foo/bar.prof", "/foo/bar.sdm", "/foo/bar.abc",
+ "/foo/baz.dm"),
+ "/foo/qux.apk"))
+ .isEmpty();
+
+ // Directories don't match.
+ assertThat(ArtManagedInstallFileHelper.filterPathsForApk(
+ List.of("/foo/bar.dm", "/foo/bar.prof", "/foo/bar.sdm", "/foo/bar.abc",
+ "/foo/baz.dm"),
+ "/quz/bar.apk"))
+ .isEmpty();
+ }
+
+ @Test
+ public void testGetTargetPathForApk() throws Exception {
+ assertThat(ArtManagedInstallFileHelper.getTargetPathForApk(
+ "/foo/bar.dm", "/somewhere/base.apk"))
+ .isEqualTo("/somewhere/base.dm");
+ assertThat(ArtManagedInstallFileHelper.getTargetPathForApk(
+ "/foo/bar.prof", "/somewhere/base.apk"))
+ .isEqualTo("/somewhere/base.prof");
+ assertThat(ArtManagedInstallFileHelper.getTargetPathForApk(
+ "/foo/bar.sdm", "/somewhere/base.apk"))
+ .isEqualTo("/somewhere/base.sdm");
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ ArtManagedInstallFileHelper.getTargetPathForApk("/foo/bar.abc", "/somewhere/base.apk");
+ });
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/UtilsTest.java b/libartservice/service/javatests/com/android/server/art/UtilsTest.java
index b3220ffdff..5dd484e021 100644
--- a/libartservice/service/javatests/com/android/server/art/UtilsTest.java
+++ b/libartservice/service/javatests/com/android/server/art/UtilsTest.java
@@ -220,4 +220,19 @@ public class UtilsTest {
assertThat(Utils.pathStartsWith("/", "/")).isTrue();
assertThat(Utils.pathStartsWith("/", "/a")).isFalse();
}
+
+ @Test
+ public void testReplaceFileExtension() {
+ assertThat(Utils.replaceFileExtension("/directory/file.apk", ".dm"))
+ .isEqualTo("/directory/file.dm");
+ assertThat(Utils.replaceFileExtension("/directory/file", ".dm"))
+ .isEqualTo("/directory/file.dm");
+ assertThat(Utils.replaceFileExtension("/.directory/file.apk", ".dm"))
+ .isEqualTo("/.directory/file.dm");
+ assertThat(Utils.replaceFileExtension("/.directory/file", ".dm"))
+ .isEqualTo("/.directory/file.dm");
+ assertThat(Utils.replaceFileExtension("/directory/file.apk", ""))
+ .isEqualTo("/directory/file");
+ assertThat(Utils.replaceFileExtension("", ".dm")).isEqualTo(".dm");
+ }
}