Use the .dm file at runtime for verification.

Bug: 112284845
Test: 674-HelloWorld-Dm
Change-Id: Icd07f86cfb2b5428186a4c086f042890eaad249b
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 983d5c9..c2557f2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1046,9 +1046,6 @@
       "name": "art-run-test-673-checker-throw-vmethod[com.google.android.art.apex]"
     },
     {
-      "name": "art-run-test-674-HelloWorld-Dm[com.google.android.art.apex]"
-    },
-    {
       "name": "art-run-test-676-proxy-jit-at-first-use[com.google.android.art.apex]"
     },
     {
@@ -2245,9 +2242,6 @@
       "name": "art-run-test-673-checker-throw-vmethod"
     },
     {
-      "name": "art-run-test-674-HelloWorld-Dm"
-    },
-    {
       "name": "art-run-test-676-proxy-jit-at-first-use"
     },
     {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index bb6dd85..0090d58 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1343,38 +1343,9 @@
 
     if (dm_file_ != nullptr) {
       DCHECK(input_vdex_file_ == nullptr);
-      std::string error_msg;
-      static const char* kDexMetadata = "DexMetadata";
-      std::unique_ptr<ZipEntry> zip_entry(dm_file_->Find(VdexFile::kVdexNameInDmFile, &error_msg));
-      if (zip_entry == nullptr) {
-        LOG(INFO) << "No " << VdexFile::kVdexNameInDmFile << " file in DexMetadata archive. "
-                  << "Not doing fast verification.";
-      } else {
-        MemMap input_file = zip_entry->MapDirectlyOrExtract(
-            VdexFile::kVdexNameInDmFile,
-            kDexMetadata,
-            &error_msg,
-            alignof(VdexFile));
-        if (!input_file.IsValid()) {
-          LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
-        } else {
-          input_vdex_file_ = std::make_unique<VdexFile>(std::move(input_file));
-          if (!input_vdex_file_->IsValid()) {
-            // Ideally we would do this validation at the framework level but the framework
-            // has not knowledge of the .vdex format and adding new APIs just for it is
-            // overkill.
-            // TODO(calin): include this in dex2oat metrics.
-            LOG(WARNING) << "The dex metadata .vdex is not valid. Ignoring it.";
-            input_vdex_file_ = nullptr;
-          } else {
-            if (input_vdex_file_->HasDexSection()) {
-              LOG(ERROR) << "The dex metadata is not allowed to contain dex files";
-              android_errorWriteLog(0x534e4554, "178055795");  // Report to SafetyNet.
-              return false;
-            }
-            VLOG(verifier) << "Doing fast verification with vdex from DexMetadata archive";
-          }
-        }
+      input_vdex_file_ = VdexFile::OpenFromDm(dm_file_location_, *dm_file_);
+      if (input_vdex_file_ != nullptr) {
+        VLOG(verifier) << "Doing fast verification with vdex from DexMetadata archive";
       }
     }
 
diff --git a/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc
index 3b980e0..1f486e6 100644
--- a/dex2oat/dex2oat_vdex_test.cc
+++ b/dex2oat/dex2oat_vdex_test.cc
@@ -233,8 +233,8 @@
   extra_args.push_back("--dm-file=" + dm_file);
 
   // Recompile again with the .dm file which contains a vdex with code.
-  // The compilation should fail.
-  ASSERT_FALSE(RunDex2oat(
+  // The compilation will pass, but dex2oat will not use the vdex file.
+  ASSERT_TRUE(RunDex2oat(
       dex_file->GetLocation(),
       GetOdex(dex_file, "v2"),
       /*public_sdk=*/ nullptr,
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 0cbc77b..9337e9f 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -519,6 +519,10 @@
   return ReplaceFileExtension(oat_location, "vdex");
 }
 
+std::string GetDmFilename(const std::string& dex_location) {
+  return ReplaceFileExtension(dex_location, "dm");
+}
+
 std::string GetSystemOdexFilenameForApex(std::string_view location, InstructionSet isa) {
   DCHECK(LocationIsOnApex(location));
   std::string dir = GetAndroidRoot() + "/framework/oat/" + GetInstructionSetString(isa);
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 33ea2e9..c3a8c9c 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -126,6 +126,9 @@
 // Returns the vdex filename for the given oat filename.
 std::string GetVdexFilename(const std::string& oat_filename);
 
+// Returns the dm filename for the given dex location.
+std::string GetDmFilename(const std::string& dex_location);
+
 // Returns the odex location on /system for a DEX file on /apex. The caller must make sure that
 // `location` is on /apex.
 std::string GetSystemOdexFilenameForApex(std::string_view location, InstructionSet isa);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 454a20f..7dd584a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4740,6 +4740,7 @@
   if (oat_file != nullptr) {
     ClassStatus vdex_status = oat_file->GetVdexFile()->ComputeClassStatus(self, klass);
     if (vdex_status >= ClassStatus::kVerifiedNeedsAccessChecks) {
+      VLOG(verifier) << "Vdex verification success for " << klass->PrettyClass();
       oat_file_class_status = vdex_status;
       return true;
     }
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index a0b0055..414b24e 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -34,6 +34,7 @@
 #include "base/string_view_cpp20.h"
 #include "base/systrace.h"
 #include "base/utils.h"
+#include "base/zip_archive.h"
 #include "class_linker.h"
 #include "class_loader_context.h"
 #include "dex/art_dex_file_loader.h"
@@ -53,6 +54,7 @@
 
 static constexpr const char* kAnonymousDexPrefix = "Anonymous-DexFile@";
 static constexpr const char* kVdexExtension = ".vdex";
+static constexpr const char* kDmExtension = ".dm";
 
 std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
   switch (status) {
@@ -107,6 +109,8 @@
       oat_(this, /*is_oat_location=*/ true),
       vdex_for_odex_(this, /*is_oat_location=*/ false),
       vdex_for_oat_(this, /*is_oat_location=*/ true),
+      dm_for_odex_(this, /*is_oat_location=*/ false),
+      dm_for_oat_(this, /*is_oat_location=*/ true),
       zip_fd_(zip_fd) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   CHECK(!load_executable || context != nullptr) << "Loading executable without a context";
@@ -141,6 +145,13 @@
                          DupCloexec(zip_fd),
                          DupCloexec(vdex_fd),
                          DupCloexec(oat_fd));
+
+    std::string dm_file_name = GetDmFilename(dex_location_);
+    dm_for_odex_.Reset(dm_file_name,
+                       UseFdToReadFiles(),
+                       DupCloexec(zip_fd),
+                       DupCloexec(vdex_fd),
+                       DupCloexec(oat_fd));
   } else {
     LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
   }
@@ -152,6 +163,8 @@
       oat_.Reset(oat_file_name, /*use_fd=*/ false);
       std::string vdex_file_name = GetVdexFilename(oat_file_name);
       vdex_for_oat_.Reset(vdex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
+      std::string dm_file_name = GetDmFilename(dex_location);
+      dm_for_oat_.Reset(dm_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
     } else {
       LOG(WARNING) << "Failed to determine oat file name for dex location "
                    << dex_location_ << ": " << error_msg;
@@ -702,8 +715,12 @@
 
     // If the odex is not useable, and we have a useable vdex, return the vdex
     // instead.
-    if (!odex_.IsUseable() && vdex_for_odex_.IsUseable()) {
-      return vdex_for_odex_;
+    if (!odex_.IsUseable()) {
+      if (vdex_for_odex_.IsUseable()) {
+        return vdex_for_odex_;
+      } else if (dm_for_odex_.IsUseable()) {
+        return dm_for_odex_;
+      }
     }
     return odex_;
   }
@@ -729,6 +746,12 @@
   if (vdex_for_odex_.IsUseable()) {
     return vdex_for_odex_;
   }
+  if (dm_for_oat_.IsUseable()) {
+    return dm_for_oat_;
+  }
+  if (dm_for_odex_.IsUseable()) {
+    return dm_for_odex_;
+  }
 
   // We got into the worst situation here:
   // - the oat location is not useable
@@ -873,6 +896,19 @@
                                         oat_file_assistant_->dex_location_,
                                         &error_msg));
     }
+  } else if (android::base::EndsWith(filename_, kDmExtension)) {
+    executable = false;
+    // Check to see if there is a vdex file we can make use of.
+    std::unique_ptr<ZipArchive> dm_file(ZipArchive::Open(filename_.c_str(), &error_msg));
+    if (dm_file != nullptr) {
+      std::unique_ptr<VdexFile> vdex(VdexFile::OpenFromDm(filename_, *dm_file));
+      if (vdex != nullptr) {
+        file_.reset(OatFile::OpenFromVdex(zip_fd_,
+                                          std::move(vdex),
+                                          oat_file_assistant_->dex_location_,
+                                          &error_msg));
+      }
+    }
   } else {
     if (executable && oat_file_assistant_->only_load_trusted_executable_) {
       executable = LocationIsTrusted(filename_, /*trust_art_apex_data_files=*/ true);
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 9cfa781..c243cc3 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -452,6 +452,10 @@
   // it is out of date).
   OatFileInfo vdex_for_oat_;
 
+  // The vdex-only file next to the apk.
+  OatFileInfo dm_for_odex_;
+  OatFileInfo dm_for_oat_;
+
   // File descriptor corresponding to apk, dex file, or zip.
   int zip_fd_;
 
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 9671679..58bb357 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -23,12 +23,14 @@
 #include <unordered_set>
 
 #include <android-base/logging.h>
+#include <log/log.h>
 
 #include "base/bit_utils.h"
 #include "base/leb128.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/unix_file/fd_file.h"
+#include "base/zip_archive.h"
 #include "class_linker.h"
 #include "class_loader_context.h"
 #include "dex/art_dex_file_loader.h"
@@ -166,6 +168,37 @@
   return vdex;
 }
 
+std::unique_ptr<VdexFile> VdexFile::OpenFromDm(const std::string& filename,
+                                               const ZipArchive& archive) {
+  std::string error_msg;
+  std::unique_ptr<ZipEntry> zip_entry(archive.Find(VdexFile::kVdexNameInDmFile, &error_msg));
+  if (zip_entry == nullptr) {
+    LOG(INFO) << "No " << VdexFile::kVdexNameInDmFile << " file in DexMetadata archive. "
+              << "Not doing fast verification.";
+    return nullptr;
+  }
+  MemMap input_file = zip_entry->MapDirectlyOrExtract(
+      filename.c_str(),
+      VdexFile::kVdexNameInDmFile,
+      &error_msg,
+      alignof(VdexFile));
+  if (!input_file.IsValid()) {
+    LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
+    return nullptr;
+  }
+  std::unique_ptr<VdexFile> vdex_file = std::make_unique<VdexFile>(std::move(input_file));
+  if (!vdex_file->IsValid()) {
+    LOG(WARNING) << "The dex metadata .vdex is not valid. Ignoring it.";
+    return nullptr;
+  }
+  if (vdex_file->HasDexSection()) {
+    LOG(ERROR) << "The dex metadata is not allowed to contain dex files";
+    android_errorWriteLog(0x534e4554, "178055795");  // Report to SafetyNet.
+    return nullptr;
+  }
+  return vdex_file;
+}
+
 const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor, uint32_t dex_file_index) const {
   DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End()));
   if (cursor == nullptr) {
@@ -506,6 +539,9 @@
 
     DCHECK(destination->IsResolved() && source->IsResolved());
     if (!destination->IsAssignableFrom(source.Get())) {
+      VLOG(verifier) << "Vdex checking failed for " << cls->PrettyClass()
+                     << ": expected " << destination->PrettyClass()
+                     << " to be assignable from " << source->PrettyClass();
       // An implicit assignability check is failing in the code, return that the
       // class is not verified.
       return ClassStatus::kResolved;
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index a66ff88..35be4fb 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -246,6 +246,9 @@
                          error_msg);
   }
 
+  static std::unique_ptr<VdexFile> OpenFromDm(const std::string& filename,
+                                              const ZipArchive& archive);
+
   const uint8_t* Begin() const { return mmap_.Begin(); }
   const uint8_t* End() const { return mmap_.End(); }
   size_t Size() const { return mmap_.Size(); }
diff --git a/test/674-HelloWorld-Dm/Android.bp b/test/674-HelloWorld-Dm/Android.bp
deleted file mode 100644
index 3c55af7..0000000
--- a/test/674-HelloWorld-Dm/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Generated by `regen-test-files`. Do not edit manually.
-
-// Build rules for ART run-test `674-HelloWorld-Dm`.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "art_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["art_license"],
-}
-
-// Test's Dex code.
-java_test {
-    name: "art-run-test-674-HelloWorld-Dm",
-    defaults: ["art-run-test-defaults"],
-    test_config_template: ":art-run-test-target-template",
-    srcs: ["src/**/*.java"],
-    data: [
-        ":art-run-test-674-HelloWorld-Dm-expected-stdout",
-        ":art-run-test-674-HelloWorld-Dm-expected-stderr",
-    ],
-}
-
-// Test's expected standard output.
-genrule {
-    name: "art-run-test-674-HelloWorld-Dm-expected-stdout",
-    out: ["art-run-test-674-HelloWorld-Dm-expected-stdout.txt"],
-    srcs: ["expected-stdout.txt"],
-    cmd: "cp -f $(in) $(out)",
-}
-
-// Test's expected standard error.
-genrule {
-    name: "art-run-test-674-HelloWorld-Dm-expected-stderr",
-    out: ["art-run-test-674-HelloWorld-Dm-expected-stderr.txt"],
-    srcs: ["expected-stderr.txt"],
-    cmd: "cp -f $(in) $(out)",
-}
diff --git a/test/674-HelloWorld-Dm/expected-stdout.txt b/test/674-HelloWorld-Dm/expected-stdout.txt
index af5626b..95e75d9 100644
--- a/test/674-HelloWorld-Dm/expected-stdout.txt
+++ b/test/674-HelloWorld-Dm/expected-stdout.txt
@@ -1 +1,2 @@
 Hello, world!
+Hello, world!
diff --git a/test/674-HelloWorld-Dm/run b/test/674-HelloWorld-Dm/run
index 199ffc3..b8a61c5 100644
--- a/test/674-HelloWorld-Dm/run
+++ b/test/674-HelloWorld-Dm/run
@@ -14,4 +14,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-exec ${RUN} --dm "${@}"
+${RUN} --dex2oat-dm "${@}"
+return_status1=$?
+
+${RUN} --runtime-dm "${@}"
+return_status2=$?
+
+(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 56cb4a8..9d7dc7e 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -101,7 +101,8 @@
 EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
 DRY_RUN="n" # if y prepare to run the test but don't run it.
 TEST_VDEX="n"
-TEST_DM="n"
+TEST_DEX2OAT_DM="n"
+TEST_RUNTIME_DM="n"
 TEST_IS_NDEBUG="n"
 APP_IMAGE="y"
 SECONDARY_APP_IMAGE="y"
@@ -479,8 +480,11 @@
     elif [ "x$1" = "x--vdex" ]; then
         TEST_VDEX="y"
         shift
-    elif [ "x$1" = "x--dm" ]; then
-        TEST_DM="y"
+    elif [ "x$1" = "x--dex2oat-dm" ]; then
+        TEST_DEX2OAT_DM="y"
+        shift
+    elif [ "x$1" = "x--runtime-dm" ]; then
+        TEST_RUNTIME_DM="y"
         shift
     elif [ "x$1" = "x--vdex-filter" ]; then
         shift
@@ -1085,10 +1089,13 @@
     else
       vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
     fi
-  elif [ "$TEST_DM" = "y" ]; then
+  elif [ "$TEST_DEX2OAT_DM" = "y" ]; then
+    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$name.dm"
     dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
     dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
-    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$name.dm"
+  elif [ "$TEST_RUNTIME_DM" = "y" ]; then
+    dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
+    dm_cmdline="zip -qj $DEX_LOCATION/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
   fi
 }
 
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index 6221980..f03d3f2 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -115,6 +115,7 @@
   "178-app-image-native-method",
   "179-nonvirtual-jni",
   "450-checker-types",
+  "674-HelloWorld-Dm",
   "1900-track-alloc",
   "1901-get-bytecodes",
   "1902-suspend",