Enable update_engine to access OTA package via file descriptor

Due to the restriction of Treble, update_engine cannot access to OTA
packages located on non-core domain area.
(e.g. /data/vendor/upgrade/xxx.zip)
To solve such problem, update_engine needs to have a new interface
which accepts a file descriptor (FD) of OTA package file instead of
its URI and to read package file while updating via FD.

Test: Manual update
Bug: 130209137
Change-Id: Ieb7173dc958ba3eb21af708e616ef7078cd17b3e
diff --git a/binder_bindings/android/os/IUpdateEngine.aidl b/binder_bindings/android/os/IUpdateEngine.aidl
index c0e29f5..cde05be 100644
--- a/binder_bindings/android/os/IUpdateEngine.aidl
+++ b/binder_bindings/android/os/IUpdateEngine.aidl
@@ -26,6 +26,11 @@
                     in long payload_size,
                     in String[] headerKeyValuePairs);
   /** @hide */
+  void applyPayloadFd(in FileDescriptor fd,
+                      in long payload_offset,
+                      in long payload_size,
+                      in String[] headerKeyValuePairs);
+  /** @hide */
   boolean bind(IUpdateEngineCallback callback);
   /** @hide */
   boolean unbind(IUpdateEngineCallback callback);
diff --git a/binder_service_android.cc b/binder_service_android.cc
index 137694a..1799438 100644
--- a/binder_service_android.cc
+++ b/binder_service_android.cc
@@ -16,14 +16,18 @@
 
 #include "update_engine/binder_service_android.h"
 
+#include <android-base/unique_fd.h>
 #include <base/bind.h>
 #include <base/logging.h>
 #include <binderwrapper/binder_wrapper.h>
 #include <brillo/errors/error.h>
 #include <utils/String8.h>
 
+using android::base::unique_fd;
 using android::binder::Status;
 using android::os::IUpdateEngineCallback;
+using std::string;
+using std::vector;
 using update_engine::UpdateEngineStatus;
 
 namespace {
@@ -94,9 +98,9 @@
     const android::String16& url,
     int64_t payload_offset,
     int64_t payload_size,
-    const std::vector<android::String16>& header_kv_pairs) {
-  const std::string payload_url{android::String8{url}.string()};
-  std::vector<std::string> str_headers;
+    const vector<android::String16>& header_kv_pairs) {
+  const string payload_url{android::String8{url}.string()};
+  vector<string> str_headers;
   str_headers.reserve(header_kv_pairs.size());
   for (const auto& header : header_kv_pairs) {
     str_headers.emplace_back(android::String8{header}.string());
@@ -110,6 +114,25 @@
   return Status::ok();
 }
 
+Status BinderUpdateEngineAndroidService::applyPayloadFd(
+    const ::android::base::unique_fd& fd,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<android::String16>& header_kv_pairs) {
+  vector<string> str_headers;
+  str_headers.reserve(header_kv_pairs.size());
+  for (const auto& header : header_kv_pairs) {
+    str_headers.emplace_back(android::String8{header}.string());
+  }
+
+  brillo::ErrorPtr error;
+  if (!service_delegate_->ApplyPayload(
+          fd.get(), payload_offset, payload_size, str_headers, &error)) {
+    return ErrorPtrToStatus(error);
+  }
+  return Status::ok();
+}
+
 Status BinderUpdateEngineAndroidService::suspend() {
   brillo::ErrorPtr error;
   if (!service_delegate_->SuspendUpdate(&error))
diff --git a/binder_service_android.h b/binder_service_android.h
index d8c4e9c..ec4a93b 100644
--- a/binder_service_android.h
+++ b/binder_service_android.h
@@ -53,6 +53,11 @@
       int64_t payload_offset,
       int64_t payload_size,
       const std::vector<android::String16>& header_kv_pairs) override;
+  android::binder::Status applyPayloadFd(
+      const ::android::base::unique_fd& fd,
+      int64_t payload_offset,
+      int64_t payload_size,
+      const std::vector<android::String16>& header_kv_pairs) override;
   android::binder::Status bind(
       const android::sp<android::os::IUpdateEngineCallback>& callback,
       bool* return_value) override;
diff --git a/common/file_fetcher.cc b/common/file_fetcher.cc
index 3836e54..7134fd6 100644
--- a/common/file_fetcher.cc
+++ b/common/file_fetcher.cc
@@ -43,8 +43,9 @@
 // static
 bool FileFetcher::SupportedUrl(const string& url) {
   // Note that we require the file path to start with a "/".
-  return base::StartsWith(
-      url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
+  return (
+      base::StartsWith(url, "file:///", base::CompareCase::INSENSITIVE_ASCII) ||
+      base::StartsWith(url, "fd://", base::CompareCase::INSENSITIVE_ASCII));
 }
 
 FileFetcher::~FileFetcher() {
@@ -67,12 +68,20 @@
     return;
   }
 
-  string file_path = url.substr(strlen("file://"));
-  stream_ =
-      brillo::FileStream::Open(base::FilePath(file_path),
-                               brillo::Stream::AccessMode::READ,
-                               brillo::FileStream::Disposition::OPEN_EXISTING,
-                               nullptr);
+  string file_path;
+
+  if (base::StartsWith(url, "fd://", base::CompareCase::INSENSITIVE_ASCII)) {
+    int fd = std::stoi(url.substr(strlen("fd://")));
+    file_path = url;
+    stream_ = brillo::FileStream::FromFileDescriptor(fd, false, nullptr);
+  } else {
+    file_path = url.substr(strlen("file://"));
+    stream_ =
+        brillo::FileStream::Open(base::FilePath(file_path),
+                                 brillo::Stream::AccessMode::READ,
+                                 brillo::FileStream::Disposition::OPEN_EXISTING,
+                                 nullptr);
+  }
 
   if (!stream_) {
     LOG(ERROR) << "Couldn't open " << file_path;
@@ -183,5 +192,4 @@
   transfer_in_progress_ = false;
   transfer_paused_ = false;
 }
-
 }  // namespace chromeos_update_engine
diff --git a/common/utils.cc b/common/utils.cc
index 34d97a2..e7b6975 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -1064,6 +1064,16 @@
   }
 }
 
+string GetFilePath(int fd) {
+  base::FilePath proc("/proc/self/fd/" + std::to_string(fd));
+  base::FilePath file_name;
+
+  if (!base::ReadSymbolicLink(proc, &file_name)) {
+    return "not found";
+  }
+  return file_name.value();
+}
+
 }  // namespace utils
 
 }  // namespace chromeos_update_engine
diff --git a/common/utils.h b/common/utils.h
index 9160d9f..9dca9e8 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -304,6 +304,9 @@
 // reboot. Returns whether it succeeded getting the boot_id.
 bool GetBootId(std::string* boot_id);
 
+// This function gets the file path of the file pointed to by FileDiscriptor.
+std::string GetFilePath(int fd);
+
 // Divide |x| by |y| and round up to the nearest integer.
 constexpr uint64_t DivRoundUp(uint64_t x, uint64_t y) {
   return (x + y - 1) / y;
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index 7d1c59e..b4ac2f5 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -507,4 +507,13 @@
   ExpectInvalidParseRollbackKeyVersion("1.99999");
 }
 
+TEST(UtilsTest, GetFilePathTest) {
+  test_utils::ScopedTempFile file;
+  int fd = HANDLE_EINTR(open(file.path().c_str(), O_RDONLY));
+  EXPECT_GE(fd, 0);
+  EXPECT_EQ(file.path(), utils::GetFilePath(fd));
+  EXPECT_EQ("not found", utils::GetFilePath(-1));
+  IGNORE_EINTR(close(fd));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 2e7b6d4..766b27c 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -19,6 +19,7 @@
 #include <base/format_macros.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 
 #include "update_engine/common/utils.h"
@@ -80,11 +81,18 @@
         base::StringPrintf(", system_version: %s", system_version.c_str());
   }
 
+  string url_str = download_url;
+  if (base::StartsWith(
+          url_str, "fd://", base::CompareCase::INSENSITIVE_ASCII)) {
+    int fd = std::stoi(url_str.substr(strlen("fd://")));
+    url_str = utils::GetFilePath(fd);
+  }
+
   LOG(INFO) << "InstallPlan: " << (is_resume ? "resume" : "new_update")
             << version_str
             << ", source_slot: " << BootControlInterface::SlotName(source_slot)
             << ", target_slot: " << BootControlInterface::SlotName(target_slot)
-            << ", url: " << download_url << payloads_str << partitions_str
+            << ", url: " << url_str << payloads_str << partitions_str
             << ", hash_checks_mandatory: "
             << utils::ToString(hash_checks_mandatory)
             << ", powerwash_required: " << utils::ToString(powerwash_required)
diff --git a/service_delegate_android_interface.h b/service_delegate_android_interface.h
index 5267bb0..6bd75b6 100644
--- a/service_delegate_android_interface.h
+++ b/service_delegate_android_interface.h
@@ -47,6 +47,13 @@
       const std::vector<std::string>& key_value_pair_headers,
       brillo::ErrorPtr* error) = 0;
 
+  virtual bool ApplyPayload(
+      int fd,
+      int64_t payload_offset,
+      int64_t payload_size,
+      const std::vector<std::string>& key_value_pair_headers,
+      brillo::ErrorPtr* error) = 0;
+
   // Suspend an ongoing update. Returns true if there was an update ongoing and
   // it was suspended. In case of failure, it returns false and sets |error|
   // accordingly.
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 1cc8505..97c53ec 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -22,6 +22,7 @@
 #include <utility>
 
 #include <android-base/properties.h>
+#include <android-base/unique_fd.h>
 #include <base/bind.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
@@ -55,6 +56,7 @@
 #include "update_engine/libcurl_http_fetcher.h"
 #endif
 
+using android::base::unique_fd;
 using base::Bind;
 using base::Time;
 using base::TimeDelta;
@@ -288,6 +290,19 @@
   return true;
 }
 
+bool UpdateAttempterAndroid::ApplyPayload(
+    int fd,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<string>& key_value_pair_headers,
+    brillo::ErrorPtr* error) {
+  payload_fd_.reset(dup(fd));
+  const string payload_url = "fd://" + std::to_string(payload_fd_.get());
+
+  return ApplyPayload(
+      payload_url, payload_offset, payload_size, key_value_pair_headers, error);
+}
+
 bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
   if (!processor_->IsRunning())
     return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
@@ -583,6 +598,7 @@
       (error_code == ErrorCode::kSuccess ? UpdateStatus::UPDATED_NEED_REBOOT
                                          : UpdateStatus::IDLE);
   SetStatusAndNotify(new_status);
+  payload_fd_.reset();
 
   // The network id is only applicable to one download attempt and once it's
   // done the network id should not be re-used anymore.
diff --git a/update_attempter_android.h b/update_attempter_android.h
index c4710ad..7e1949d 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -23,6 +23,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
 #include <base/time/time.h>
 
 #include "update_engine/client_library/include/update_engine/update_status.h"
@@ -65,6 +66,11 @@
                     int64_t payload_size,
                     const std::vector<std::string>& key_value_pair_headers,
                     brillo::ErrorPtr* error) override;
+  bool ApplyPayload(int fd,
+                    int64_t payload_offset,
+                    int64_t payload_size,
+                    const std::vector<std::string>& key_value_pair_headers,
+                    brillo::ErrorPtr* error) override;
   bool SuspendUpdate(brillo::ErrorPtr* error) override;
   bool ResumeUpdate(brillo::ErrorPtr* error) override;
   bool CancelUpdate(brillo::ErrorPtr* error) override;
@@ -191,6 +197,8 @@
 
   std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
 
+  ::android::base::unique_fd payload_fd_;
+
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
 };