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) {