ART: Native support for multidex

Native support for zip files with multiple classesX.dex.

Works by explicitly looking for those files in ascending order. As
these files have no file system representation for themselves,
introduce synthetic dex locations: the name of the originating file
plus a colon plus the name of the dex file, e.g., test.jar:classes2.dex.

Opening a zip dex file will return all dex files in this way. This
keeps the changes to dex2oat minimal.

To hide multidex/synthetic names from the Java layer, let the handle
of dalvik.system.DexFile refer to a vector of DexFile objects. When
opening a location, test possible synthetic names and add them to the
vector. Thus, the original multidex jar in the classpath will be
associated with all embedded dex files.

Change-Id: I0de107e1369cbc94416c544aca3b17525c9eac8b
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 10f34d9..d368e41 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -87,7 +87,21 @@
 bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
   CHECK(checksum != NULL);
   uint32_t magic;
-  ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg));
+
+  // Strip ":...", which is the location
+  const char* zip_entry_name = kClassesDex;
+  const char* file_part = filename;
+  std::unique_ptr<const char> file_part_ptr;
+
+
+  if (IsMultiDexLocation(filename)) {
+    std::pair<const char*, const char*> pair = SplitMultiDexLocation(filename);
+    file_part_ptr.reset(pair.first);
+    file_part = pair.first;
+    zip_entry_name = pair.second;
+  }
+
+  ScopedFd fd(OpenAndReadMagic(file_part, &magic, error_msg));
   if (fd.get() == -1) {
     DCHECK(!error_msg->empty());
     return false;
@@ -95,13 +109,13 @@
   if (IsZipMagic(magic)) {
     std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd.release(), filename, error_msg));
     if (zip_archive.get() == NULL) {
-      *error_msg = StringPrintf("Failed to open zip archive '%s'", filename);
+      *error_msg = StringPrintf("Failed to open zip archive '%s'", file_part);
       return false;
     }
-    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex, error_msg));
+    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg));
     if (zip_entry.get() == NULL) {
-      *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
-                                kClassesDex, error_msg->c_str());
+      *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part,
+                                zip_entry_name, error_msg->c_str());
       return false;
     }
     *checksum = zip_entry->GetCrc32();
@@ -119,20 +133,26 @@
   return false;
 }
 
-const DexFile* DexFile::Open(const char* filename,
-                             const char* location,
-                             std::string* error_msg) {
+bool DexFile::Open(const char* filename, const char* location, std::string* error_msg,
+                   std::vector<const DexFile*>* dex_files) {
   uint32_t magic;
   ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg));
   if (fd.get() == -1) {
     DCHECK(!error_msg->empty());
-    return NULL;
+    return false;
   }
   if (IsZipMagic(magic)) {
-    return DexFile::OpenZip(fd.release(), location, error_msg);
+    return DexFile::OpenZip(fd.release(), location, error_msg, dex_files);
   }
   if (IsDexMagic(magic)) {
-    return DexFile::OpenFile(fd.release(), location, true, error_msg);
+    std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true,
+                                                              error_msg));
+    if (dex_file.get() != nullptr) {
+      dex_files->push_back(dex_file.release());
+      return true;
+    } else {
+      return false;
+    }
   }
   *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
   return nullptr;
@@ -217,13 +237,14 @@
 
 const char* DexFile::kClassesDex = "classes.dex";
 
-const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg) {
+bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg,
+                      std::vector<const  DexFile*>* dex_files) {
   std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
   if (zip_archive.get() == nullptr) {
     DCHECK(!error_msg->empty());
-    return nullptr;
+    return false;
   }
-  return DexFile::Open(*zip_archive, location, error_msg);
+  return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files);
 }
 
 const DexFile* DexFile::OpenMemory(const std::string& location,
@@ -238,17 +259,20 @@
                     error_msg);
 }
 
-const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location,
-                             std::string* error_msg) {
+const DexFile* DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
+                             const std::string& location, std::string* error_msg,
+                             ZipOpenErrorCode* error_code) {
   CHECK(!location.empty());
-  std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(kClassesDex, error_msg));
+  std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
   if (zip_entry.get() == NULL) {
+    *error_code = ZipOpenErrorCode::kEntryNotFound;
     return nullptr;
   }
-  std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), kClassesDex, error_msg));
+  std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
   if (map.get() == NULL) {
-    *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", kClassesDex, location.c_str(),
+    *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
                               error_msg->c_str());
+    *error_code = ZipOpenErrorCode::kExtractToMemoryError;
     return nullptr;
   }
   std::unique_ptr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(),
@@ -256,20 +280,63 @@
   if (dex_file.get() == nullptr) {
     *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
                               error_msg->c_str());
+    *error_code = ZipOpenErrorCode::kDexFileError;
     return nullptr;
   }
   if (!dex_file->DisableWrite()) {
     *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
+    *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
     return nullptr;
   }
   CHECK(dex_file->IsReadOnly()) << location;
   if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
                                location.c_str(), error_msg)) {
+    *error_code = ZipOpenErrorCode::kVerifyError;
     return nullptr;
   }
+  *error_code = ZipOpenErrorCode::kNoError;
   return dex_file.release();
 }
 
+bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
+                          std::string* error_msg, std::vector<const DexFile*>* dex_files) {
+  ZipOpenErrorCode error_code;
+  std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg,
+                                               &error_code));
+  if (dex_file.get() == nullptr) {
+    return false;
+  } else {
+    // Had at least classes.dex.
+    dex_files->push_back(dex_file.release());
+
+    // Now try some more.
+    size_t i = 2;
+
+    // We could try to avoid std::string allocations by working on a char array directly. As we
+    // do not expect a lot of iterations, this seems too involved and brittle.
+
+    while (i < 100) {
+      std::string name = StringPrintf("classes%zu.dex", i);
+      std::string fake_location = location + ":" + name;
+      std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location,
+                                                        error_msg, &error_code));
+      if (next_dex_file.get() == nullptr) {
+        if (error_code != ZipOpenErrorCode::kEntryNotFound) {
+          LOG(WARNING) << error_msg;
+        }
+        break;
+      } else {
+        dex_files->push_back(next_dex_file.release());
+      }
+
+      i++;
+    }
+
+    return true;
+  }
+}
+
+
 const DexFile* DexFile::OpenMemory(const byte* base,
                                    size_t size,
                                    const std::string& location,
@@ -865,6 +932,25 @@
   }
 }
 
+bool DexFile::IsMultiDexLocation(const char* location) {
+  return strrchr(location, kMultiDexSeparator) != nullptr;
+}
+
+std::pair<const char*, const char*> DexFile::SplitMultiDexLocation(
+    const char* location) {
+  const char* colon_ptr = strrchr(location, kMultiDexSeparator);
+
+  // Check it's synthetic.
+  CHECK_NE(colon_ptr, static_cast<const char*>(nullptr));
+
+  size_t colon_index = colon_ptr - location;
+  char* tmp = new char[colon_index + 1];
+  strncpy(tmp, location, colon_index);
+  tmp[colon_index] = 0;
+
+  return std::make_pair(tmp, colon_ptr + 1);
+}
+
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
   os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]",
                      dex_file.GetLocation().c_str(),