Add zip-fd support in dexoptanalyzer

Test: installd flow(manual), oat_file_assistant_test
(cherry-picked from commit 89455b30a30de68d389cef68748a01e13ea1356)
Bug: 67111829
Change-Id: Icf24c671ee060c75ba53932a7ccbe422e0ceb2e0
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 7e9ecab..39c9b99 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -103,6 +103,8 @@
   UsageError("");
   UsageError("  --vdex-fd=number: file descriptor of the vdex file corresponding to the oat file");
   UsageError("");
+  UsageError("  --zip-fd=number: specifies a file descriptor corresponding to the dex file.");
+  UsageError("");
   UsageError("  --downgrade: optional, if the purpose of dexopt is to downgrade the dex file");
   UsageError("       By default, dexopt considers upgrade case.");
   UsageError("");
@@ -175,8 +177,19 @@
         downgrade_ = true;
       } else if (option.starts_with("--oat-fd")) {
         oat_fd_ = std::stoi(option.substr(strlen("--oat-fd=")).ToString(), nullptr, 0);
+        if (oat_fd_ < 0) {
+          Usage("Invalid --oat-fd %d", oat_fd_);
+        }
       } else if (option.starts_with("--vdex-fd")) {
         vdex_fd_ = std::stoi(option.substr(strlen("--vdex-fd=")).ToString(), nullptr, 0);
+        if (vdex_fd_ < 0) {
+          Usage("Invalid --vdex-fd %d", vdex_fd_);
+        }
+      } else if (option.starts_with("--zip-fd")) {
+          zip_fd_ = std::stoi(option.substr(strlen("--zip-fd=")).ToString(), nullptr, 0);
+          if (zip_fd_ < 0) {
+            Usage("Invalid --zip-fd %d", zip_fd_);
+          }
       } else if (option.starts_with("--class-loader-context=")) {
         std::string context_str = option.substr(strlen("--class-loader-context=")).ToString();
         class_loader_context_ = ClassLoaderContext::Create(context_str);
@@ -199,12 +212,6 @@
         Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist.");
       }
     }
-    if (oat_fd_ > 0 && vdex_fd_ < 0) {
-      Usage("A valid --vdex-fd must also be provided with --oat-fd.");
-    }
-    if (oat_fd_ < 0 && vdex_fd_ > 0) {
-      Usage("A valid --oat-fd must also be provided with --vdex-fd.");
-    }
   }
 
   bool CreateRuntime() {
@@ -248,17 +255,12 @@
     std::unique_ptr<Runtime> runtime(Runtime::Current());
 
     std::unique_ptr<OatFileAssistant> oat_file_assistant;
-    if (oat_fd_ != -1 && vdex_fd_ != -1) {
-      oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
-                                                              isa_,
-                                                              false /*load_executable*/,
-                                                              vdex_fd_,
-                                                              oat_fd_);
-    } else {
-      oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
-                                                              isa_,
-                                                              false /*load_executable*/);
-    }
+    oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
+                                                            isa_,
+                                                            false /*load_executable*/,
+                                                            vdex_fd_,
+                                                            oat_fd_,
+                                                            zip_fd_);
     // Always treat elements of the bootclasspath as up-to-date.
     // TODO(calin): this check should be in OatFileAssistant.
     if (oat_file_assistant->IsInBootClassPath()) {
@@ -295,6 +297,8 @@
   std::string image_;
   int oat_fd_ = -1;
   int vdex_fd_ = -1;
+  // File descriptor corresponding to apk, dex_file, or zip.
+  int zip_fd_ = -1;
 };
 
 static int dexoptAnalyze(int argc, char** argv) {
diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc
index 30b4f05..dffb9b4 100644
--- a/runtime/base/file_magic.cc
+++ b/runtime/base/file_magic.cc
@@ -37,19 +37,26 @@
     *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno));
     return File();
   }
-  int n = TEMP_FAILURE_RETRY(read(fd.Fd(), magic, sizeof(*magic)));
-  if (n != sizeof(*magic)) {
-    *error_msg = StringPrintf("Failed to find magic in '%s'", filename);
-    return File();
-  }
-  if (lseek(fd.Fd(), 0, SEEK_SET) != 0) {
-    *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename,
-                              strerror(errno));
+  if (!ReadMagicAndReset(fd.Fd(), magic, error_msg)) {
+    StringPrintf("Error in reading magic from file %s: %s", filename, error_msg->c_str());
     return File();
   }
   return fd;
 }
 
+bool ReadMagicAndReset(int fd, uint32_t* magic, std::string* error_msg) {
+  int n = TEMP_FAILURE_RETRY(read(fd, magic, sizeof(*magic)));
+  if (n != sizeof(*magic)) {
+    *error_msg = StringPrintf("Failed to find magic");
+    return false;
+  }
+  if (lseek(fd, 0, SEEK_SET) != 0) {
+    *error_msg = StringPrintf("Failed to seek to beginning of file : %s", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
 bool IsZipMagic(uint32_t magic) {
   return (('P' == ((magic >> 0) & 0xff)) &&
           ('K' == ((magic >> 8) & 0xff)));
diff --git a/runtime/base/file_magic.h b/runtime/base/file_magic.h
index 1c9effd..e7bd706 100644
--- a/runtime/base/file_magic.h
+++ b/runtime/base/file_magic.h
@@ -27,6 +27,9 @@
 // Open file and read magic number
 File OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg);
 
+// Read magic number and reset pointer to SEEK_SET.
+bool ReadMagicAndReset(int fd, uint32_t* magic, std::string* error_msg);
+
 // Check whether the given magic matches a known file type.
 bool IsZipMagic(uint32_t magic);
 
diff --git a/runtime/dex_file_loader.cc b/runtime/dex_file_loader.cc
index 06e3397..bc92769 100644
--- a/runtime/dex_file_loader.cc
+++ b/runtime/dex_file_loader.cc
@@ -103,11 +103,19 @@
 
 bool DexFileLoader::GetMultiDexChecksums(const char* filename,
                                          std::vector<uint32_t>* checksums,
-                                         std::string* error_msg) {
+                                         std::string* error_msg,
+                                         int zip_fd) {
   CHECK(checksums != nullptr);
   uint32_t magic;
 
-  File fd = OpenAndReadMagic(filename, &magic, error_msg);
+  File fd;
+  if (zip_fd != -1) {
+     if (ReadMagicAndReset(zip_fd, &magic, error_msg)) {
+       fd = File(zip_fd, false /* check_usage */);
+     }
+  } else {
+    fd = OpenAndReadMagic(filename, &magic, error_msg);
+  }
   if (fd.Fd() == -1) {
     DCHECK(!error_msg->empty());
     return false;
diff --git a/runtime/dex_file_loader.h b/runtime/dex_file_loader.h
index 97c886a..1763123 100644
--- a/runtime/dex_file_loader.h
+++ b/runtime/dex_file_loader.h
@@ -50,10 +50,15 @@
   // For .dex files, this is the single header checksum.
   // For zip files, this is the zip entry CRC32 checksum for classes.dex and
   // each additional multidex entry classes2.dex, classes3.dex, etc.
+  // If a valid zip_fd is provided the file content will be read directly from
+  // the descriptor and `filename` will be used as alias for error logging. If
+  // zip_fd is -1, the method will try to open the `filename` and read the
+  // content from it.
   // Return true if the checksums could be found, false otherwise.
   static bool GetMultiDexChecksums(const char* filename,
                                    std::vector<uint32_t>* checksums,
-                                   std::string* error_msg);
+                                   std::string* error_msg,
+                                   int zip_fd = -1);
 
   // Check whether a location denotes a multidex dex file. This is a very simple check: returns
   // whether the string contains the separator character.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 9f6bf69..97b2aec 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -71,15 +71,34 @@
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const InstructionSet isa,
+                                   bool load_executable)
+    : OatFileAssistant(dex_location,
+                       isa, load_executable,
+                       -1 /* vdex_fd */,
+                       -1 /* oat_fd */,
+                       -1 /* zip_fd */) {}
+
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+                                   const InstructionSet isa,
                                    bool load_executable,
                                    int vdex_fd,
-                                   int oat_fd)
+                                   int oat_fd,
+                                   int zip_fd)
     : isa_(isa),
       load_executable_(load_executable),
       odex_(this, /*is_oat_location*/ false),
-      oat_(this, /*is_oat_location*/ true) {
+      oat_(this, /*is_oat_location*/ true),
+      zip_fd_(zip_fd) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
 
+  if (zip_fd < 0) {
+    CHECK_LE(oat_fd, 0) << "zip_fd must be provided with valid oat_fd. zip_fd=" << zip_fd
+      << " oat_fd=" << oat_fd;
+    CHECK_LE(vdex_fd, 0) << "zip_fd must be provided with valid vdex_fd. zip_fd=" << zip_fd
+      << " vdex_fd=" << vdex_fd;;
+  }
+
   // Try to get the realpath for the dex location.
   //
   // This is OK with respect to dalvik cache naming scheme because we never
@@ -113,18 +132,20 @@
   std::string error_msg;
   std::string odex_file_name;
   if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
-    odex_.Reset(odex_file_name, vdex_fd, oat_fd);
+    odex_.Reset(odex_file_name, UseFdToReadFiles(), vdex_fd, oat_fd);
   } else {
     LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
   }
 
-  // Get the oat filename.
-  std::string oat_file_name;
-  if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
-    oat_.Reset(oat_file_name);
-  } else {
-    LOG(WARNING) << "Failed to determine oat file name for dex location "
-        << dex_location_ << ": " << error_msg;
+  if (!UseFdToReadFiles()) {
+    // Get the oat filename.
+    std::string oat_file_name;
+    if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
+      oat_.Reset(oat_file_name, false /* use_fd */);
+    } else {
+      LOG(WARNING) << "Failed to determine oat file name for dex location "
+                   << dex_location_ << ": " << error_msg;
+    }
   }
 
   // Check if the dex directory is writable.
@@ -134,9 +155,11 @@
   size_t pos = dex_location_.rfind('/');
   if (pos == std::string::npos) {
     LOG(WARNING) << "Failed to determine dex file parent directory: " << dex_location_;
-  } else {
+  } else if (!UseFdToReadFiles()) {
+    // We cannot test for parent access when using file descriptors. That's ok
+    // because in this case we will always pick the odex file anyway.
     std::string parent = dex_location_.substr(0, pos);
-    if (access(parent.c_str(), W_OK) == 0 || oat_fd > 0) {
+    if (access(parent.c_str(), W_OK) == 0) {
       dex_parent_writable_ = true;
     } else {
       VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror(errno);
@@ -151,6 +174,10 @@
   }
 }
 
+bool OatFileAssistant::UseFdToReadFiles() {
+  return zip_fd_ >= 0;
+}
+
 bool OatFileAssistant::IsInBootClassPath() {
   // Note: We check the current boot class path, regardless of the ISA
   // specified by the user. This is okay, because the boot class path should
@@ -237,6 +264,9 @@
 OatFileAssistant::MakeUpToDate(bool profile_changed,
                                ClassLoaderContext* class_loader_context,
                                std::string* error_msg) {
+  // The method doesn't use zip_fd_ and directly opens dex files at dex_locations_.
+  CHECK_EQ(-1, zip_fd_) << "MakeUpToDate should not be called with zip_fd";
+
   CompilerFilter::Filter target;
   if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
     return kUpdateNotAttempted;
@@ -869,7 +899,8 @@
     std::string error_msg;
     if (DexFileLoader::GetMultiDexChecksums(dex_location_.c_str(),
                                             &cached_required_dex_checksums_,
-                                            &error_msg)) {
+                                            &error_msg,
+                                            zip_fd_)) {
       required_dex_checksums_found_ = true;
       has_original_dex_files_ = true;
     } else {
@@ -932,7 +963,7 @@
 OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
   // TODO(calin): Document the side effects of class loading when
   // running dalvikvm command line.
-  if (dex_parent_writable_) {
+  if (dex_parent_writable_ || UseFdToReadFiles()) {
     // If the parent of the dex file is writable it means that we can
     // create the odex file. In this case we unconditionally pick the odex
     // as the best oat file. This corresponds to the regular use case when
@@ -1021,26 +1052,28 @@
       std::string error_msg;
       std::string vdex_filename = GetVdexFilename(filename_);
       std::unique_ptr<VdexFile> vdex;
-      if (vdex_fd_ == -1) {
+      if (use_fd_) {
+        if (vdex_fd_ >= 0) {
+          struct stat s;
+          int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s));
+          if (rc == -1) {
+            error_msg = StringPrintf("Failed getting length of the vdex file %s.", strerror(errno));
+          } else {
+            vdex = VdexFile::Open(vdex_fd_,
+                                  s.st_size,
+                                  vdex_filename,
+                                  false /*writable*/,
+                                  false /*low_4gb*/,
+                                  false /* unquicken */,
+                                  &error_msg);
+          }
+        }
+      } else {
         vdex = VdexFile::Open(vdex_filename,
                               false /*writeable*/,
                               false /*low_4gb*/,
                               false /*unquicken*/,
                               &error_msg);
-      } else {
-        struct stat s;
-        int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s));
-        if (rc == -1) {
-          PLOG(WARNING) << "Failed getting length of vdex file";
-        } else {
-          vdex = VdexFile::Open(vdex_fd_,
-                                s.st_size,
-                                vdex_filename,
-                                false /*writable*/,
-                                false /*low_4gb*/,
-                                false /* unquicken */,
-                                &error_msg);
-        }
       }
       if (vdex == nullptr) {
         status_ = kOatCannotOpen;
@@ -1116,16 +1149,18 @@
     load_attempted_ = true;
     if (filename_provided_) {
       std::string error_msg;
-      if (oat_fd_ != -1 && vdex_fd_ != -1) {
-        file_.reset(OatFile::Open(vdex_fd_,
-                                  oat_fd_,
-                                  filename_.c_str(),
-                                  nullptr,
-                                  nullptr,
-                                  oat_file_assistant_->load_executable_,
-                                  false /* low_4gb */,
-                                  oat_file_assistant_->dex_location_.c_str(),
-                                  &error_msg));
+      if (use_fd_) {
+        if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
+          file_.reset(OatFile::Open(vdex_fd_,
+                                    oat_fd_,
+                                    filename_.c_str(),
+                                    nullptr,
+                                    nullptr,
+                                    oat_file_assistant_->load_executable_,
+                                    false /* low_4gb */,
+                                    oat_file_assistant_->dex_location_.c_str(),
+                                    &error_msg));
+        }
       } else {
         file_.reset(OatFile::Open(filename_.c_str(),
                                   filename_.c_str(),
@@ -1203,10 +1238,11 @@
   status_attempted_ = false;
 }
 
-void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, int vdex_fd,
+void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, bool use_fd, int vdex_fd,
                                           int oat_fd) {
   filename_provided_ = true;
   filename_ = filename;
+  use_fd_ = use_fd;
   vdex_fd_ = vdex_fd;
   oat_fd_ = oat_fd;
   Reset();
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 44d8bd8..6c01c1e 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -121,9 +121,17 @@
   // executable code for this dex location.
   OatFileAssistant(const char* dex_location,
                    const InstructionSet isa,
+                   bool load_executable);
+
+  // Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
+  // provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
+  // Otherwise, dex_location will be used to construct necessary filenames.
+  OatFileAssistant(const char* dex_location,
+                   const InstructionSet isa,
                    bool load_executable,
-                   int vdex_fd = -1,
-                   int oat_fd = -1);
+                   int vdex_fd,
+                   int oat_fd,
+                   int zip_fd);
 
   ~OatFileAssistant();
 
@@ -351,7 +359,7 @@
 
     // Clear any cached information and switch to getting info about the oat
     // file with the given filename.
-    void Reset(const std::string& filename, int vdex_fd = -1, int oat_fd = -1);
+    void Reset(const std::string& filename, bool use_fd, int vdex_fd = -1, int oat_fd = -1);
 
     // Release the loaded oat file for runtime use.
     // Returns null if the oat file hasn't been loaded or is out of date.
@@ -390,6 +398,7 @@
 
     int oat_fd_ = -1;
     int vdex_fd_ = -1;
+    bool use_fd_ = false;
 
     bool load_attempted_ = false;
     std::unique_ptr<OatFile> file_;
@@ -420,6 +429,12 @@
   // Return info for the best oat file.
   OatFileInfo& GetBestInfo();
 
+  // Returns true when vdex/oat/odex files should be read from file descriptors.
+  // The method checks the value of zip_fd_, and if the value is valid, returns
+  // true. This is required to have a deterministic behavior around how different
+  // files are being read.
+  bool UseFdToReadFiles();
+
   // Returns true if the dex checksums in the given vdex file are up to date
   // with respect to the dex location. If the dex checksums are not up to
   // date, error_msg is updated with a message describing the problem.
@@ -482,6 +497,9 @@
   OatFileInfo odex_;
   OatFileInfo oat_;
 
+  // File descriptor corresponding to apk, dex file, or zip.
+  int zip_fd_;
+
   // Cached value of the image info.
   // Use the GetImageInfo method rather than accessing these directly.
   // TODO: The image info should probably be moved out of the oat file
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d12c331..bd500eb 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -241,12 +241,14 @@
 
   android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY));
   android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY));
+  android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY));
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
                                       vdex_fd.get(),
-                                      odex_fd.get());
+                                      odex_fd.get(),
+                                      zip_fd.get());
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
@@ -262,37 +264,8 @@
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
-// Case: Passing valid odex fd, however, invalid fd for vdex with
-// the dex file.
-// Expect: The status is kDex2oatFromScratch.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
-  std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
-  std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
-
-  Copy(GetDexSrc1(), dex_location);
-  GenerateOatForTest(dex_location.c_str(),
-                     odex_location.c_str(),
-                     CompilerFilter::kSpeed,
-                     true,
-                     false,
-                     false);
-
-  android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY));
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-                                      kRuntimeISA,
-                                      false,
-                                      -1,
-                                      odex_fd.get());
-  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
-  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
-}
-
-// Case: Passing valid vdex fd, however, invalid fd for odex with
-// the dex file.
-// Expect: The status is kDex2oatFromScratch.
+// Case: Passing invalid odex fd and valid vdex and zip fds.
+// Expect: The status should be kDex2OatForBootImage.
 TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
   std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
   std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
@@ -307,35 +280,71 @@
                      false);
 
   android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY));
+  android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY));
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
                                       vdex_fd.get(),
-                                      -1);
-  // Even though the vdex file is up to date, because we don't have the oat
-  // file, we can't know that the vdex depends on the boot image and is up to
-  // date with respect to the boot image. Instead we must assume the vdex file
-  // depends on the boot image and is out of date with respect to the boot
-  // image.
+                                      -1 /* oat_fd */,
+                                      zip_fd.get());
   EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus());
   EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
-// Case: Passing invalid vdex and odex fd with the dex file.
+// Case: Passing invalid vdex fd and valid odex and zip fds.
+// Expect: The status should be kDex2OatFromScratch.
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
+  std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+  std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(),
+                     odex_location.c_str(),
+                     CompilerFilter::kSpeed,
+                     true,
+                     false,
+                     false);
+
+  android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY));
+  android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY));
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                      kRuntimeISA,
+                                      false,
+                                      -1 /* vdex_fd */,
+                                      odex_fd.get(),
+                                      zip_fd.get());
+
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: Passing invalid vdex and odex fd with valid zip fd.
 // Expect: The status is kDex2oatFromScratch.
 TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
   std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
 
   Copy(GetDexSrc1(), dex_location);
 
+  android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY));
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
-                                      -1,
-                                      -1);
+                                      -1 /* vdex_fd */,
+                                      -1 /* oat_fd */,
+                                      zip_fd);
   EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());