dex2oat: Accept .dm files.

And locate primary.vdex in it to do fast verification.

bug: 63920015
Test: 674-HelloWorld-Dm
Change-Id: If920a6c7e4856a047a24bd30b049ef1ee16d7c1f
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 34ba4b3..f21a5a8 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -605,6 +605,7 @@
       input_vdex_fd_(-1),
       output_vdex_fd_(-1),
       input_vdex_file_(nullptr),
+      dm_fd_(-1),
       zip_fd_(-1),
       image_base_(0U),
       image_classes_zip_filename_(nullptr),
@@ -757,6 +758,11 @@
       Usage("--oat-fd should not be used with --image");
     }
 
+    if ((input_vdex_fd_ != -1 || !input_vdex_.empty()) &&
+        (dm_fd_ != -1 || !dm_file_location_.empty())) {
+      Usage("An input vdex should not be passed with a .dm file");
+    }
+
     if (!parser_options->oat_symbols.empty() &&
         parser_options->oat_symbols.size() != oat_filenames_.size()) {
       Usage("--oat-file arguments do not match --oat-symbols arguments");
@@ -1176,6 +1182,8 @@
     AssignIfExists(args, M::OutputVdexFd, &output_vdex_fd_);
     AssignIfExists(args, M::InputVdex, &input_vdex_);
     AssignIfExists(args, M::OutputVdex, &output_vdex_);
+    AssignIfExists(args, M::DmFd, &dm_fd_);
+    AssignIfExists(args, M::DmFile, &dm_file_location_);
     AssignIfExists(args, M::OatFd, &oat_fd_);
     AssignIfExists(args, M::OatLocation, &oat_location_);
     AssignIfExists(args, M::Watchdog, &parser_options->watch_dog_enabled);
@@ -1389,6 +1397,42 @@
       }
     }
 
+    if (dm_fd_ != -1 || !dm_file_location_.empty()) {
+      std::string error_msg;
+      if (dm_fd_ != -1) {
+        dm_file_.reset(ZipArchive::OpenFromFd(dm_fd_, "DexMetadata", &error_msg));
+      } else {
+        dm_file_.reset(ZipArchive::Open(dm_file_location_.c_str(), &error_msg));
+      }
+      if (dm_file_ == nullptr) {
+        LOG(WARNING) << "Could not open DexMetadata archive " << error_msg;
+      }
+    }
+
+    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 {
+        std::unique_ptr<MemMap> input_file;
+        if (zip_entry->IsUncompressed()) {
+          input_file.reset(zip_entry->MapDirectlyFromFile(VdexFile::kVdexNameInDmFile, &error_msg));
+        } else {
+          input_file.reset(zip_entry->ExtractToMemMap(
+              kDexMetadata, VdexFile::kVdexNameInDmFile, &error_msg));
+        }
+        if (input_file == nullptr) {
+          LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
+        } else {
+          input_vdex_file_ = std::make_unique<VdexFile>(input_file.release());
+        }
+      }
+    }
+
     // Swap file handling
     //
     // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file
@@ -2238,7 +2282,7 @@
   }
 
   bool DoEagerUnquickeningOfVdex() const {
-    return MayInvalidateVdexMetadata();
+    return MayInvalidateVdexMetadata() && dm_file_ == nullptr;
   }
 
   bool LoadProfile() {
@@ -2788,6 +2832,9 @@
   std::string input_vdex_;
   std::string output_vdex_;
   std::unique_ptr<VdexFile> input_vdex_file_;
+  int dm_fd_;
+  std::string dm_file_location_;
+  std::unique_ptr<ZipArchive> dm_file_;
   std::vector<const char*> dex_filenames_;
   std::vector<const char*> dex_locations_;
   int zip_fd_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index a2e2b48..0eecc84 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -86,6 +86,12 @@
       .Define("--output-vdex=_")
           .WithType<std::string>()
           .IntoKey(M::OutputVdex)
+      .Define("--dm-fd=_")
+          .WithType<int>()
+          .IntoKey(M::DmFd)
+      .Define("--dm-file=_")
+          .WithType<std::string>()
+          .IntoKey(M::DmFile)
       .Define("--oat-file=_")
           .WithType<std::vector<std::string>>().AppendValues()
           .IntoKey(M::OatFiles)
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 9362a3d..9a8bdf4 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -43,6 +43,8 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    InputVdex)
 DEX2OAT_OPTIONS_KEY (int,                            OutputVdexFd)
 DEX2OAT_OPTIONS_KEY (std::string,                    OutputVdex)
+DEX2OAT_OPTIONS_KEY (int,                            DmFd)
+DEX2OAT_OPTIONS_KEY (std::string,                    DmFile)
 DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       OatFiles)
 DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       OatSymbols)
 DEX2OAT_OPTIONS_KEY (int,                            OatFd)
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 4e45128..36fe09c 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -97,6 +97,9 @@
     friend class VdexFile;
   };
 
+  // Note: The file is called "primary" to match the naming with profiles.
+  static const constexpr char* kVdexNameInDmFile = "primary.vdex";
+
   typedef uint32_t VdexChecksum;
   using QuickeningTableOffsetType = uint32_t;
 
diff --git a/test/674-HelloWorld-Dm/expected.txt b/test/674-HelloWorld-Dm/expected.txt
new file mode 100644
index 0000000..af5626b
--- /dev/null
+++ b/test/674-HelloWorld-Dm/expected.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/674-HelloWorld-Dm/info.txt b/test/674-HelloWorld-Dm/info.txt
new file mode 100644
index 0000000..3a769c4
--- /dev/null
+++ b/test/674-HelloWorld-Dm/info.txt
@@ -0,0 +1 @@
+Hello World test with --dm-file passed to dex2oat.
diff --git a/test/674-HelloWorld-Dm/run b/test/674-HelloWorld-Dm/run
new file mode 100644
index 0000000..199ffc3
--- /dev/null
+++ b/test/674-HelloWorld-Dm/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+exec ${RUN} --dm "${@}"
diff --git a/test/674-HelloWorld-Dm/src/Main.java b/test/674-HelloWorld-Dm/src/Main.java
new file mode 100644
index 0000000..1ef6289
--- /dev/null
+++ b/test/674-HelloWorld-Dm/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+  }
+}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5e40b86..fd40ed7 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -64,6 +64,7 @@
 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_IS_NDEBUG="n"
 APP_IMAGE="y"
 JVMTI_STRESS="n"
@@ -346,6 +347,9 @@
     elif [ "x$1" = "x--vdex" ]; then
         TEST_VDEX="y"
         shift
+    elif [ "x$1" = "x--dm" ]; then
+        TEST_DM="y"
+        shift
     elif [ "x$1" = "x--vdex-filter" ]; then
         shift
         option="$1"
@@ -672,6 +676,7 @@
 profman_cmdline="true"
 dex2oat_cmdline="true"
 vdex_cmdline="true"
+dm_cmdline="true"
 mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
 sync_cmdline="true"
@@ -735,6 +740,10 @@
     vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
   elif [ "$TEST_VDEX" = "y" ]; then
     vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+  elif [ "$TEST_DM" = "y" ]; then
+    dex2oat_cmdline="${dex2oat_cmdline} --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
+    dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$TEST_NAME.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
+    vdex_cmdline="${dex2oat_cmdline} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm"
   elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
     vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
   fi
@@ -782,6 +791,7 @@
 # Remove whitespace.
 dex2oat_cmdline=$(echo $dex2oat_cmdline)
 dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
+dm_cmdline=$(echo $dm_cmdline)
 vdex_cmdline=$(echo $vdex_cmdline)
 profman_cmdline=$(echo $profman_cmdline)
 
@@ -851,6 +861,7 @@
              export PATH=$ANDROID_ROOT/bin:$PATH && \
              $profman_cmdline && \
              $dex2oat_cmdline && \
+             $dm_cmdline && \
              $vdex_cmdline && \
              $strip_cmdline && \
              $sync_cmdline && \
@@ -927,7 +938,7 @@
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
-      echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
+      echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
     fi
 
     cd $ANDROID_BUILD_TOP
@@ -943,6 +954,7 @@
     mkdir -p ${mkdir_locations} || exit 1
     $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
     $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+    $dm_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
     $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
diff --git a/test/run-test b/test/run-test
index 75fe15c..bca5408 100755
--- a/test/run-test
+++ b/test/run-test
@@ -421,6 +421,9 @@
     elif [ "x$1" = "x--vdex" ]; then
         run_args="${run_args} --vdex"
         shift
+    elif [ "x$1" = "x--dm" ]; then
+        run_args="${run_args} --dm"
+        shift
     elif [ "x$1" = "x--vdex-filter" ]; then
         shift
         filter=$1