Run dex2oat if the APK is compressed.
This CL updates OatFileAssistant. After this change, if the APK is
compressed and the current oat file doesn't contain dex code,
OatFileAssistant::GetDexOptNeeded will return true.
Bug: 267143543
Test: atest art_standalone_runtime_tests:OatFileAssistantTest
Test: -
1. Install a compressed APK and a DM file that only contains a VDEX
file.
2. See dex2oat invoked.
Test: -
1. Install an uncompressed APK and a DM file that only contains a
VDEX file.
2. See dex2oat not invoked.
Change-Id: If057858a614e493e00d58463af20e723f98e68b5
Merged-In: If057858a614e493e00d58463af20e723f98e68b5
(partially picked from commit c98ed72511453b8efc25e6d1047a04c1d8db374b)
Change-Id: I21e0a63a5fae56ba373aee9e6179a991b3e7b2ee
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 2f7d2f1..d04a8f6 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -492,6 +492,7 @@
":art-gtest-jars-ManyMethods",
":art-gtest-jars-MultiDex",
":art-gtest-jars-MultiDexModifiedSecondary",
+ ":art-gtest-jars-MultiDexUncompressedAligned",
":art-gtest-jars-MyClassNatives",
":art-gtest-jars-Nested",
":art-gtest-jars-ProfileTestMultiDex",
@@ -625,6 +626,7 @@
":art-gtest-jars-MainStripped",
":art-gtest-jars-MultiDex",
":art-gtest-jars-MultiDexModifiedSecondary",
+ ":art-gtest-jars-MultiDexUncompressedAligned",
":art-gtest-jars-Nested",
":generate-boot-image",
],
diff --git a/dex2oat/art_standalone_dex2oat_cts_tests.xml b/dex2oat/art_standalone_dex2oat_cts_tests.xml
index 980b295..cf20994 100644
--- a/dex2oat/art_standalone_dex2oat_cts_tests.xml
+++ b/dex2oat/art_standalone_dex2oat_cts_tests.xml
@@ -32,6 +32,7 @@
<option name="push" value="art-gtest-jars-MainStripped.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MainStripped.jar" />
<option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDex.jar" />
<option name="push" value="art-gtest-jars-MultiDexModifiedSecondary.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDexModifiedSecondary.jar" />
+ <option name="push" value="art-gtest-jars-MultiDexUncompressedAligned.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDexUncompressedAligned.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-Nested.jar" />
</target_preparer>
diff --git a/dex2oat/art_standalone_dex2oat_tests.xml b/dex2oat/art_standalone_dex2oat_tests.xml
index 1cb5fef..3a8c7b9 100644
--- a/dex2oat/art_standalone_dex2oat_tests.xml
+++ b/dex2oat/art_standalone_dex2oat_tests.xml
@@ -41,6 +41,7 @@
<option name="push" value="art-gtest-jars-ManyMethods.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ManyMethods.jar" />
<option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDex.jar" />
<option name="push" value="art-gtest-jars-MultiDexModifiedSecondary.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDexModifiedSecondary.jar" />
+ <option name="push" value="art-gtest-jars-MultiDexUncompressedAligned.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDexUncompressedAligned.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MyClassNatives.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Nested.jar" />
<option name="push" value="art-gtest-jars-ProfileTestMultiDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ProfileTestMultiDex.jar" />
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 3bfc792..deda75a 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -177,6 +177,10 @@
return GetTestDexFileName("MultiDex");
}
+ std::string GetMultiDexUncompressedAlignedSrc1() const {
+ return GetTestDexFileName("MultiDexUncompressedAligned");
+ }
+
// Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
// with the contents of the secondary dex file changed.
std::string GetMultiDexSrc2() const {
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 74f5671..0312bad 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -297,7 +297,8 @@
}
// This is the usual case. The caller's intention is to see if a better oat file can be generated.
- DexOptTrigger dexopt_trigger{.targetFilterIsBetter = true, .primaryBootImageBecomesUsable = true};
+ DexOptTrigger dexopt_trigger{
+ .targetFilterIsBetter = true, .primaryBootImageBecomesUsable = true, .needExtraction = true};
if (profile_changed && CompilerFilter::DependsOnProfile(target_compiler_filter)) {
// Since the profile has been changed, we should re-compile even if the compilation does not
// make the compiler filter better.
@@ -543,19 +544,13 @@
VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
}
+ // The constraint is only enforced if the zip has uncompressed dex code.
if (only_load_trusted_executable_ &&
- !LocationIsTrusted(file.GetLocation(),
- !GetRuntimeOptions().deny_art_apex_data_files) &&
- file.ContainsDexCode()) {
- // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
- GetRequiredDexChecksums(&error_msg);
- // The constraint is only enforced if the zip has uncompressed dex code.
- if (zip_file_only_contains_uncompressed_dex_) {
- LOG(ERROR) << "Not loading "
- << dex_location_
- << ": oat file has dex code, but APK has uncompressed dex code";
- return kOatDexOutOfDate;
- }
+ !LocationIsTrusted(file.GetLocation(), !GetRuntimeOptions().deny_art_apex_data_files) &&
+ file.ContainsDexCode() && ZipFileOnlyContainsUncompressedDex()) {
+ LOG(ERROR) << "Not loading " << dex_location_
+ << ": oat file has dex code, but APK has uncompressed dex code";
+ return kOatDexOutOfDate;
}
if (!ClassLoaderContextIsOkay(file)) {
@@ -1173,6 +1168,11 @@
}
}
+ if (dexopt_trigger.needExtraction && !file->ContainsDexCode() &&
+ !oat_file_assistant_->ZipFileOnlyContainsUncompressedDex()) {
+ return true;
+ }
+
return false;
}
@@ -1327,4 +1327,13 @@
UNREACHABLE();
}
+bool OatFileAssistant::ZipFileOnlyContainsUncompressedDex() {
+ // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
+ std::string error_msg;
+ if (GetRequiredDexChecksums(&error_msg) == nullptr) {
+ LOG(ERROR) << error_msg;
+ }
+ return zip_file_only_contains_uncompressed_dex_;
+}
+
} // namespace art
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 7001650..81cd231 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -106,6 +106,9 @@
// Dexopt should be performed if the current oat file was compiled without a primary image,
// and the runtime is now running with a primary image loaded from disk.
bool primaryBootImageBecomesUsable : 1;
+ // Dexopt should be performed if the APK is compressed and the current oat/vdex file doesn't
+ // contain dex code.
+ bool needExtraction : 1;
};
// Represents the location of the current oat file and/or vdex file.
@@ -516,6 +519,9 @@
return GetOatFileAssistantContext()->GetRuntimeOptions();
}
+ // Returns whether the zip file only contains uncompressed dex.
+ bool ZipFileOnlyContainsUncompressedDex();
+
std::string dex_location_;
// The class loader context to check against, or null representing that the check should be
@@ -534,8 +540,9 @@
// Whether only oat files from trusted locations are loaded executable.
const bool only_load_trusted_executable_ = false;
- // Whether the potential zip file only contains uncompressed dex.
- // Will be set during GetRequiredDexChecksums.
+
+ // Cached value of whether the potential zip file only contains uncompressed dex.
+ // This should be accessed only by the ZipFileOnlyContainsUncompressedDex() method.
bool zip_file_only_contains_uncompressed_dex_ = true;
// Cached value of the required dex checksums.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index ef60ea0..45f7631 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -230,8 +230,8 @@
std::unique_ptr<ClassLoaderContext> default_context_ = InitializeDefaultContext();
bool with_runtime_;
- const OatFileAssistant::DexOptTrigger default_trigger_{.targetFilterIsBetter = true,
- .primaryBootImageBecomesUsable = true};
+ const OatFileAssistant::DexOptTrigger default_trigger_{
+ .targetFilterIsBetter = true, .primaryBootImageBecomesUsable = true, .needExtraction = true};
std::unique_ptr<OatFileAssistantContext> ofa_context_;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
};
@@ -810,8 +810,8 @@
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
-// Case: We have a DEX file and up-to-date VDEX file for it, but no
-// ODEX file.
+// Case: We have a DEX file and up-to-date VDEX file for it, but no ODEX file, and the DEX file is
+// compressed.
TEST_P(OatFileAssistantTest, VdexUpToDateNoOdex) {
std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar";
std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat";
@@ -2102,18 +2102,18 @@
/*expected_location=*/OatFileAssistant::kLocationNoneOrError);
}
-// Case: We have a DEX file and a DM file for it.
+// Case: We have a DEX file and a DM file for it, and the DEX file is uncompressed.
// Expect: Dexopt should be performed if the compiler filter is better than "verify". The location
// should be kLocationDm.
//
// The legacy version should return kDex2OatFromScratch if the target compiler filter is better than
// "verify".
-TEST_P(OatFileAssistantTest, DmUpToDate) {
+TEST_P(OatFileAssistantTest, DmUpToDateDexUncompressed) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
- Copy(GetDexSrc1(), dex_location);
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Generate temporary ODEX and VDEX files in order to create the DM file from.
GenerateOdexForTest(
@@ -2157,6 +2157,58 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
}
+// Case: We have a DEX file and a DM file for it, and the DEX file is compressed.
+// Expect: Dexopt should be performed regardless of the compiler filter. The location
+// should be kLocationDm.
+TEST_P(OatFileAssistantTest, DmUpToDateDexCompressed) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ // Generate temporary ODEX and VDEX files in order to create the DM file from.
+ GenerateOdexForTest(
+ dex_location, odex_location, CompilerFilter::kVerify, "install", {"--copy-dex-files=false"});
+
+ CreateDexMetadata(vdex_location, dm_location);
+
+ // Cleanup the temporary files.
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+ ASSERT_EQ(0, unlink(vdex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
// Case: We have an ODEX file, but the DEX file is gone.
// Expect: No dexopt is needed, as there's nothing we can do.
TEST_P(OatFileAssistantTest, OdexNoDex) {