ART: Store classpath information into oat file

Store a "dependency list" of class-path dex-files into the key-value
store of an oat file. The list is made up of dex locations and
corresponding checksums.

Add tests for encoding, decoding and checking the list.

Bug: 19781184
Change-Id: Ie700dd37e6e086db599c95d329ac1f1d2ff0b758
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 81703b1..d3c4b49 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <cstdlib>
 #include <sstream>
 
 #include "base/bit_vector.h"
@@ -592,4 +593,90 @@
   // TODO: Check against oat_patches. b/18144996
 }
 
+static constexpr char kDexClassPathEncodingSeparator = '*';
+
+std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
+  std::ostringstream out;
+
+  for (const DexFile* dex_file : dex_files) {
+    out << dex_file->GetLocation().c_str();
+    out << kDexClassPathEncodingSeparator;
+    out << dex_file->GetLocationChecksum();
+    out << kDexClassPathEncodingSeparator;
+  }
+
+  return out.str();
+}
+
+bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) {
+  if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
+    // No dependencies.
+    return true;
+  }
+
+  // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
+  //             Split() instead of manual parsing of the combined char*.
+  std::vector<std::string> split;
+  Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
+  if (split.size() % 2 != 0) {
+    // Expected pairs of location and checksum.
+    *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies);
+    return false;
+  }
+
+  for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
+    std::string& location = *it;
+    std::string& checksum = *(it + 1);
+    int64_t converted = strtoll(checksum.c_str(), nullptr, 10);
+    if (converted == 0) {
+      // Conversion error.
+      *msg = StringPrintf("Conversion error for %s", checksum.c_str());
+      return false;
+    }
+
+    uint32_t dex_checksum;
+    std::string error_msg;
+    if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(),
+                             &dex_checksum,
+                             &error_msg)) {
+      if (converted != dex_checksum) {
+        *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u",
+                            location.c_str(), converted, dex_checksum);
+        return false;
+      }
+    } else {
+      // Problem retrieving checksum.
+      // TODO: odex files?
+      *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(),
+                          error_msg.c_str());
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies,
+                                              std::vector<std::string>* locations) {
+  DCHECK(locations != nullptr);
+  if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
+    return true;
+  }
+
+  // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
+  //             Split() instead of manual parsing of the combined char*.
+  std::vector<std::string> split;
+  Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
+  if (split.size() % 2 != 0) {
+    // Expected pairs of location and checksum.
+    return false;
+  }
+
+  for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
+    locations->push_back(*it);
+  }
+
+  return true;
+}
+
 }  // namespace art