Rewrite libdexfile C API to follow NDK guidelines better.

Bug: 123517037
Bug: 145347927
Test: test-art-host-gtest-art_libdexfile_support_tests64
Test: test-art-host-gtest-art_libdexfile_external_tests64
Change-Id: I9673872585456937d2f54abb63e3dbd8aad4e9ad
diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc
index 302e188..7c0bf2d 100644
--- a/libdexfile/external/dex_file_ext.cc
+++ b/libdexfile/external/dex_file_ext.cc
@@ -43,37 +43,36 @@
 
 extern "C" {
 
-// Wraps DexFile to add the caching needed by the external interface. This is
-// what gets passed over as ExtDexFile*.
-struct ExtDexFile {
-  struct MethodCacheEntry {
-    uint32_t offset;  // Offset relative to the start of the dex file header.
-    uint32_t len;
-    uint32_t index;  // Method index.
-  };
+struct ADexFile_Method {
+  ADexFile* adex;
+  uint32_t index;
+  size_t offset;
+  size_t size;
+};
 
- public:
-  std::unique_ptr<const art::DexFile> dex_file_;
-  explicit ExtDexFile(std::unique_ptr<const art::DexFile>&& dex_file)
+// Opaque implementation of ADexFile for the C interface.
+struct ADexFile {
+  explicit ADexFile(std::unique_ptr<const art::DexFile> dex_file)
       : dex_file_(std::move(dex_file)) {}
 
-  bool GetMethodDefIndex(uint32_t dex_offset, uint32_t* index, uint32_t* addr, uint32_t* size) {
+  inline bool FindMethod(uint32_t dex_offset, /*out*/ ADexFile_Method* result) {
     uint32_t class_def_index;
     if (GetClassDefIndex(dex_offset, &class_def_index)) {
       art::ClassAccessor accessor(*dex_file_, class_def_index);
-
       for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
         art::CodeItemInstructionAccessor code = method.GetInstructions();
         if (!code.HasCodeItem()) {
           continue;
         }
-
-        uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
-        uint32_t len = code.InsnsSizeInBytes();
-        if (offset <= dex_offset && dex_offset < offset + len) {
-          *index = method.GetIndex();
-          *addr = offset;
-          *size = len;
+        size_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+        size_t size = code.InsnsSizeInBytes();
+        if (offset <= dex_offset && dex_offset < offset + size) {
+          *result = ADexFile_Method {
+            .adex = this,
+            .index = method.GetIndex(),
+            .offset = offset,
+            .size = size,
+          };
           return true;
         }
       }
@@ -81,40 +80,43 @@
     return false;
   }
 
- private:
-  bool GetClassDefIndex(uint32_t dex_offset, uint32_t* class_def_index) {
+  void CreateClassCache() {
+    // Create binary search table with (end_dex_offset, class_def_index) entries.
+    // That is, we don't assume that dex code of given class is consecutive.
+    std::deque<std::pair<uint32_t, uint32_t>> cache;
+    for (art::ClassAccessor accessor : dex_file_->GetClasses()) {
+      for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
+        art::CodeItemInstructionAccessor code = method.GetInstructions();
+        if (code.HasCodeItem()) {
+          int32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+          DCHECK_NE(offset, 0);
+          cache.emplace_back(offset + code.InsnsSizeInBytes(), accessor.GetClassDefIndex());
+        }
+      }
+    }
+    std::sort(cache.begin(), cache.end());
+
+    // If two consecutive methods belong to same class, we can merge them.
+    // This tends to reduce the number of entries (used memory) by 10x.
+    size_t num_entries = cache.size();
+    if (cache.size() > 1) {
+      for (auto it = std::next(cache.begin()); it != cache.end(); it++) {
+        if (std::prev(it)->second == it->second) {
+          std::prev(it)->first = 0;  // Clear entry with lower end_dex_offset (mark to remove).
+          num_entries--;
+        }
+      }
+    }
+
+    // The cache is immutable now. Store it as continuous vector to save space.
+    class_cache_.reserve(num_entries);
+    auto pred = [](auto it) { return it.first != 0; };  // Entries to copy (not cleared above).
+    std::copy_if(cache.begin(), cache.end(), std::back_inserter(class_cache_), pred);
+  }
+
+  inline bool GetClassDefIndex(uint32_t dex_offset, uint32_t* class_def_index) {
     if (class_cache_.empty()) {
-      // Create binary search table with (end_dex_offset, class_def_index) entries.
-      // That is, we don't assume that dex code of given class is consecutive.
-      std::deque<std::pair<uint32_t, uint32_t>> cache;
-      for (art::ClassAccessor accessor : dex_file_->GetClasses()) {
-        for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
-          art::CodeItemInstructionAccessor code = method.GetInstructions();
-          if (code.HasCodeItem()) {
-            int32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
-            DCHECK_NE(offset, 0);
-            cache.emplace_back(offset + code.InsnsSizeInBytes(), accessor.GetClassDefIndex());
-          }
-        }
-      }
-      std::sort(cache.begin(), cache.end());
-
-      // If two consecutive methods belong to same class, we can merge them.
-      // This tends to reduce the number of entries (used memory) by 10x.
-      size_t num_entries = cache.size();
-      if (cache.size() > 1) {
-        for (auto it = std::next(cache.begin()); it != cache.end(); it++) {
-          if (std::prev(it)->second == it->second) {
-            std::prev(it)->first = 0;  // Clear entry with lower end_dex_offset (mark to remove).
-            num_entries--;
-          }
-        }
-      }
-
-      // The cache is immutable now. Store it as continuous vector to save space.
-      class_cache_.reserve(num_entries);
-      auto pred = [](auto it) { return it.first != 0; };  // Entries to copy (not cleared above).
-      std::copy_if(cache.begin(), cache.end(), std::back_inserter(class_cache_), pred);
+      CreateClassCache();
     }
 
     // Binary search in the class cache. First element of the pair is the key.
@@ -127,20 +129,31 @@
     return false;
   }
 
+  // The underlying ART object.
+  std::unique_ptr<const art::DexFile> dex_file_;
+
   // Binary search table with (end_dex_offset, class_def_index) entries.
   std::vector<std::pair<uint32_t, uint32_t>> class_cache_;
+
+  // Used as short lived temporary when needed. Avoids alloc/free.
+  std::string temporary_qualified_name_;
 };
 
-int ExtDexFileOpenFromMemory(const void* addr,
-                             /*inout*/ size_t* size,
-                             const char* location,
-                             /*out*/ ExtDexFile** ext_dex_file) {
-  if (*size < sizeof(art::DexFile::Header)) {
-    *size = sizeof(art::DexFile::Header);
-    return kExtDexFileNotEnoughData;
+ADexFile_Error ADexFile_create(const void* _Nonnull address,
+                               size_t size,
+                               size_t* _Nullable new_size,
+                               const char* _Nonnull location,
+                               /*out*/ ADexFile* _Nullable * _Nonnull out_dex_file) {
+  *out_dex_file = nullptr;
+
+  if (size < sizeof(art::DexFile::Header)) {
+    if (new_size != nullptr) {
+      *new_size = sizeof(art::DexFile::Header);
+    }
+    return ADEXFILE_ERROR_NOT_ENOUGH_DATA;
   }
 
-  const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(addr);
+  const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(address);
   uint32_t file_size = header->file_size_;
   if (art::CompactDexFile::IsMagicValid(header->magic_)) {
     // Compact dex files store the data section separately so that it can be shared.
@@ -149,25 +162,27 @@
     //       In practice, this should be fine, as such sharing only happens on disk.
     uint32_t computed_file_size;
     if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
-      return kExtDexFileInvalidHeader;
+      return ADEXFILE_ERROR_INVALID_HEADER;
     }
     if (computed_file_size > file_size) {
       file_size = computed_file_size;
     }
   } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
-    return kExtDexFileInvalidHeader;
+    return ADEXFILE_ERROR_INVALID_HEADER;
   }
 
-  if (*size < file_size) {
-    *size = file_size;
-    return kExtDexFileNotEnoughData;
+  if (size < file_size) {
+    if (new_size != nullptr) {
+      *new_size = file_size;
+    }
+    return ADEXFILE_ERROR_NOT_ENOUGH_DATA;
   }
 
   std::string loc_str(location);
   art::DexFileLoader loader;
   std::string error_msg;
-  std::unique_ptr<const art::DexFile> dex_file = loader.Open(static_cast<const uint8_t*>(addr),
-                                                             *size,
+  std::unique_ptr<const art::DexFile> dex_file = loader.Open(static_cast<const uint8_t*>(address),
+                                                             size,
                                                              loc_str,
                                                              header->checksum_,
                                                              /*oat_dex_file=*/nullptr,
@@ -175,78 +190,118 @@
                                                              /*verify_checksum=*/false,
                                                              &error_msg);
   if (dex_file == nullptr) {
-    LOG(ERROR) << "Can not opend dex file " << loc_str << ": " << error_msg;
-    return kExtDexFileError;
+    LOG(ERROR) << "Can not open dex file " << loc_str << ": " << error_msg;
+    return ADEXFILE_ERROR_INVALID_DEX;
   }
 
-  *ext_dex_file = new ExtDexFile(std::move(dex_file));
-  return kExtDexFileOk;
+  *out_dex_file = new ADexFile(std::move(dex_file));
+  return ADEXFILE_ERROR_OK;
 }
 
-int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file,
-                                     uint32_t dex_offset,
-                                     uint32_t flags,
-                                     ExtDexFileMethodInfoCallback* method_info_cb,
-                                     void* user_data) {
-  if (!ext_dex_file->dex_file_->IsInDataSection(ext_dex_file->dex_file_->Begin() + dex_offset)) {
-    return false;  // The DEX offset is not within the bytecode of this dex file.
+void ADexFile_destroy(ADexFile* self) {
+  delete self;
+}
+
+size_t ADexFile_findMethodAtOffset(ADexFile* self,
+                                   size_t dex_offset,
+                                   ADexFile_MethodCallback* callback,
+                                   void* callback_data) {
+  const art::DexFile* dex_file = self->dex_file_.get();
+  if (!dex_file->IsInDataSection(dex_file->Begin() + dex_offset)) {
+    return 0;  // The DEX offset is not within the bytecode of this dex file.
   }
 
-  if (ext_dex_file->dex_file_->IsCompactDexFile()) {
+  if (dex_file->IsCompactDexFile()) {
     // The data section of compact dex files might be shared.
     // Check the subrange unique to this compact dex.
     const art::CompactDexFile::Header& cdex_header =
-        ext_dex_file->dex_file_->AsCompactDexFile()->GetHeader();
+        dex_file->AsCompactDexFile()->GetHeader();
     uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
     uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
     if (dex_offset < begin || dex_offset >= end) {
-      return false;  // The DEX offset is not within the bytecode of this dex file.
+      return 0;  // The DEX offset is not within the bytecode of this dex file.
     }
   }
 
-  uint32_t method_index, addr, size;
-  if (!ext_dex_file->GetMethodDefIndex(dex_offset, &method_index, &addr, &size)) {
-    return false;
+  ADexFile_Method info;
+  if (!self->FindMethod(dex_offset, &info)) {
+    return 0;
   }
 
-  bool with_signature = flags & kExtDexFileWithSignature;
-  std::string name = ext_dex_file->dex_file_->PrettyMethod(method_index, with_signature);
-  ExtDexFileMethodInfo info {
-    .sizeof_struct = sizeof(ExtDexFileMethodInfo),
-    .addr = addr,
-    .size = size,
-    .name = name.c_str(),
-    .name_size = name.size()
-  };
-  method_info_cb(user_data, &info);
-  return true;
+  callback(callback_data, &info);
+  return 1;
 }
 
-void ExtDexFileGetAllMethodInfos(ExtDexFile* ext_dex_file,
-                                 uint32_t flags,
-                                 ExtDexFileMethodInfoCallback* method_info_cb,
-                                 void* user_data) {
-  const art::DexFile* dex_file = ext_dex_file->dex_file_.get();
-  for (art::ClassAccessor accessor : ext_dex_file->dex_file_->GetClasses()) {
+size_t ADexFile_forEachMethod(ADexFile* self,
+                              ADexFile_MethodCallback* callback,
+                              void* callback_data) {
+  size_t count = 0;
+  for (art::ClassAccessor accessor : self->dex_file_->GetClasses()) {
     for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
       art::CodeItemInstructionAccessor code = method.GetInstructions();
       if (code.HasCodeItem()) {
-        const uint8_t* insns = reinterpret_cast<const uint8_t*>(code.Insns());
-        bool with_signature = flags & kExtDexFileWithSignature;
-        std::string name = dex_file->PrettyMethod(method.GetIndex(), with_signature);
-        ExtDexFileMethodInfo info {
-          .sizeof_struct = sizeof(ExtDexFileMethodInfo),
-          .addr = static_cast<uint32_t>(insns - dex_file->Begin()),
+        size_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - self->dex_file_->Begin();
+        ADexFile_Method info {
+          .adex = self,
+          .index = method.GetIndex(),
+          .offset = offset,
           .size = code.InsnsSizeInBytes(),
-          .name = name.c_str(),
-          .name_size = name.size()
         };
-        method_info_cb(user_data, &info);
+        callback(callback_data, &info);
+        count++;
       }
     }
   }
+  return count;
 }
 
-void ExtDexFileClose(ExtDexFile* ext_dex_file) { delete (ext_dex_file); }
+size_t ADexFile_Method_getCodeOffset(const ADexFile_Method* self,
+                                     size_t* out_size) {
+  if (out_size != nullptr) {
+    *out_size = self->size;
+  }
+  return self->offset;
+}
+
+const char* ADexFile_Method_getName(const ADexFile_Method* self,
+                                    size_t* out_size) {
+  const char* name = self->adex->dex_file_->GetMethodName(self->index);
+  if (out_size != nullptr) {
+    *out_size = strlen(name);
+  }
+  return name;
+}
+
+const char* ADexFile_Method_getQualifiedName(const ADexFile_Method* self,
+                                             int with_params,
+                                             size_t* out_size) {
+  std::string& temp = self->adex->temporary_qualified_name_;
+  temp.clear();
+  self->adex->dex_file_->AppendPrettyMethod(self->index, with_params, &temp);
+  if (out_size != nullptr) {
+    *out_size = temp.size();
+  }
+  return temp.data();
+}
+
+const char* ADexFile_Method_getClassDescriptor(const ADexFile_Method* self,
+                                               size_t* out_size) {
+  const art::dex::MethodId& method_id = self->adex->dex_file_->GetMethodId(self->index);
+  const char* name = self->adex->dex_file_->GetMethodDeclaringClassDescriptor(method_id);
+  if (out_size != nullptr) {
+    *out_size = strlen(name);
+  }
+  return name;
+}
+
+const char* ADexFile_Error_toString(ADexFile_Error self) {
+  switch (self) {
+    case ADEXFILE_ERROR_OK: return "Ok";
+    case ADEXFILE_ERROR_INVALID_DEX: return "Dex file is invalid.";
+    case ADEXFILE_ERROR_NOT_ENOUGH_DATA: return "Not enough data. Incomplete dex file.";
+    case ADEXFILE_ERROR_INVALID_HEADER: return "Invalid dex file header.";
+  }
+  return nullptr;
+}
 
 }  // extern "C"
diff --git a/libdexfile/external/dex_file_ext_c_test.c b/libdexfile/external/dex_file_ext_c_test.c
index e113971..656a6cb 100644
--- a/libdexfile/external/dex_file_ext_c_test.c
+++ b/libdexfile/external/dex_file_ext_c_test.c
@@ -14,66 +14,6 @@
  * limitations under the License.
  */
 
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-/* The main purpose of this test is to ensure this C header compiles in C, so
- * that no C++ features inadvertently leak into the C ABI. */
+/* The main purpose of this file is to ensure this C header compiles in C,
+ * so that no C++ features inadvertently leak into the C ABI. */
 #include "art_api/dex_file_external.h"
-
-static const char gtest_output_arg[] = "--gtest_output=xml:";
-static const char gtest_output_xml[] = "\
-<?xml version=\"1.0\"?>\n\
-<testsuites tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\" name=\"AllTests\">\n\
-  <testsuite tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\" name=\"NopTest\">\n\
-    <testcase name=\"nop\" status=\"run\" />\n\
-  </testsuite>\n\
-</testsuites>";
-
-static const char canned_stdout[] = "\
-[==========] Running 1 test from 1 test suite.\n\
-[----------] 1 test from NopTest\n\
-[ RUN      ] NopTest.nop\n\
-[       OK ] NopTest.nop (0 ms)\n\
-[----------] 1 test from NopTest (0 ms total)\n\
-\n\
-[==========] 1 test from 1 test suite ran. (0 ms total)\n\
-[  PASSED  ] 1 test.\n\
-";
-
-/* Writes a fake gtest xml report to the given path. */
-static int write_gtest_output_xml(char* gtest_output_path) {
-  FILE* output_fd = fopen(gtest_output_path, "w");
-  if (output_fd == NULL) {
-    fprintf(stderr, "Failed to open %s: %s\n", gtest_output_path, strerror(errno));
-    return 1;
-  }
-  if (fprintf(output_fd, gtest_output_xml) != sizeof(gtest_output_xml) - 1) {
-    fprintf(stderr, "Failed to write %s: %s\n", gtest_output_path, strerror(errno));
-    fclose(output_fd);
-    return 1;
-  }
-  if (fclose(output_fd) != 0) {
-    fprintf(stderr, "Failed to close %s: %s\n", gtest_output_path, strerror(errno));
-    return 1;
-  }
-  return 0;
-}
-
-int main(int argc, char** argv) {
-  int i;
-  for (i = 1; i < argc; ++i) {
-    if (strncmp(argv[i], gtest_output_arg, sizeof(gtest_output_arg) - 1) == 0) {
-      /* The ART gtest framework expects all tests to understand --gtest_output. */
-      if (write_gtest_output_xml(argv[i] + sizeof(gtest_output_arg) - 1)) {
-        return 1;
-      }
-    }
-  }
-  /* Tradefed parses the output, so send something passable there. */
-  if (fputs(canned_stdout, stdout) < 0) {
-    return 1;
-  }
-  return 0;
-}
diff --git a/libdexfile/external/dex_file_ext_test.cc b/libdexfile/external/dex_file_ext_test.cc
new file mode 100644
index 0000000..54aaa3b
--- /dev/null
+++ b/libdexfile/external/dex_file_ext_test.cc
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <dex/dex_file.h>
+#include <gtest/gtest.h>
+
+#include "art_api/dex_file_external.h"
+#include "dex_file_test_data.h"
+
+namespace art_api {
+namespace dex {
+
+class ADexFileTest : public ::testing::Test {
+ protected:
+  void TearDown() override {
+    ADexFile_destroy(dex_);
+  }
+
+  ADexFile* dex_ = nullptr;
+};
+
+TEST_F(ADexFileTest, create) {
+  size_t size = sizeof(kDexData);
+  EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex_), ADEXFILE_ERROR_OK);
+  EXPECT_EQ(size, sizeof(kDexData));
+  EXPECT_NE(dex_, nullptr);
+}
+
+TEST_F(ADexFileTest, create_null_new_size) {
+  const size_t size = sizeof(kDexData);
+  EXPECT_EQ(ADexFile_create(kDexData, size, nullptr, "", &dex_), ADEXFILE_ERROR_OK);
+  EXPECT_NE(dex_, nullptr);
+}
+
+TEST_F(ADexFileTest, create_header_too_small) {
+  size_t size = sizeof(art::DexFile::Header) - 1;
+  EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex_), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+  EXPECT_EQ(size, sizeof(art::DexFile::Header));
+  EXPECT_EQ(dex_, nullptr);
+}
+
+TEST_F(ADexFileTest, create_file_too_small) {
+  size_t size = sizeof(art::DexFile::Header);
+  EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex_), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+  EXPECT_EQ(size, sizeof(kDexData));
+  EXPECT_EQ(dex_, nullptr);
+}
+
+static ADexFile* GetTestDexData() {
+  size_t size = sizeof(kDexData);
+  ADexFile* dex;
+  EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex), ADEXFILE_ERROR_OK);
+  EXPECT_EQ(size, sizeof(kDexData));
+  EXPECT_NE(dex, nullptr);
+  return dex;
+}
+
+TEST_F(ADexFileTest, findMethodAtOffset) {
+  dex_ = GetTestDexData();
+  ASSERT_NE(dex_, nullptr);
+
+  bool found_init = false;
+  auto check_init = [](void* ctx, const ADexFile_Method* method) {
+    size_t size;
+    size_t offset = ADexFile_Method_getCodeOffset(method, &size);
+    EXPECT_EQ(offset, 0x100u);
+    EXPECT_EQ(size, 8u);
+    EXPECT_STREQ(ADexFile_Method_getName(method, &size), "<init>");
+    EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, false, &size), "Main.<init>");
+    EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, true, &size), "void Main.<init>()");
+    EXPECT_STREQ(ADexFile_Method_getClassDescriptor(method, &size), "LMain;");
+    *reinterpret_cast<bool*>(ctx) = true;
+  };
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x102, check_init, &found_init), 1u);
+  EXPECT_TRUE(found_init);
+
+  bool found_main = false;
+  auto check_main = [](void* ctx, const ADexFile_Method* method) {
+    size_t size;
+    size_t offset = ADexFile_Method_getCodeOffset(method, &size);
+    EXPECT_EQ(offset, 0x118u);
+    EXPECT_EQ(size, 2u);
+    EXPECT_STREQ(ADexFile_Method_getName(method, &size), "main");
+    EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, false, &size), "Main.main");
+    EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, true, &size), "void Main.main(java.lang.String[])");
+    EXPECT_STREQ(ADexFile_Method_getClassDescriptor(method, &size), "LMain;");
+    *reinterpret_cast<bool*>(ctx) = true;
+  };
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x118, check_main, &found_main), 1u);
+  EXPECT_TRUE(found_main);
+}
+
+TEST_F(ADexFileTest, findMethodAtOffset_for_offset_boundaries) {
+  dex_ = GetTestDexData();
+  ASSERT_NE(dex_, nullptr);
+
+  auto no_cb = [](void*, const ADexFile_Method*) {};
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x99, no_cb, nullptr), 0);
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x100, no_cb, nullptr), 1);
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x107, no_cb, nullptr), 1);
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x108, no_cb, nullptr), 0);
+  EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x100000, no_cb, nullptr), 0);
+}
+
+TEST_F(ADexFileTest, forEachMethod) {
+  dex_ = GetTestDexData();
+  ASSERT_NE(dex_, nullptr);
+
+  std::vector<std::string> names;
+  auto add = [](void* ctx, const ADexFile_Method* method) {
+    reinterpret_cast<decltype(names)*>(ctx)->
+        push_back(ADexFile_Method_getQualifiedName(method, false, nullptr));
+  };
+  EXPECT_EQ(ADexFile_forEachMethod(dex_, add, &names), 2u);
+  EXPECT_EQ(names, std::vector<std::string>({"Main.<init>", "Main.main"}));
+}
+
+TEST_F(ADexFileTest, Error_toString) {
+  constexpr size_t kNumErrors = 4;
+  for (size_t i = 0; i < kNumErrors; i++) {
+    const char* err = ADexFile_Error_toString(static_cast<ADexFile_Error>(i));
+    ASSERT_NE(err, nullptr);
+    ASSERT_FALSE(std::string_view(err).empty());
+  }
+  const char* err = ADexFile_Error_toString(static_cast<ADexFile_Error>(kNumErrors));
+  ASSERT_EQ(err, nullptr);
+}
+
+}  // namespace dex
+}  // namespace art_api
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 6313afe..23fc88a 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -33,21 +33,15 @@
 namespace art_api {
 namespace dex {
 
-#define FOR_ALL_DLFUNCS(MACRO) \
-  MACRO(DexFile, ExtDexFileOpenFromMemory) \
-  MACRO(DexFile, ExtDexFileGetMethodInfoForOffset) \
-  MACRO(DexFile, ExtDexFileGetAllMethodInfos) \
-  MACRO(DexFile, ExtDexFileClose)
-
-#ifdef STATIC_LIB
-#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC;
+#if defined(STATIC_LIB)
+#define DEFINE_ADEX_FILE_SYMBOL(DLFUNC) decltype(DLFUNC)* g_##DLFUNC = DLFUNC;
 #else
-#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = nullptr;
+#define DEFINE_ADEX_FILE_SYMBOL(DLFUNC) decltype(DLFUNC)* g_##DLFUNC = nullptr;
 #endif
-FOR_ALL_DLFUNCS(DEFINE_DLFUNC_PTR)
-#undef DEFINE_DLFUNC_PTR
+FOR_EACH_ADEX_FILE_SYMBOL(DEFINE_ADEX_FILE_SYMBOL)
+#undef DEFINE_ADEX_FILE_SYMBOL
 
-bool TryLoadLibdexfileExternal([[maybe_unused]] std::string* err_msg) {
+bool TryLoadLibdexfile([[maybe_unused]] std::string* err_msg) {
 #if defined(STATIC_LIB)
   // Nothing to do here since all function pointers are initialised statically.
   return true;
@@ -76,18 +70,18 @@
       return false;
     }
 
-#define RESOLVE_DLFUNC_PTR(CLASS, DLFUNC) \
-    decltype(DLFUNC)* DLFUNC##_ptr = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
-    if ((DLFUNC) == nullptr) { \
+#define RESOLVE_ADEX_FILE_SYMBOL(DLFUNC) \
+    auto DLFUNC##_ptr = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
+    if (DLFUNC##_ptr == nullptr) { \
       *err_msg = dlerror(); \
       return false; \
     }
-    FOR_ALL_DLFUNCS(RESOLVE_DLFUNC_PTR);
-#undef RESOLVE_DLFUNC_PTR
+FOR_EACH_ADEX_FILE_SYMBOL(RESOLVE_ADEX_FILE_SYMBOL)
+#undef RESOLVE_ADEX_FILE_SYMBOL
 
-#define SET_DLFUNC_PTR(CLASS, DLFUNC) CLASS::g_##DLFUNC = DLFUNC##_ptr;
-    FOR_ALL_DLFUNCS(SET_DLFUNC_PTR);
-#undef SET_DLFUNC_PTR
+#define SET_ADEX_FILE_SYMBOL(DLFUNC) g_##DLFUNC = DLFUNC##_ptr;
+    FOR_EACH_ADEX_FILE_SYMBOL(SET_ADEX_FILE_SYMBOL);
+#undef SET_ADEX_FILE_SYMBOL
 
     is_loaded = true;
   }
@@ -96,102 +90,13 @@
 #endif  // !defined(NO_DEXFILE_SUPPORT) && !defined(STATIC_LIB)
 }
 
-void LoadLibdexfileExternal() {
+void LoadLibdexfile() {
 #ifndef STATIC_LIB
-  if (std::string err_msg; !TryLoadLibdexfileExternal(&err_msg)) {
+  if (std::string err_msg; !TryLoadLibdexfile(&err_msg)) {
     LOG_ALWAYS_FATAL("%s", err_msg.c_str());
   }
 #endif
 }
 
-DexFile::~DexFile() { g_ExtDexFileClose(ext_dex_file_); }
-
-std::unique_ptr<DexFile> DexFile::OpenFromMemory(const void* addr,
-                                                 size_t* size,
-                                                 const std::string& location,
-                                                 /*out*/ std::string* error_msg) {
-  if (UNLIKELY(g_ExtDexFileOpenFromMemory == nullptr)) {
-    // Load libdexfile_external.so in this factory function, so instance
-    // methods don't need to check this.
-    LoadLibdexfileExternal();
-  }
-  ExtDexFile* ext_dex_file;
-  int res = g_ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_dex_file);
-  switch (static_cast<ExtDexFileError>(res)) {
-    case kExtDexFileOk:
-      return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
-    case kExtDexFileInvalidHeader:
-      *error_msg = std::string("Invalid DexFile header ") + location;
-      return nullptr;
-    case kExtDexFileNotEnoughData:
-      *error_msg = std::string("Not enough data");
-      return nullptr;
-    case kExtDexFileError:
-      break;
-  }
-  *error_msg = std::string("Failed to open DexFile ") + location;
-  return nullptr;
-}
-
-std::unique_ptr<DexFile> DexFile::OpenFromFd(int fd,
-                                             off_t offset,
-                                             const std::string& location,
-                                             /*out*/ std::string* error_msg) {
-  using android::base::StringPrintf;
-  size_t length;
-  {
-    struct stat sbuf;
-    std::memset(&sbuf, 0, sizeof(sbuf));
-    if (fstat(fd, &sbuf) == -1) {
-      *error_msg = StringPrintf("fstat '%s' failed: %s", location.c_str(), std::strerror(errno));
-      return nullptr;
-    }
-    if (S_ISDIR(sbuf.st_mode)) {
-      *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
-      return nullptr;
-    }
-    length = sbuf.st_size;
-  }
-
-  if (static_cast<off_t>(length) < offset) {
-    *error_msg = StringPrintf(
-        "Offset %" PRId64 " too large for '%s' of size %zu",
-        int64_t{offset},
-        location.c_str(),
-        length);
-    return nullptr;
-  }
-  length -= offset;
-
-  std::unique_ptr<android::base::MappedFile> map;
-  map = android::base::MappedFile::FromFd(fd, offset, length, PROT_READ);
-  if (map == nullptr) {
-    *error_msg = StringPrintf("mmap '%s' failed: %s", location.c_str(), std::strerror(errno));
-    return nullptr;
-  }
-
-  std::unique_ptr<DexFile> dex = OpenFromMemory(map->data(), &length, location, error_msg);
-  if (dex != nullptr) {
-    dex->map_ = std::move(map);  // Ensure the map gets freed with the dex file.
-  }
-  return dex;
-}
-
-MethodInfo DexFile::GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
-  MethodInfo res{};
-  auto set_method = [&res](ExtDexFileMethodInfo* info) { res = AbsorbMethodInfo(info); };
-  uint32_t flags = with_signature ? kExtDexFileWithSignature : 0;
-  GetMethodInfoForOffset(dex_offset, set_method, flags);
-  return res;
-}
-
-std::vector<MethodInfo> DexFile::GetAllMethodInfos(bool with_signature) {
-  std::vector<MethodInfo> res;
-  auto add_method = [&res](ExtDexFileMethodInfo* info) { res.push_back(AbsorbMethodInfo(info)); };
-  uint32_t flags = with_signature ? kExtDexFileWithSignature : 0;
-  GetAllMethodInfos(add_method, flags);
-  return res;
-}
-
 }  // namespace dex
 }  // namespace art_api
diff --git a/libdexfile/external/dex_file_supp_test.cc b/libdexfile/external/dex_file_supp_test.cc
index ff263fc..3285edf 100644
--- a/libdexfile/external/dex_file_supp_test.cc
+++ b/libdexfile/external/dex_file_supp_test.cc
@@ -25,218 +25,104 @@
 #include <gtest/gtest.h>
 
 #include "art_api/dex_file_support.h"
+#include "dex_file_test_data.h"
 
 namespace art_api {
 namespace dex {
 
-static constexpr uint32_t kDexData[] = {
-    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
-    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
-    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
-    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
-    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
-    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
-    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
-    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
-    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
-    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
-    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
-    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
-    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
-    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
-    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
-    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
-    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
-};
-
-TEST(DexFileTest, from_memory_header_too_small) {
-  size_t size = sizeof(art::DexFile::Header) - 1;
-  std::string error_msg;
-  EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr);
-  EXPECT_EQ(size, sizeof(art::DexFile::Header));
-  EXPECT_FALSE(error_msg.empty());
+TEST(DexFileTest, create) {
+  size_t size = sizeof(kDexData);
+  std::unique_ptr<DexFile> dex_file;
+  EXPECT_TRUE(DexFile::Create(kDexData, size, &size, "", &dex_file).Ok());
+  EXPECT_EQ(size, sizeof(kDexData));
+  EXPECT_NE(dex_file, nullptr);
 }
 
-TEST(DexFileTest, from_memory_file_too_small) {
+TEST(DexFileTest, create_header_too_small) {
+  size_t size = sizeof(art::DexFile::Header) - 1;
+  std::unique_ptr<DexFile> dex_file;
+  DexFile::Error error = DexFile::Create(kDexData, size, &size, "", &dex_file);
+  EXPECT_FALSE(error.Ok());
+  EXPECT_EQ(error.Code(), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+  EXPECT_STREQ(error.ToString(), "Not enough data. Incomplete dex file.");
+  EXPECT_EQ(size, sizeof(art::DexFile::Header));
+  EXPECT_EQ(dex_file, nullptr);
+}
+
+TEST(DexFileTest, create_file_too_small) {
   size_t size = sizeof(art::DexFile::Header);
-  std::string error_msg;
-  EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr);
+  std::unique_ptr<DexFile> dex_file;
+  DexFile::Error error = DexFile::Create(kDexData, size, &size, "", &dex_file);
+  EXPECT_FALSE(error.Ok());
+  EXPECT_EQ(error.Code(), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+  EXPECT_STREQ(error.ToString(), "Not enough data. Incomplete dex file.");
   EXPECT_EQ(size, sizeof(kDexData));
-  EXPECT_FALSE(error_msg.empty());
+  EXPECT_EQ(dex_file, nullptr);
 }
 
 static std::unique_ptr<DexFile> GetTestDexData() {
   size_t size = sizeof(kDexData);
-  std::string error_msg;
-  std::unique_ptr<DexFile> dex_file = DexFile::OpenFromMemory(kDexData, &size, "", &error_msg);
-  EXPECT_TRUE(error_msg.empty());
+  std::unique_ptr<DexFile> dex_file;
+  EXPECT_TRUE(DexFile::Create(kDexData, size, &size, "", &dex_file).Ok());
+  EXPECT_EQ(size, sizeof(kDexData));
+  EXPECT_NE(dex_file, nullptr);
   return dex_file;
 }
 
-TEST(DexFileTest, from_memory) {
-  EXPECT_NE(GetTestDexData(), nullptr);
-}
-
-TEST(DexFileTest, from_fd_header_too_small) {
-  TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1);
-  ASSERT_EQ(sizeof(art::DexFile::Header) - 1,
-            static_cast<size_t>(
-                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1))));
-
-  std::string error_msg;
-  EXPECT_EQ(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr);
-  EXPECT_FALSE(error_msg.empty());
-}
-
-TEST(DexFileTest, from_fd_file_too_small) {
-  TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1);
-  ASSERT_EQ(sizeof(art::DexFile::Header),
-            static_cast<size_t>(
-                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)))));
-
-  std::string error_msg;
-  EXPECT_EQ(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr);
-  EXPECT_FALSE(error_msg.empty());
-}
-
-TEST(DexFileTest, from_fd) {
-  TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1);
-  ASSERT_EQ(sizeof(kDexData),
-            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
-
-  std::string error_msg;
-  EXPECT_NE(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr);
-  EXPECT_TRUE(error_msg.empty());
-}
-
-TEST(DexFileTest, from_fd_non_zero_offset) {
-  TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1);
-  ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET));
-  ASSERT_EQ(sizeof(kDexData),
-            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
-
-  std::string error_msg;
-  EXPECT_NE(DexFile::OpenFromFd(tf.fd, 0x100, tf.path, &error_msg), nullptr);
-  EXPECT_TRUE(error_msg.empty());
-}
-
-TEST(DexFileTest, get_method_info_for_offset_without_signature) {
+TEST(DexFileTest, findMethodAtOffset) {
   std::unique_ptr<DexFile> dex_file = GetTestDexData();
   ASSERT_NE(dex_file, nullptr);
 
-  MethodInfo info = dex_file->GetMethodInfoForOffset(0x102, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  EXPECT_EQ(info.len, int32_t{8});
-  EXPECT_STREQ(info.name.data(), "Main.<init>");
+  bool found_init = false;
+  auto check_init = [&](const DexFile::Method& method) {
+    size_t size;
+    size_t offset = method.GetCodeOffset(&size);
+    EXPECT_EQ(offset, 0x100u);
+    EXPECT_EQ(size, 8u);
+    EXPECT_STREQ(method.GetName(), "<init>");
+    EXPECT_STREQ(method.GetQualifiedName(), "Main.<init>");
+    EXPECT_STREQ(method.GetQualifiedName(true), "void Main.<init>()");
+    EXPECT_STREQ(method.GetClassDescriptor(), "LMain;");
+    found_init = true;
+  };
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x102, check_init), 1u);
+  EXPECT_TRUE(found_init);
 
-  info = dex_file->GetMethodInfoForOffset(0x118, false);
-  EXPECT_EQ(info.offset, int32_t{0x118});
-  EXPECT_EQ(info.len, int32_t{2});
-  EXPECT_STREQ(info.name.data(), "Main.main");
-
-  // Retrieve a cached result.
-  info = dex_file->GetMethodInfoForOffset(0x104, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  EXPECT_EQ(info.len, int32_t{8});
-  EXPECT_STREQ(info.name.data(), "Main.<init>");
-}
-
-TEST(DexFileTest, get_method_info_for_offset_with_signature) {
-  std::unique_ptr<DexFile> dex_file = GetTestDexData();
-  ASSERT_NE(dex_file, nullptr);
-
-  MethodInfo info = dex_file->GetMethodInfoForOffset(0x102, true);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  EXPECT_EQ(info.len, int32_t{8});
-  EXPECT_STREQ(info.name.data(), "void Main.<init>()");
-
-  info = dex_file->GetMethodInfoForOffset(0x118, true);
-  EXPECT_EQ(info.offset, int32_t{0x118});
-  EXPECT_EQ(info.len, int32_t{2});
-  EXPECT_STREQ(info.name.data(), "void Main.main(java.lang.String[])");
-
-  // Retrieve a cached result.
-  info = dex_file->GetMethodInfoForOffset(0x104, true);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  EXPECT_EQ(info.len, int32_t{8});
-  EXPECT_STREQ(info.name.data(), "void Main.<init>()");
-
-  // with_signature doesn't affect the cache.
-  info = dex_file->GetMethodInfoForOffset(0x104, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  EXPECT_EQ(info.len, int32_t{8});
-  EXPECT_STREQ(info.name.data(), "Main.<init>");
+  bool found_main = false;
+  auto check_main = [&](const DexFile::Method& method) {
+    size_t size;
+    size_t offset = method.GetCodeOffset(&size);
+    EXPECT_EQ(offset, 0x118u);
+    EXPECT_EQ(size, 2u);
+    EXPECT_STREQ(method.GetName(), "main");
+    EXPECT_STREQ(method.GetQualifiedName(), "Main.main");
+    EXPECT_STREQ(method.GetQualifiedName(true), "void Main.main(java.lang.String[])");
+    EXPECT_STREQ(method.GetClassDescriptor(), "LMain;");
+    found_main = true;
+  };
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x118, check_main), 1u);
+  EXPECT_TRUE(found_main);
 }
 
 TEST(DexFileTest, get_method_info_for_offset_boundaries) {
   std::unique_ptr<DexFile> dex_file = GetTestDexData();
   ASSERT_NE(dex_file, nullptr);
 
-  MethodInfo info = dex_file->GetMethodInfoForOffset(0x100000, false);
-  EXPECT_EQ(info.offset, int32_t{0});
-
-  info = dex_file->GetMethodInfoForOffset(0x99, false);
-  EXPECT_EQ(info.offset, int32_t{0});
-  info = dex_file->GetMethodInfoForOffset(0x100, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  info = dex_file->GetMethodInfoForOffset(0x107, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-  info = dex_file->GetMethodInfoForOffset(0x108, false);
-  EXPECT_EQ(info.offset, int32_t{0});
-
-  // Make sure that once the whole dex file has been cached, no problems occur.
-  info = dex_file->GetMethodInfoForOffset(0x98, false);
-  EXPECT_EQ(info.offset, int32_t{0});
-
-  // Choose a value that is in the cached map, but not in a valid method.
-  info = dex_file->GetMethodInfoForOffset(0x110, false);
-  EXPECT_EQ(info.offset, int32_t{0});
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x99, [](auto){}), 0);
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x100, [](auto){}), 1);
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x107, [](auto){}), 1);
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x108, [](auto){}), 0);
+  EXPECT_EQ(dex_file->FindMethodAtOffset(0x100000, [](auto){}), 0);
 }
 
 TEST(DexFileTest, get_all_method_infos_without_signature) {
   std::unique_ptr<DexFile> dex_file = GetTestDexData();
   ASSERT_NE(dex_file, nullptr);
 
-  std::vector<MethodInfo> infos;
-  infos.emplace_back(MethodInfo{0x100, 8, std::string("Main.<init>")});
-  infos.emplace_back(MethodInfo{0x118, 2, std::string("Main.main")});
-  ASSERT_EQ(dex_file->GetAllMethodInfos(false), infos);
-}
-
-TEST(DexFileTest, get_all_method_infos_with_signature) {
-  std::unique_ptr<DexFile> dex_file = GetTestDexData();
-  ASSERT_NE(dex_file, nullptr);
-
-  std::vector<MethodInfo> infos;
-  infos.emplace_back(MethodInfo{0x100, 8, std::string("void Main.<init>()")});
-  infos.emplace_back(MethodInfo{0x118, 2, std::string("void Main.main(java.lang.String[])")});
-  ASSERT_EQ(dex_file->GetAllMethodInfos(true), infos);
-}
-
-TEST(DexFileTest, move_construct) {
-  std::unique_ptr<DexFile> dex_file = GetTestDexData();
-  ASSERT_NE(dex_file, nullptr);
-
-  auto df1 = DexFile(std::move(*dex_file));
-  auto df2 = DexFile(std::move(df1));
-
-  MethodInfo info = df2.GetMethodInfoForOffset(0x100, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
-}
-
-TEST(DexFileTest, pointer_construct) {
-  std::unique_ptr<DexFile> dex_file = GetTestDexData();
-  ASSERT_NE(dex_file, nullptr);
-
-  auto new_dex = DexFile(dex_file);
-  ASSERT_TRUE(dex_file.get() == nullptr);
-
-  MethodInfo info = new_dex.GetMethodInfoForOffset(0x100, false);
-  EXPECT_EQ(info.offset, int32_t{0x100});
+  std::vector<std::string> names;
+  auto add = [&](const DexFile::Method& method) { names.push_back(method.GetQualifiedName()); };
+  EXPECT_EQ(dex_file->ForEachMethod(add), 2u);
+  EXPECT_EQ(names, std::vector<std::string>({"Main.<init>", "Main.main"}));
 }
 
 }  // namespace dex
diff --git a/libdexfile/external/dex_file_test_data.h b/libdexfile/external/dex_file_test_data.h
new file mode 100644
index 0000000..fd6b80b
--- /dev/null
+++ b/libdexfile/external/dex_file_test_data.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ART_LIBDEXFILE_EXTERNAL_DEX_FILE_TEST_DATA_H_
+#define ART_LIBDEXFILE_EXTERNAL_DEX_FILE_TEST_DATA_H_
+
+#include <stdint.h>
+
+namespace art_api {
+namespace dex {
+
+static constexpr uint32_t kDexData[] = {
+    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+}  // namespace dex
+}  // namespace art_api
+
+#endif  // ART_LIBDEXFILE_EXTERNAL_DEX_FILE_TEST_DATA_H_
diff --git a/libdexfile/external/include/art_api/dex_file_external.h b/libdexfile/external/include/art_api/dex_file_external.h
index b880335..360be92 100644
--- a/libdexfile/external/include/art_api/dex_file_external.h
+++ b/libdexfile/external/include/art_api/dex_file_external.h
@@ -18,67 +18,140 @@
 #define ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_EXTERNAL_H_
 
 // Dex file external API
-
-#include <sys/types.h>
 #include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
 
 // This is the stable C ABI that backs art_api::dex below. Structs and functions
 // may only be added here. C++ users should use dex_file_support.h instead.
 
-struct ExtDexFileMethodInfo {
-  size_t sizeof_struct;  // Size of this structure (to allow future extensions).
-  uint32_t addr;  // Start of dex byte-code relative to the start of the dex file.
-  uint32_t size;  // Size of the dex byte-code in bytes.
-  const char* name;
-  size_t name_size;
+struct ADexFile;
+typedef struct ADexFile ADexFile;
+
+struct ADexFile_Method;
+typedef struct ADexFile_Method ADexFile_Method;
+
+enum ADexFile_Error : uint32_t {
+  ADEXFILE_ERROR_OK = 0,
+  ADEXFILE_ERROR_INVALID_DEX = 1,
+  ADEXFILE_ERROR_INVALID_HEADER = 2,
+  ADEXFILE_ERROR_NOT_ENOUGH_DATA = 3,
 };
-
-enum ExtDexFileError {
-  kExtDexFileOk = 0,
-  kExtDexFileError = 1,  // Unspecified error.
-  kExtDexFileNotEnoughData = 2,
-  kExtDexFileInvalidHeader = 3,
-};
-
-enum ExtDexFileMethodFlags {
-  kExtDexFileWithSignature = 1,
-};
-
-struct ExtDexFile;
-
-// Try to open a dex file in the given memory range.
-// If the memory range is too small, larger suggest size is written to the argument.
-int ExtDexFileOpenFromMemory(const void* addr,
-                             /*inout*/ size_t* size,
-                             const char* location,
-                             /*out*/ struct ExtDexFile** ext_dex_file);
+typedef enum ADexFile_Error ADexFile_Error;
 
 // Callback used to return information about a dex method.
-typedef void ExtDexFileMethodInfoCallback(void* user_data,
-                                          struct ExtDexFileMethodInfo* method_info);
+// The method information is valid only during the callback.
+typedef void ADexFile_MethodCallback(void* _Nullable callback_data,
+                                     const ADexFile_Method* _Nonnull method);
 
-// Find a single dex method based on the given dex offset.
-int ExtDexFileGetMethodInfoForOffset(struct ExtDexFile* ext_dex_file,
-                                     uint32_t dex_offset,
-                                     uint32_t flags,
-                                     ExtDexFileMethodInfoCallback* method_info_cb,
-                                     void* user_data);
+// Interprets a chunk of memory as a dex file.
+//
+// @param address Pointer to the start of dex file data.
+//                The caller must retain the memory until the object is destroyed.
+// @param size Size of the memory range. If the size is too small, the method returns
+//             ADEXFILE_ERROR_NOT_ENOUGH_DATA and sets new_size to some larger size
+//             (which still might large enough, so several retries might be needed).
+// @param new_size On successful load, this contains exact dex file size from header.
+// @param location A string that describes the dex file. Preferably its path.
+//                 It is mostly used just for log messages and may be "".
+// @param dex_file The created dex file object, or nullptr on error.
+//                 It must be later freed with ADexFile_Destroy.
+//
+// @return ADEXFILE_ERROR_OK if successful.
+// @return ADEXFILE_ERROR_NOT_ENOUGH_DATA if the provided dex file is too short (truncated).
+// @return ADEXFILE_ERROR_INVALID_HEADER if the memory does not seem to represent DEX file.
+// @return ADEXFILE_ERROR_INVALID_DEX if any other non-specific error occurs.
+//
+// Thread-safe (creates new object).
+ADexFile_Error ADexFile_create(const void* _Nonnull address,
+                               size_t size,
+                               size_t* _Nullable new_size,
+                               const char* _Nonnull location,
+                               /*out*/ ADexFile* _Nullable * _Nonnull out_dex_file);
 
-// Return all dex methods in the dex file.
-void ExtDexFileGetAllMethodInfos(struct ExtDexFile* ext_dex_file,
-                                 uint32_t flags,
-                                 ExtDexFileMethodInfoCallback* method_info_cb,
-                                 void* user_data);
+// Find method at given offset and call callback with information about the method.
+//
+// @param dex_offset Offset relative to the start of the dex file header.
+// @param callback The callback to call when method is found. Any data that needs to
+//                 outlive the execution of the callback must be copied by the user.
+// @param callback_data Extra user-specified argument for the callback.
+//
+// @return Number of methods found (0 or 1).
+//
+// Not thread-safe for calls on the same ADexFile instance.
+size_t ADexFile_findMethodAtOffset(ADexFile* _Nonnull self,
+                                   size_t dex_offset,
+                                   ADexFile_MethodCallback* _Nonnull callback,
+                                   void* _Nullable callback_data);
 
-// Release all associated memory.
-void ExtDexFileClose(struct ExtDexFile* ext_dex_file);
+// Call callback for all methods in the DEX file.
+//
+// @param flags Specifies which information should be obtained.
+// @param callback The callback to call for all methods. Any data that needs to
+//                 outlive the execution of the callback must be copied by the user.
+// @param callback_data Extra user-specified argument for the callback.
+//
+// @return Number of methods found.
+//
+// Not thread-safe for calls on the same ADexFile instance.
+size_t ADexFile_forEachMethod(ADexFile* _Nonnull self,
+                              ADexFile_MethodCallback* _Nonnull callback,
+                              void* _Nullable callback_data);
 
-#ifdef __cplusplus
-}  // extern "C"
-#endif
+// Free the given object.
+//
+// Thread-safe, can be called only once for given instance.
+void ADexFile_destroy(ADexFile* _Nullable self);
+
+// @return Offset of byte-code of the method relative to start of the dex file.
+// @param out_size Optionally return size of byte-code in bytes.
+// Not thread-safe for calls on the same ADexFile instance.
+size_t ADexFile_Method_getCodeOffset(const ADexFile_Method* _Nonnull self,
+                                     size_t* _Nullable out_size);
+
+// @return Method name only (without class).
+//         The encoding is slightly modified UTF8 (see Dex specification).
+// @param out_size Optionally return string size (excluding null-terminator).
+//
+// Returned data may be short lived: it must be copied before calling
+// this method again within the same ADexFile.
+// (it is currently long lived, but this is not guaranteed in the future).
+//
+// Not thread-safe for calls on the same ADexFile instance.
+const char* _Nonnull ADexFile_Method_getName(const ADexFile_Method* _Nonnull self,
+                                             size_t* _Nullable out_size);
+
+// @return Method name (with class name).
+//         The encoding is slightly modified UTF8 (see Dex specification).
+// @param out_size Optionally return string size (excluding null-terminator).
+// @param with_params Whether to include method parameters and return type.
+//
+// Returned data may be short lived: it must be copied before calling
+// this method again within the same ADexFile.
+// (it points to pretty printing buffer within the ADexFile instance)
+//
+// Not thread-safe for calls on the same ADexFile instance.
+const char* _Nonnull ADexFile_Method_getQualifiedName(const ADexFile_Method* _Nonnull self,
+                                                      int with_params,
+                                                      size_t* _Nullable out_size);
+
+// @return Class descriptor (mangled class name).
+//         The encoding is slightly modified UTF8 (see Dex specification).
+// @param out_size Optionally return string size (excluding null-terminator).
+//
+// Returned data may be short lived: it must be copied before calling
+// this method again within the same ADexFile.
+// (it is currently long lived, but this is not guaranteed in the future).
+//
+// Not thread-safe for calls on the same ADexFile instance.
+const char* _Nonnull ADexFile_Method_getClassDescriptor(const ADexFile_Method* _Nonnull self,
+                                                        size_t* _Nullable out_size);
+
+// @return Compile-time literal or nullptr on error.
+const char* _Nullable ADexFile_Error_toString(ADexFile_Error self);
+
+__END_DECLS
 
 #endif  // ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_EXTERNAL_H_
diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h
index 7c4ff5c..2361bf9 100644
--- a/libdexfile/external/include/art_api/dex_file_support.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -19,133 +19,114 @@
 
 // C++ wrapper for the dex file external API.
 
-#include <cstring>
 #include <memory>
 #include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
 
 #include <android-base/macros.h>
-#include <android-base/mapped_file.h>
 
 #include "art_api/dex_file_external.h"
 
 namespace art_api {
 namespace dex {
 
-// Returns true if libdexfile_external.so is already loaded. Otherwise tries to
+#define FOR_EACH_ADEX_FILE_SYMBOL(MACRO) \
+  MACRO(ADexFile_Error_toString) \
+  MACRO(ADexFile_Method_getClassDescriptor) \
+  MACRO(ADexFile_Method_getCodeOffset) \
+  MACRO(ADexFile_Method_getName) \
+  MACRO(ADexFile_Method_getQualifiedName) \
+  MACRO(ADexFile_create) \
+  MACRO(ADexFile_destroy) \
+  MACRO(ADexFile_findMethodAtOffset) \
+  MACRO(ADexFile_forEachMethod) \
+
+#define DEFINE_ADEX_FILE_SYMBOL(DLFUNC) extern decltype(DLFUNC)* g_##DLFUNC;
+FOR_EACH_ADEX_FILE_SYMBOL(DEFINE_ADEX_FILE_SYMBOL)
+#undef DEFINE_ADEX_FILE_SYMBOL
+
+// Returns true if libdexfile.so is already loaded. Otherwise tries to
 // load it and returns true if successful. Otherwise returns false and sets
-// *error_msg. If false is returned then calling any function below may abort
-// the process. Thread safe.
-bool TryLoadLibdexfileExternal(std::string* error_msg);
+// *error_msg. Thread safe.
+bool TryLoadLibdexfile(std::string* error_msg);
 
-// Loads the libdexfile_external.so library and sets up function pointers.
-// Aborts with a fatal error on any error. For internal use by the classes
-// below.
-void LoadLibdexfileExternal();
+// TryLoadLibdexfile and fatally abort process if unsuccessful.
+void LoadLibdexfile();
 
-using DexString = std::string;
-
-struct MethodInfo {
-  uint32_t offset = 0;  // Code offset relative to the start of the dex file header
-  uint32_t len = 0;     // Code length
-  DexString name;
-};
-
-inline bool operator==(const MethodInfo& s1, const MethodInfo& s2) {
-  return s1.offset == s2.offset && s1.len == s2.len && s1.name == s2.name;
-}
-
-// External stable API to access ordinary dex files and CompactDex. This wraps
-// the stable C ABI and handles instance ownership. Thread-compatible but not
-// thread-safe.
+// API for reading ordinary dex files and CompactDex files.
+// It is minimal 1:1 C++ wrapper around the C ABI.
+// See documentation in dex_file_external.h
 class DexFile {
  public:
-  DexFile(DexFile&& dex_file) noexcept {
-    std::swap(ext_dex_file_, dex_file.ext_dex_file_);
-    std::swap(map_, dex_file.map_);
+  struct Method {
+    size_t GetCodeOffset(size_t* out_size = nullptr) const {
+      return g_ADexFile_Method_getCodeOffset(self, out_size);
+    }
+
+    const char* GetName(size_t* out_size = nullptr) const {
+      return g_ADexFile_Method_getName(self, out_size);
+    }
+
+    const char* GetQualifiedName(bool with_params = false, size_t* out_size = nullptr) const {
+      return g_ADexFile_Method_getQualifiedName(self, with_params, out_size);
+    }
+
+    const char* GetClassDescriptor(size_t* out_size = nullptr) const {
+      return g_ADexFile_Method_getClassDescriptor(self, out_size);
+    }
+
+    const ADexFile_Method* const self;
+  };
+
+  struct Error {
+    const char* ToString() const {
+      return g_ADexFile_Error_toString(self);
+    }
+
+    bool Ok() const {
+      return self == ADEXFILE_ERROR_OK;
+    }
+
+    ADexFile_Error Code() {
+      return self;
+    }
+
+    ADexFile_Error const self;
+  };
+
+  static Error Create(const void* address,
+                      size_t size,
+                      size_t* new_size,
+                      const char* location,
+                      /*out*/ std::unique_ptr<DexFile>* out_dex_file) {
+    LoadLibdexfile();
+    ADexFile* adex = nullptr;
+    ADexFile_Error error = g_ADexFile_create(address, size, new_size, location, &adex);
+    if (adex != nullptr) {
+      *out_dex_file = std::unique_ptr<DexFile>(new DexFile{adex});
+    }
+    return Error{error};
   }
 
-  explicit DexFile(std::unique_ptr<DexFile>& dex_file) noexcept {
-    std::swap(ext_dex_file_, dex_file->ext_dex_file_);
-    std::swap(map_, dex_file->map_);
-    dex_file.reset();
+  virtual ~DexFile() {
+    g_ADexFile_destroy(self_);
   }
 
-  virtual ~DexFile();
-
-  // Interprets a chunk of memory as a dex file. As long as *size is too small,
-  // returns nullptr, sets *size to a new size to try again with, and sets
-  // *error_msg. That might happen repeatedly. Also returns nullptr
-  // on error in which case *error_msg is set to a nonempty string.
-  //
-  // location is a string that describes the dex file, and is preferably its
-  // path. It is mostly used to make error messages better, and may be "".
-  //
-  // The caller must retain the memory.
-  static std::unique_ptr<DexFile> OpenFromMemory(const void* addr,
-                                                 size_t* size,
-                                                 const std::string& location,
-                                                 /*out*/ std::string* error_msg);
-
-  // mmaps the given file offset in the open fd and reads a dexfile from there.
-  // Returns nullptr on error in which case *error_msg is set.
-  //
-  // location is a string that describes the dex file, and is preferably its
-  // path. It is mostly used to make error messages better, and may be "".
-  static std::unique_ptr<DexFile> OpenFromFd(int fd,
-                                             off_t offset,
-                                             const std::string& location,
-                                             /*out*/ std::string* error_msg);
-
-  // Given an offset relative to the start of the dex file header, if there is a
-  // method whose instruction range includes that offset then calls the provided
-  // callback with ExtDexFileMethodInfo* (which is live only during the callback).
-  template<typename T /* lambda taking (ExtDexFileMethodInfo*) */>
-  void GetMethodInfoForOffset(int64_t dex_offset, T& callback, uint32_t flags = 0) {
-    auto cb = [](void* ctx, ExtDexFileMethodInfo* info) { (*reinterpret_cast<T*>(ctx))(info); };
-    g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_, dex_offset, flags, cb, &callback);
+  template<typename T /* lambda which takes (const DexFile::Method&) as argument */>
+  inline size_t FindMethodAtOffset(uint32_t dex_offset, T callback) {
+    auto cb = [](void* ctx, const ADexFile_Method* m) { (*reinterpret_cast<T*>(ctx))(Method{m}); };
+    return g_ADexFile_findMethodAtOffset(self_, dex_offset, cb, &callback);
   }
 
-  // Given an offset relative to the start of the dex file header, if there is a
-  // method whose instruction range includes that offset then returns info about
-  // it, otherwise returns a struct with offset == 0. MethodInfo.name receives
-  // the full function signature if with_signature is set, otherwise it gets the
-  // class and method name only.
-  MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature);
-
-  // Call the provided callback for all dex methods.
-  template<typename T /* lambda taking (ExtDexFileMethodInfo*) */>
-  void GetAllMethodInfos(T& callback, uint32_t flags = 0) {
-    auto cb = [](void* ctx, ExtDexFileMethodInfo* info) { (*reinterpret_cast<T*>(ctx))(info); };
-    g_ExtDexFileGetAllMethodInfos(ext_dex_file_, flags, cb, &callback);
+  template<typename T /* lambda which takes (const DexFile::Method&) as argument */>
+  inline size_t ForEachMethod(T callback) {
+    auto cb = [](void* ctx, const ADexFile_Method* m) { (*reinterpret_cast<T*>(ctx))(Method{m}); };
+    return g_ADexFile_forEachMethod(self_, cb, &callback);
   }
 
-  // Returns info structs about all methods in the dex file. MethodInfo.name
-  // receives the full function signature if with_signature is set, otherwise it
-  // gets the class and method name only.
-  std::vector<MethodInfo> GetAllMethodInfos(bool with_signature);
+ protected:
+  explicit DexFile(ADexFile* self) : self_(self) {}
 
- private:
-  static inline MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo* info) {
-    return {
-      .offset = info->addr,
-      .len = info->size,
-      .name = std::string(info->name, info->name_size)
-    };
-  }
-
-  friend bool TryLoadLibdexfileExternal(std::string* error_msg);
-  explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
-  ExtDexFile* ext_dex_file_ = nullptr;  // Owned instance. nullptr only in moved-from zombies.
-  std::unique_ptr<android::base::MappedFile> map_;  // Owned map (if we allocated one).
-
-  // These are initialized by TryLoadLibdexfileExternal.
-  static decltype(ExtDexFileOpenFromMemory)* g_ExtDexFileOpenFromMemory;
-  static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset;
-  static decltype(ExtDexFileGetAllMethodInfos)* g_ExtDexFileGetAllMethodInfos;
-  static decltype(ExtDexFileClose)* g_ExtDexFileClose;
+  ADexFile* const self_;
 
   DISALLOW_COPY_AND_ASSIGN(DexFile);
 };
diff --git a/libdexfile/external/libdexfile_external.map.txt b/libdexfile/external/libdexfile_external.map.txt
deleted file mode 100644
index e9d12fb..0000000
--- a/libdexfile/external/libdexfile_external.map.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-LIBDEXFILE_EXTERNAL_1 {
-  global:
-    ExtDexFileClose;
-    ExtDexFileGetAllMethodInfos;
-    ExtDexFileGetMethodInfoForOffset;
-    ExtDexFileOpenFromMemory;
-  local:
-    *;
-};