Fix dex2oat failure when --zip-fd actually points to raw dexfile.

Installd always pass --zip-fd to dex2oat when perform dexopt,
however some apps use RawDexFile rather than ZipArchive as
secondary dex, so check whether it's a RawDexfile before try
it as ZipArchive. This make sure dex2oat success when secondary
dex is a raw dexfile.

Test: m test-art-host-gtest-oat_writer_test
Test: m test-art-host-gtest-dex2oat_test

Change-Id: I6da98845cdf64acee1865dd997616e8214417315
Signed-off-by: wangshumin <wangshumin@xiaomi.com>
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8713886..ccb89a6 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2701,8 +2701,8 @@
       }
     } else if (zip_fd_ != -1) {
       DCHECK_EQ(oat_writers_.size(), 1u);
-      if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false),
-                                                    zip_location_.c_str())) {
+      if (!oat_writers_[0]->AddDexFileSource(File(zip_fd_, /* check_usage */ false),
+                                             zip_location_.c_str())) {
         return false;
       }
     } else if (oat_writers_.size() > 1u) {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 372ee35..45b7e4a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -34,6 +34,7 @@
 #include "base/mutex-inl.h"
 #include "base/string_view_cpp20.h"
 #include "base/utils.h"
+#include "base/zip_archive.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/base64_test_util.h"
 #include "dex/bytecode_utils.h"
@@ -2249,6 +2250,40 @@
   ASSERT_TRUE(odex_file != nullptr);
 }
 
+TEST_F(Dex2oatTest, DexFileFd) {
+  std::string error_msg;
+  std::string zip_location = GetTestDexFileName("Main");
+  std::unique_ptr<File> zip_file(OS::OpenFileForReading(zip_location.c_str()));
+  ASSERT_NE(-1, zip_file->Fd());
+
+  std::unique_ptr<ZipArchive> zip_archive(
+      ZipArchive::OpenFromFd(zip_file->Release(), zip_location.c_str(), &error_msg));
+  ASSERT_TRUE(zip_archive != nullptr);
+
+  std::string entry_name = DexFileLoader::GetMultiDexClassesDexName(0);
+  std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg));
+  ASSERT_TRUE(entry != nullptr);
+
+  ScratchFile dex_file;
+  const std::string& dex_location = dex_file.GetFilename();
+  const std::string base_oat_name = GetScratchDir() + "/base.oat";
+
+  bool success = entry->ExtractToFile(*(dex_file.GetFile()), &error_msg);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(0, lseek(dex_file.GetFd(), 0, SEEK_SET));
+
+  std::vector<std::string> extra_args{
+      StringPrintf("--zip-fd=%d", dex_file.GetFd()),
+      "--zip-location=" + dex_location,
+  };
+  ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                  base_oat_name,
+                                  CompilerFilter::Filter::kQuicken,
+                                  extra_args,
+                                  /*expect_success=*/ true,
+                                  /*use_fd=*/ false,
+                                  /*use_zip_fd=*/ true));
+}
 
 TEST_F(Dex2oatTest, AppImageResolveStrings) {
   using Hotness = ProfileCompilationInfo::MethodHotness;
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 16e75d0..055f246 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -504,21 +504,38 @@
                                  const char* location,
                                  CreateTypeLookupTable create_type_lookup_table) {
   DCHECK(write_state_ == WriteState::kAddingDexFileSources);
-  uint32_t magic;
-  std::string error_msg;
-  File fd = OpenAndReadMagic(filename, &magic, &error_msg);
+  File fd(filename, O_RDONLY, /* check_usage= */ false);
   if (fd.Fd() == -1) {
-    PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'";
+    PLOG(ERROR) << "Failed to open dex file: '" << filename << "'";
     return false;
-  } else if (DexFileLoader::IsMagicValid(magic)) {
+  }
+
+  return AddDexFileSource(std::move(fd), location, create_type_lookup_table);
+}
+
+// Add dex file source(s) from a file specified by a file handle.
+// Note: The `dex_file_fd` specifies a plain dex file or a zip file.
+bool OatWriter::AddDexFileSource(File&& dex_file_fd,
+                                 const char* location,
+                                 CreateTypeLookupTable create_type_lookup_table) {
+  DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+  std::string error_msg;
+  uint32_t magic;
+  if (!ReadMagicAndReset(dex_file_fd.Fd(), &magic, &error_msg)) {
+    LOG(ERROR) << "Failed to read magic number from dex file '" << location << "': " << error_msg;
+    return false;
+  }
+  if (DexFileLoader::IsMagicValid(magic)) {
     uint8_t raw_header[sizeof(DexFile::Header)];
-    const UnalignedDexFileHeader* header = GetDexFileHeader(&fd, raw_header, location);
+    const UnalignedDexFileHeader* header = GetDexFileHeader(&dex_file_fd, raw_header, location);
     if (header == nullptr) {
+      LOG(ERROR) << "Failed to get DexFileHeader from file descriptor for '"
+          << location << "': " << error_msg;
       return false;
     }
     // The file is open for reading, not writing, so it's OK to let the File destructor
     // close it without checking for explicit Close(), so pass checkUsage = false.
-    raw_dex_files_.emplace_back(new File(fd.Release(), location, /* checkUsage */ false));
+    raw_dex_files_.emplace_back(new File(dex_file_fd.Release(), location, /* checkUsage */ false));
     oat_dex_files_.emplace_back(/* OatDexFile */
         location,
         DexFileSource(raw_dex_files_.back().get()),
@@ -526,48 +543,36 @@
         header->checksum_,
         header->file_size_);
   } else if (IsZipMagic(magic)) {
-    if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) {
+    zip_archives_.emplace_back(ZipArchive::OpenFromFd(dex_file_fd.Release(), location, &error_msg));
+    ZipArchive* zip_archive = zip_archives_.back().get();
+    if (zip_archive == nullptr) {
+      LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': "
+          << error_msg;
+      return false;
+    }
+    for (size_t i = 0; ; ++i) {
+      std::string entry_name = DexFileLoader::GetMultiDexClassesDexName(i);
+      std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg));
+      if (entry == nullptr) {
+        break;
+      }
+      zipped_dex_files_.push_back(std::move(entry));
+      zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location));
+      const char* full_location = zipped_dex_file_locations_.back().c_str();
+      // We override the checksum from header with the CRC from ZIP entry.
+      oat_dex_files_.emplace_back(/* OatDexFile */
+          full_location,
+          DexFileSource(zipped_dex_files_.back().get()),
+          create_type_lookup_table,
+          zipped_dex_files_.back()->GetCrc32(),
+          zipped_dex_files_.back()->GetUncompressedLength());
+    }
+    if (zipped_dex_file_locations_.empty()) {
+      LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg;
       return false;
     }
   } else {
-    LOG(ERROR) << "Expected valid zip or dex file: '" << filename << "'";
-    return false;
-  }
-  return true;
-}
-
-// Add dex file source(s) from a zip file specified by a file handle.
-bool OatWriter::AddZippedDexFilesSource(File&& zip_fd,
-                                        const char* location,
-                                        CreateTypeLookupTable create_type_lookup_table) {
-  DCHECK(write_state_ == WriteState::kAddingDexFileSources);
-  std::string error_msg;
-  zip_archives_.emplace_back(ZipArchive::OpenFromFd(zip_fd.Release(), location, &error_msg));
-  ZipArchive* zip_archive = zip_archives_.back().get();
-  if (zip_archive == nullptr) {
-    LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': "
-        << error_msg;
-    return false;
-  }
-  for (size_t i = 0; ; ++i) {
-    std::string entry_name = DexFileLoader::GetMultiDexClassesDexName(i);
-    std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg));
-    if (entry == nullptr) {
-      break;
-    }
-    zipped_dex_files_.push_back(std::move(entry));
-    zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location));
-    const char* full_location = zipped_dex_file_locations_.back().c_str();
-    // We override the checksum from header with the CRC from ZIP entry.
-    oat_dex_files_.emplace_back(/* OatDexFile */
-        full_location,
-        DexFileSource(zipped_dex_files_.back().get()),
-        create_type_lookup_table,
-        zipped_dex_files_.back()->GetCrc32(),
-        zipped_dex_files_.back()->GetUncompressedLength());
-  }
-  if (zipped_dex_file_locations_.empty()) {
-    LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg;
+    LOG(ERROR) << "Expected valid zip or dex file: '" << location << "'";
     return false;
   }
   return true;
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index dd11b87..f83f077 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -127,7 +127,6 @@
 
   // To produce a valid oat file, the user must first add sources with any combination of
   //   - AddDexFileSource(),
-  //   - AddZippedDexFilesSource(),
   //   - AddRawDexFileSource(),
   //   - AddVdexDexFilesSource().
   // Then the user must call in order
@@ -148,9 +147,10 @@
       const char* filename,
       const char* location,
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
-  // Add dex file source(s) from a zip file specified by a file handle.
-  bool AddZippedDexFilesSource(
-      File&& zip_fd,
+  // Add dex file source(s) from a file specified by a file handle.
+  // Note: The `dex_file_fd` specifies a plain dex file or a zip file.
+  bool AddDexFileSource(
+      File&& dex_file_fd,
       const char* location,
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
   // Add dex file source from raw memory.
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 6d6d114..1b27bce 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -147,18 +147,19 @@
 
   bool WriteElf(File* vdex_file,
                 File* oat_file,
-                File&& zip_fd,
+                File&& dex_file_fd,
                 const char* location,
                 SafeMap<std::string, std::string>& key_value_store,
                 bool verify,
-                CopyOption copy) {
+                CopyOption copy,
+                ProfileCompilationInfo* profile_compilation_info = nullptr) {
     TimingLogger timings("WriteElf", false, false);
     ClearBootImageOption();
     OatWriter oat_writer(*compiler_options_,
                          &timings,
-                         /*profile_compilation_info*/nullptr,
+                         profile_compilation_info,
                          CompactDexLevel::kCompactDexLevelNone);
-    if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
+    if (!oat_writer.AddDexFileSource(std::move(dex_file_fd), location)) {
       return false;
     }
     return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify, copy);
@@ -262,6 +263,56 @@
     return true;
   }
 
+  void CheckOatWriteResult(ScratchFile& oat_file,
+                           ScratchFile& vdex_file,
+                           std::vector<std::unique_ptr<const DexFile>>& input_dexfiles,
+                           const unsigned int expected_oat_dexfile_count,
+                           bool low_4gb) {
+    ASSERT_EQ(expected_oat_dexfile_count, input_dexfiles.size());
+
+    std::string error_msg;
+    std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/*zip_fd=*/ -1,
+                                                           oat_file.GetFilename(),
+                                                           oat_file.GetFilename(),
+                                                           /*executable=*/ false,
+                                                           low_4gb,
+                                                           &error_msg));
+    ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
+    ASSERT_EQ(expected_oat_dexfile_count, opened_oat_file->GetOatDexFiles().size());
+
+    if (low_4gb) {
+      uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin());
+      EXPECT_EQ(begin, static_cast<uint32_t>(begin));
+    }
+
+    for (uint32_t i = 0; i <  input_dexfiles.size(); i++) {
+      const std::unique_ptr<const DexFile>& dex_file_data = input_dexfiles[i];
+      std::unique_ptr<const DexFile> opened_dex_file =
+          opened_oat_file->GetOatDexFiles()[i]->OpenDexFile(&error_msg);
+
+      ASSERT_EQ(opened_oat_file->GetOatDexFiles()[i]->GetDexFileLocationChecksum(),
+                dex_file_data->GetHeader().checksum_);
+
+      ASSERT_EQ(dex_file_data->GetHeader().file_size_, opened_dex_file->GetHeader().file_size_);
+      ASSERT_EQ(0, memcmp(&dex_file_data->GetHeader(),
+                          &opened_dex_file->GetHeader(),
+                          dex_file_data->GetHeader().file_size_));
+      ASSERT_EQ(dex_file_data->GetLocation(), opened_dex_file->GetLocation());
+    }
+    const VdexFile::DexSectionHeader &vdex_header =
+        opened_oat_file->GetVdexFile()->GetDexSectionHeader();
+    if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) {
+      // If quickening is enabled we will always write the table since there is no special logic
+      // that checks for all methods not being quickened (not worth the complexity).
+      ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u);
+    }
+
+    int64_t actual_vdex_size = vdex_file.GetFile()->GetLength();
+    ASSERT_GE(actual_vdex_size, 0);
+    ASSERT_EQ(dchecked_integral_cast<uint64_t>(actual_vdex_size),
+              opened_oat_file->GetVdexFile()->GetComputedFileSize());
+  }
+
   void TestDexFileInput(bool verify, bool low_4gb, bool use_profile);
   void TestZipFileInput(bool verify, CopyOption copy);
   void TestZipFileInputWithEmptyDex();
@@ -549,6 +600,8 @@
   TimingLogger timings("OatTest::DexFileInput", false, false);
 
   std::vector<const char*> input_filenames;
+  std::vector<std::unique_ptr<const DexFile>> input_dexfiles;
+  std::vector<const ScratchFile*> scratch_files;
 
   ScratchFile dex_file1;
   TestDexFileBuilder builder1;
@@ -564,6 +617,8 @@
   success = dex_file1.GetFile()->Flush() == 0;
   ASSERT_TRUE(success);
   input_filenames.push_back(dex_file1.GetFilename().c_str());
+  input_dexfiles.push_back(std::move(dex_file1_data));
+  scratch_files.push_back(&dex_file1);
 
   ScratchFile dex_file2;
   TestDexFileBuilder builder2;
@@ -579,73 +634,75 @@
   success = dex_file2.GetFile()->Flush() == 0;
   ASSERT_TRUE(success);
   input_filenames.push_back(dex_file2.GetFilename().c_str());
+  input_dexfiles.push_back(std::move(dex_file2_data));
+  scratch_files.push_back(&dex_file2);
 
-  ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
-  std::unique_ptr<ProfileCompilationInfo>
-      profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
-  success = WriteElf(tmp_vdex.GetFile(),
-                     tmp_oat.GetFile(),
-                     input_filenames,
-                     key_value_store,
-                     verify,
-                     CopyOption::kOnlyIfCompressed,
-                     profile_compilation_info.get());
+  {
+    // Test using the AddDexFileSource() interface with the dex files.
+    ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
+    std::unique_ptr<ProfileCompilationInfo>
+        profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
+    success = WriteElf(tmp_vdex.GetFile(),
+                       tmp_oat.GetFile(),
+                       input_filenames,
+                       key_value_store,
+                       verify,
+                       CopyOption::kOnlyIfCompressed,
+                       profile_compilation_info.get());
 
-  // In verify mode, we expect failure.
-  if (verify) {
-    ASSERT_FALSE(success);
-    return;
+    // In verify mode, we expect failure.
+    if (verify) {
+      ASSERT_FALSE(success);
+      return;
+    }
+
+    ASSERT_TRUE(success);
+
+    CheckOatWriteResult(tmp_oat,
+                        tmp_vdex,
+                        input_dexfiles,
+                        /* oat_dexfile_count */ 2,
+                        low_4gb);
   }
 
-  ASSERT_TRUE(success);
+  {
+    // Test using the AddDexFileSource() interface with the dexfile1's fd.
+    // Only need one input dexfile.
+    std::vector<std::unique_ptr<const DexFile>> input_dexfiles2;
+    input_dexfiles2.push_back(std::move(input_dexfiles[0]));
+    const ScratchFile* dex_file = scratch_files[0];
+    File dex_file_fd(DupCloexec(dex_file->GetFd()), /*check_usage=*/ false);
 
-  std::string error_msg;
-  std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/*zip_fd=*/ -1,
-                                                         tmp_oat.GetFilename(),
-                                                         tmp_oat.GetFilename(),
-                                                         /*executable=*/ false,
-                                                         low_4gb,
-                                                         &error_msg));
-  ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
-  if (low_4gb) {
-    uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin());
-    EXPECT_EQ(begin, static_cast<uint32_t>(begin));
+    ASSERT_NE(-1, dex_file_fd.Fd());
+    ASSERT_EQ(0, lseek(dex_file_fd.Fd(), 0, SEEK_SET));
+
+    ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
+    std::unique_ptr<ProfileCompilationInfo>
+        profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
+    success = WriteElf(tmp_vdex.GetFile(),
+                       tmp_oat.GetFile(),
+                       std::move(dex_file_fd),
+                       dex_file->GetFilename().c_str(),
+                       key_value_store,
+                       verify,
+                       CopyOption::kOnlyIfCompressed,
+                       profile_compilation_info.get());
+
+    // In verify mode, we expect failure.
+    if (verify) {
+      ASSERT_FALSE(success);
+      return;
+    }
+
+    ASSERT_TRUE(success);
+
+    CheckOatWriteResult(tmp_oat,
+                        tmp_vdex,
+                        input_dexfiles2,
+                        /* oat_dexfile_count */ 1,
+                        low_4gb);
   }
-  ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
-  std::unique_ptr<const DexFile> opened_dex_file1 =
-      opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
-  std::unique_ptr<const DexFile> opened_dex_file2 =
-      opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
-
-  ASSERT_EQ(opened_oat_file->GetOatDexFiles()[0]->GetDexFileLocationChecksum(),
-            dex_file1_data->GetHeader().checksum_);
-  ASSERT_EQ(opened_oat_file->GetOatDexFiles()[1]->GetDexFileLocationChecksum(),
-            dex_file2_data->GetHeader().checksum_);
-
-  ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
-  ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
-                      &opened_dex_file1->GetHeader(),
-                      dex_file1_data->GetHeader().file_size_));
-  ASSERT_EQ(dex_file1_data->GetLocation(), opened_dex_file1->GetLocation());
-
-  ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
-  ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
-                      &opened_dex_file2->GetHeader(),
-                      dex_file2_data->GetHeader().file_size_));
-  ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation());
-
-  const VdexFile::DexSectionHeader &vdex_header =
-      opened_oat_file->GetVdexFile()->GetDexSectionHeader();
-  if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) {
-    // If quickening is enabled we will always write the table since there is no special logic that
-    // checks for all methods not being quickened (not worth the complexity).
-    ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u);
-  }
-
-  int64_t actual_vdex_size = tmp_vdex.GetFile()->GetLength();
-  ASSERT_GE(actual_vdex_size, 0);
-  ASSERT_EQ((uint64_t) actual_vdex_size, opened_oat_file->GetVdexFile()->GetComputedFileSize());
 }
 
 TEST_F(OatTest, DexFileInputCheckOutput) {
@@ -759,9 +816,10 @@
   }
 
   {
-    // Test using the AddZipDexFileSource() interface with the zip file handle.
+    // Test using the AddDexFileSource() interface with the zip file handle.
     File zip_fd(DupCloexec(zip_file.GetFd()), /*check_usage=*/ false);
     ASSERT_NE(-1, zip_fd.Fd());
+    ASSERT_EQ(0, lseek(zip_fd.Fd(), 0, SEEK_SET));
 
     ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
     success = WriteElf(tmp_vdex.GetFile(),