Merge "pm: Require manifest.json for .dm (dex-metadata) files."
This commit is contained in:
@@ -22,17 +22,26 @@ import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
|
|||||||
import android.content.pm.PackageParser;
|
import android.content.pm.PackageParser;
|
||||||
import android.content.pm.PackageParser.PackageLite;
|
import android.content.pm.PackageParser.PackageLite;
|
||||||
import android.content.pm.PackageParser.PackageParserException;
|
import android.content.pm.PackageParser.PackageParserException;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.jar.StrictJarFile;
|
import android.util.jar.StrictJarFile;
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class used to compute and validate the location of dex metadata files.
|
* Helper class used to compute and validate the location of dex metadata files.
|
||||||
@@ -40,6 +49,12 @@ import java.util.Map;
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class DexMetadataHelper {
|
public class DexMetadataHelper {
|
||||||
|
public static final String TAG = "DexMetadataHelper";
|
||||||
|
/** $> adb shell 'setprop log.tag.DexMetadataHelper VERBOSE' */
|
||||||
|
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
|
/** $> adb shell 'setprop pm.dexopt.dm.require_manifest true' */
|
||||||
|
private static String PROPERTY_DM_JSON_MANIFEST_REQUIRED = "pm.dexopt.dm.require_manifest";
|
||||||
|
|
||||||
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
|
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
|
||||||
|
|
||||||
private DexMetadataHelper() {}
|
private DexMetadataHelper() {}
|
||||||
@@ -147,14 +162,31 @@ public class DexMetadataHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that the given file is a dex metadata archive.
|
* Validate that the given file is a dex metadata archive.
|
||||||
* This is just a validation that the file is a zip archive.
|
* This is just a validation that the file is a zip archive that contains a manifest.json
|
||||||
|
* with the package name and version code.
|
||||||
*
|
*
|
||||||
* @throws PackageParserException if the file is not a .dm file.
|
* @throws PackageParserException if the file is not a .dm file.
|
||||||
*/
|
*/
|
||||||
public static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
|
public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode)
|
||||||
|
throws PackageParserException {
|
||||||
|
validateDexMetadataFile(dmaPath, packageName, versionCode,
|
||||||
|
SystemProperties.getBoolean(PROPERTY_DM_JSON_MANIFEST_REQUIRED, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode,
|
||||||
|
boolean requireManifest) throws PackageParserException {
|
||||||
StrictJarFile jarFile = null;
|
StrictJarFile jarFile = null;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "validateDexMetadataFile: " + dmaPath + ", " + packageName +
|
||||||
|
", " + versionCode);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
jarFile = new StrictJarFile(dmaPath, false, false);
|
jarFile = new StrictJarFile(dmaPath, false, false);
|
||||||
|
validateDexMetadataManifest(dmaPath, jarFile, packageName, versionCode,
|
||||||
|
requireManifest);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
||||||
"Error opening " + dmaPath, e);
|
"Error opening " + dmaPath, e);
|
||||||
@@ -168,6 +200,72 @@ public class DexMetadataHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ensure that packageName and versionCode match the manifest.json in the .dm file */
|
||||||
|
private static void validateDexMetadataManifest(String dmaPath, StrictJarFile jarFile,
|
||||||
|
String packageName, long versionCode, boolean requireManifest)
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
if (!requireManifest) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "validateDexMetadataManifest: " + dmaPath
|
||||||
|
+ " manifest.json check skipped");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipEntry zipEntry = jarFile.findEntry("manifest.json");
|
||||||
|
if (zipEntry == null) {
|
||||||
|
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
||||||
|
"Missing manifest.json in " + dmaPath);
|
||||||
|
}
|
||||||
|
InputStream inputStream = jarFile.getInputStream(zipEntry);
|
||||||
|
|
||||||
|
JsonReader reader;
|
||||||
|
try {
|
||||||
|
reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
||||||
|
"Error opening manifest.json in " + dmaPath, e);
|
||||||
|
}
|
||||||
|
String jsonPackageName = null;
|
||||||
|
long jsonVersionCode = -1;
|
||||||
|
|
||||||
|
reader.beginObject();
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
String name = reader.nextName();
|
||||||
|
if (name.equals("packageName")) {
|
||||||
|
jsonPackageName = reader.nextString();
|
||||||
|
} else if (name.equals("versionCode")) {
|
||||||
|
jsonVersionCode = reader.nextLong();
|
||||||
|
} else {
|
||||||
|
reader.skipValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.endObject();
|
||||||
|
|
||||||
|
if (jsonPackageName == null || jsonVersionCode == -1) {
|
||||||
|
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
||||||
|
"manifest.json in " + dmaPath
|
||||||
|
+ " is missing 'packageName' and/or 'versionCode'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonPackageName.equals(packageName)) {
|
||||||
|
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
||||||
|
"manifest.json in " + dmaPath + " has invalid packageName: " + jsonPackageName
|
||||||
|
+ ", expected: " + packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionCode != jsonVersionCode) {
|
||||||
|
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
|
||||||
|
"manifest.json in " + dmaPath + " has invalid versionCode: " + jsonVersionCode
|
||||||
|
+ ", expected: " + versionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName +
|
||||||
|
", " + versionCode + ": successful");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that all dex metadata paths in the given list have a matching apk.
|
* Validates that all dex metadata paths in the given list have a matching apk.
|
||||||
* (for any foo.dm there should be either a 'foo' of a 'foo.apk' file).
|
* (for any foo.dm there should be either a 'foo' of a 'foo.apk' file).
|
||||||
|
|||||||
@@ -125,8 +125,10 @@ public class AndroidPackageUtils {
|
|||||||
public static void validatePackageDexMetadata(AndroidPackage pkg)
|
public static void validatePackageDexMetadata(AndroidPackage pkg)
|
||||||
throws PackageParserException {
|
throws PackageParserException {
|
||||||
Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
|
Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
|
||||||
|
String packageName = pkg.getPackageName();
|
||||||
|
long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
|
||||||
for (String dexMetadata : apkToDexMetadataList) {
|
for (String dexMetadata : apkToDexMetadataList) {
|
||||||
DexMetadataHelper.validateDexMetadataFile(dexMetadata);
|
DexMetadataHelper.validateDexMetadataFile(dexMetadata, packageName, versionCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import androidx.test.runner.AndroidJUnit4;
|
|||||||
|
|
||||||
import com.android.frameworks.servicestests.R;
|
import com.android.frameworks.servicestests.R;
|
||||||
import com.android.server.pm.parsing.TestPackageParser2;
|
import com.android.server.pm.parsing.TestPackageParser2;
|
||||||
|
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||||
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
|
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
|
||||||
import com.android.server.pm.parsing.pkg.ParsedPackage;
|
import com.android.server.pm.parsing.pkg.ParsedPackage;
|
||||||
|
|
||||||
@@ -57,6 +58,8 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
@@ -66,6 +69,9 @@ import java.util.zip.ZipOutputStream;
|
|||||||
public class DexMetadataHelperTest {
|
public class DexMetadataHelperTest {
|
||||||
private static final String APK_FILE_EXTENSION = ".apk";
|
private static final String APK_FILE_EXTENSION = ".apk";
|
||||||
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
|
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
|
||||||
|
private static final String DEX_METADATA_PACKAGE_NAME =
|
||||||
|
"com.android.frameworks.servicestests.install_split";
|
||||||
|
private static long DEX_METADATA_VERSION_CODE = 30;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
|
public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
|
||||||
@@ -78,12 +84,46 @@ public class DexMetadataHelperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private File createDexMetadataFile(String apkFileName) throws IOException {
|
private File createDexMetadataFile(String apkFileName) throws IOException {
|
||||||
|
return createDexMetadataFile(apkFileName, /*validManifest=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createDexMetadataFile(String apkFileName, boolean validManifest) throws IOException
|
||||||
|
{
|
||||||
|
return createDexMetadataFile(apkFileName,DEX_METADATA_PACKAGE_NAME,
|
||||||
|
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, validManifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createDexMetadataFile(String apkFileName, String packageName, Long versionCode,
|
||||||
|
boolean emptyManifest, boolean validManifest) throws IOException {
|
||||||
File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION,
|
File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION,
|
||||||
DEX_METADATA_FILE_EXTENSION));
|
DEX_METADATA_FILE_EXTENSION));
|
||||||
try (FileOutputStream fos = new FileOutputStream(dmFile)) {
|
try (FileOutputStream fos = new FileOutputStream(dmFile)) {
|
||||||
try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
|
try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
|
||||||
zipOs.putNextEntry(new ZipEntry("primary.prof"));
|
zipOs.putNextEntry(new ZipEntry("primary.prof"));
|
||||||
zipOs.closeEntry();
|
zipOs.closeEntry();
|
||||||
|
|
||||||
|
if (validManifest) {
|
||||||
|
zipOs.putNextEntry(new ZipEntry("manifest.json"));
|
||||||
|
if (!emptyManifest) {
|
||||||
|
String manifestStr = "{";
|
||||||
|
|
||||||
|
if (packageName != null) {
|
||||||
|
manifestStr += "\"packageName\": " + "\"" + packageName + "\"";
|
||||||
|
|
||||||
|
if (versionCode != null) {
|
||||||
|
manifestStr += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (versionCode != null) {
|
||||||
|
manifestStr += " \"versionCode\": " + versionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestStr += "}";
|
||||||
|
byte[] bytes = manifestStr.getBytes(StandardCharsets.UTF_8);
|
||||||
|
zipOs.write(bytes, /*off=*/0, /*len=*/bytes.length);
|
||||||
|
}
|
||||||
|
zipOs.closeEntry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dmFile;
|
return dmFile;
|
||||||
@@ -98,17 +138,38 @@ public class DexMetadataHelperTest {
|
|||||||
return outFile;
|
return outFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validatePackageDexMetadata(AndroidPackage pkg, boolean requireManifest)
|
||||||
|
throws PackageParserException {
|
||||||
|
Collection<String> apkToDexMetadataList =
|
||||||
|
AndroidPackageUtils.getPackageDexMetadata(pkg).values();
|
||||||
|
String packageName = pkg.getPackageName();
|
||||||
|
long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
|
||||||
|
for (String dexMetadata : apkToDexMetadataList) {
|
||||||
|
DexMetadataHelper.validateDexMetadataFile(
|
||||||
|
dexMetadata, packageName, versionCode, requireManifest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validatePackageDexMetatadataVaryingRequireManifest(ParsedPackage pkg)
|
||||||
|
throws PackageParserException {
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
|
public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
|
||||||
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
createDexMetadataFile("install_split_base.apk");
|
createDexMetadataFile("install_split_base.apk");
|
||||||
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, 0 /* flags */, false);
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
|
||||||
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
|
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
|
||||||
assertEquals(1, packageDexMetadata.size());
|
assertEquals(1, packageDexMetadata.size());
|
||||||
String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
|
String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
|
||||||
assertNotNull(baseDexMetadata);
|
assertNotNull(baseDexMetadata);
|
||||||
assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
|
assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
|
||||||
|
|
||||||
|
// Should throw no exceptions.
|
||||||
|
validatePackageDexMetatadataVaryingRequireManifest(pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -118,7 +179,7 @@ public class DexMetadataHelperTest {
|
|||||||
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
|
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
|
||||||
createDexMetadataFile("install_split_base.apk");
|
createDexMetadataFile("install_split_base.apk");
|
||||||
createDexMetadataFile("install_split_feature_a.apk");
|
createDexMetadataFile("install_split_feature_a.apk");
|
||||||
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, 0 /* flags */, false);
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
|
||||||
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
|
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
|
||||||
assertEquals(2, packageDexMetadata.size());
|
assertEquals(2, packageDexMetadata.size());
|
||||||
@@ -129,6 +190,9 @@ public class DexMetadataHelperTest {
|
|||||||
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
|
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
|
||||||
assertNotNull(splitDexMetadata);
|
assertNotNull(splitDexMetadata);
|
||||||
assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
|
assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
|
||||||
|
|
||||||
|
// Should throw no exceptions.
|
||||||
|
validatePackageDexMetatadataVaryingRequireManifest(pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -137,7 +201,7 @@ public class DexMetadataHelperTest {
|
|||||||
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
|
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
|
||||||
createDexMetadataFile("install_split_feature_a.apk");
|
createDexMetadataFile("install_split_feature_a.apk");
|
||||||
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, 0 /* flags */, false);
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
|
||||||
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
|
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
|
||||||
assertEquals(1, packageDexMetadata.size());
|
assertEquals(1, packageDexMetadata.size());
|
||||||
@@ -145,6 +209,9 @@ public class DexMetadataHelperTest {
|
|||||||
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
|
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
|
||||||
assertNotNull(splitDexMetadata);
|
assertNotNull(splitDexMetadata);
|
||||||
assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
|
assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
|
||||||
|
|
||||||
|
// Should throw no exceptions.
|
||||||
|
validatePackageDexMetatadataVaryingRequireManifest(pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -153,9 +220,17 @@ public class DexMetadataHelperTest {
|
|||||||
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
|
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
|
||||||
Files.createFile(invalidDmFile.toPath());
|
Files.createFile(invalidDmFile.toPath());
|
||||||
try {
|
try {
|
||||||
ParsedPackage pkg = new TestPackageParser2()
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
.parsePackage(mTmpDir, 0 /* flags */, false);
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
AndroidPackageUtils.validatePackageDexMetadata(pkg);
|
fail("Should fail validation: empty .dm file");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/false);
|
||||||
|
fail("Should fail validation: empty .dm file");
|
||||||
} catch (PackageParserException e) {
|
} catch (PackageParserException e) {
|
||||||
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
}
|
}
|
||||||
@@ -171,9 +246,112 @@ public class DexMetadataHelperTest {
|
|||||||
Files.createFile(invalidDmFile.toPath());
|
Files.createFile(invalidDmFile.toPath());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ParsedPackage pkg = new TestPackageParser2()
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
.parsePackage(mTmpDir, 0 /* flags */, false);
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
AndroidPackageUtils.validatePackageDexMetadata(pkg);
|
fail("Should fail validation: empty .dm file");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/false);
|
||||||
|
fail("Should fail validation: empty .dm file");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsePackageWithDmFileInvalidManifest()
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
|
createDexMetadataFile("install_split_base.apk", /*validManifest=*/false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
fail("Should fail validation: missing manifest.json in the .dm archive");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsePackageWithDmFileEmptyManifest()
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
|
createDexMetadataFile("install_split_base.apk", /*packageName=*/"doesn't matter",
|
||||||
|
/*versionCode=*/-12345L, /*emptyManifest=*/true, /*validManifest=*/true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
fail("Should fail validation: empty manifest.json in the .dm archive");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsePackageWithDmFileBadPackageName()
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
|
createDexMetadataFile("install_split_base.apk", /*packageName=*/"bad package name",
|
||||||
|
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
fail("Should fail validation: bad package name in the .dm archive");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsePackageWithDmFileBadVersionCode()
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
|
createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
|
||||||
|
/*versionCode=*/12345L, /*emptyManifest=*/false, /*validManifest=*/true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
fail("Should fail validation: bad version code in the .dm archive");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsePackageWithDmFileMissingPackageName()
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
|
createDexMetadataFile("install_split_base.apk", /*packageName=*/null,
|
||||||
|
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
fail("Should fail validation: missing package name in the .dm archive");
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsePackageWithDmFileMissingVersionCode()
|
||||||
|
throws IOException, PackageParserException {
|
||||||
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
|
createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
|
||||||
|
/*versionCode=*/null, /*emptyManifest=*/false, /*validManifest=*/true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
|
||||||
|
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
|
||||||
|
fail("Should fail validation: missing version code in the .dm archive");
|
||||||
} catch (PackageParserException e) {
|
} catch (PackageParserException e) {
|
||||||
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
|
||||||
}
|
}
|
||||||
@@ -186,7 +364,7 @@ public class DexMetadataHelperTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
DexMetadataHelper.validateDexPaths(mTmpDir.list());
|
DexMetadataHelper.validateDexPaths(mTmpDir.list());
|
||||||
fail("Should fail validation");
|
fail("Should fail validation: split .dm filename unmatched against .apk");
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// expected.
|
// expected.
|
||||||
}
|
}
|
||||||
@@ -202,7 +380,7 @@ public class DexMetadataHelperTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
DexMetadataHelper.validateDexPaths(mTmpDir.list());
|
DexMetadataHelper.validateDexPaths(mTmpDir.list());
|
||||||
fail("Should fail validation");
|
fail("Should fail validation: .dm filename has no match against .apk");
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// expected.
|
// expected.
|
||||||
}
|
}
|
||||||
@@ -214,7 +392,7 @@ public class DexMetadataHelperTest {
|
|||||||
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
|
||||||
File dm = createDexMetadataFile("install_split_base.apk");
|
File dm = createDexMetadataFile("install_split_base.apk");
|
||||||
ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
|
ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
|
||||||
ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, 0 /* flags */);
|
ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, /*flags=*/0);
|
||||||
if (result.isError()) {
|
if (result.isError()) {
|
||||||
throw new IllegalStateException(result.getErrorMessage(), result.getException());
|
throw new IllegalStateException(result.getErrorMessage(), result.getException());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user