Add update_engine_stable_client

This is a update_engine console client installed to APEXes so that
scripts can invoke on. This client operates on the IUpdateEngineStable
service.

Test: pass
Bug: 160996544

Change-Id: I0672b7bd1ccd87e35ffb99d7a66e63dffaf7df24
diff --git a/Android.bp b/Android.bp
index b8cff0a..1076c52 100644
--- a/Android.bp
+++ b/Android.bp
@@ -729,3 +729,12 @@
         },
     },
 }
+
+// update_engine header library
+cc_library_headers {
+    name: "libupdate_engine_headers",
+    export_include_dirs: ["."],
+    apex_available: [
+        "com.android.gki.*",
+    ],
+}
diff --git a/stable/Android.bp b/stable/Android.bp
index 01dd88b..337ae96 100644
--- a/stable/Android.bp
+++ b/stable/Android.bp
@@ -37,3 +37,29 @@
         },
     },
 }
+
+// update_engine_stable_client (type: executable)
+// ========================================================
+// update_engine console client installed to APEXes
+cc_binary {
+    name: "update_engine_stable_client",
+
+    header_libs: [
+        "libupdate_engine_headers",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libgflags",
+        "libupdate_engine_stable-ndk_platform",
+    ],
+    srcs: [
+        "update_engine_stable_client.cc",
+    ],
+    apex_available: [
+        "com.android.gki.*",
+    ],
+}
diff --git a/stable/update_engine_stable_client.cc b/stable/update_engine_stable_client.cc
new file mode 100644
index 0000000..c466c42
--- /dev/null
+++ b/stable/update_engine_stable_client.cc
@@ -0,0 +1,190 @@
+//
+// Copyright (C) 2020 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.
+//
+
+// update_engine console client installed to APEXes for scripts to invoke
+// directly. Uses the stable API.
+
+#include <fcntl.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <aidl/android/os/BnUpdateEngineStableCallback.h>
+#include <aidl/android/os/IUpdateEngineStable.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/binder_ibinder.h>
+#include <common/error_code.h>
+#include <gflags/gflags.h>
+#include <utils/StrongPointer.h>
+
+namespace chromeos_update_engine::internal {
+
+DEFINE_string(payload,
+              "file:///path/to/payload.bin",
+              "The file URI to the update payload to use, or path to the file");
+DEFINE_int64(offset,
+             0,
+             "The offset in the payload where the CrAU update starts.");
+DEFINE_int64(size,
+             0,
+             "The size of the CrAU part of the payload. If 0 is passed, it "
+             "will be autodetected.");
+DEFINE_string(headers,
+              "",
+              "A list of key-value pairs, one element of the list per line.");
+
+int Exit(int return_code) {
+  LOG(INFO) << "Exit: " << return_code;
+  exit(return_code);
+  __builtin_unreachable();
+}
+// Called whenever the UpdateEngine daemon dies.
+void UpdateEngineServiceDied(void*) {
+  LOG(ERROR) << "UpdateEngineService died.";
+  Exit(EX_SOFTWARE);
+}
+
+class UpdateEngineClientAndroid {
+ public:
+  UpdateEngineClientAndroid() = default;
+  int Run();
+
+ private:
+  class UECallback : public aidl::android::os::BnUpdateEngineStableCallback {
+   public:
+    UECallback() = default;
+
+    // android::os::BnUpdateEngineStableCallback overrides.
+    ndk::ScopedAStatus onStatusUpdate(int status_code, float progress) override;
+    ndk::ScopedAStatus onPayloadApplicationComplete(int error_code) override;
+  };
+
+  static std::vector<std::string> ParseHeaders(const std::string& arg);
+
+  const ndk::ScopedAIBinder_DeathRecipient death_recipient_{
+      AIBinder_DeathRecipient_new(&UpdateEngineServiceDied)};
+  std::shared_ptr<aidl::android::os::IUpdateEngineStable> service_;
+  std::shared_ptr<aidl::android::os::BnUpdateEngineStableCallback> callback_;
+};
+
+ndk::ScopedAStatus UpdateEngineClientAndroid::UECallback::onStatusUpdate(
+    int status_code, float progress) {
+  LOG(INFO) << "onStatusUpdate(" << status_code << ", " << progress << ")";
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus
+UpdateEngineClientAndroid::UECallback::onPayloadApplicationComplete(
+    int error_code) {
+  LOG(INFO) << "onPayloadApplicationComplete(" << error_code << ")";
+  auto code = static_cast<ErrorCode>(error_code);
+  Exit((code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
+           ? EX_OK
+           : EX_SOFTWARE);
+  __builtin_unreachable();
+}
+
+int UpdateEngineClientAndroid::Run() {
+  service_ = aidl::android::os::IUpdateEngineStable::fromBinder(ndk::SpAIBinder(
+      AServiceManager_getService("android.os.UpdateEngineStableService")));
+  if (service_ == nullptr) {
+    LOG(ERROR)
+        << "Failed to get IUpdateEngineStable binder from service manager.";
+    return EX_SOFTWARE;
+  }
+
+  // Register a callback object with the service.
+  callback_ = ndk::SharedRefBase::make<UECallback>();
+  bool bound;
+  if (!service_->bind(callback_, &bound).isOk() || !bound) {
+    LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
+    return EX_SOFTWARE;
+  }
+
+  auto headers = ParseHeaders(FLAGS_headers);
+  ndk::ScopedAStatus status;
+  const char* payload_path;
+  std::string file_prefix = "file://";
+  if (android::base::StartsWith(FLAGS_payload, file_prefix)) {
+    payload_path = FLAGS_payload.data() + file_prefix.length();
+  } else {
+    payload_path = FLAGS_payload.data();
+  }
+  ndk::ScopedFileDescriptor ufd(
+      TEMP_FAILURE_RETRY(open(payload_path, O_RDONLY)));
+  if (ufd.get() < 0) {
+    PLOG(ERROR) << "Can't open " << payload_path;
+    return EX_SOFTWARE;
+  }
+  status = service_->applyPayloadFd(ufd, FLAGS_offset, FLAGS_size, headers);
+  if (!status.isOk()) {
+    LOG(ERROR) << "Cannot apply payload: " << status.getDescription();
+    return EX_SOFTWARE;
+  }
+
+  // When following updates status changes, exit if the update_engine daemon
+  // dies.
+  if (AIBinder_linkToDeath(service_->asBinder().get(),
+                           death_recipient_.get(),
+                           nullptr) != STATUS_OK) {
+    return EX_SOFTWARE;
+  }
+
+  return EX_OK;
+}
+
+std::vector<std::string> UpdateEngineClientAndroid::ParseHeaders(
+    const std::string& arg) {
+  std::vector<std::string> lines = android::base::Split(arg, "\n");
+  std::vector<std::string> headers;
+  for (const auto& line : lines) {
+    auto header = android::base::Trim(line);
+    if (!header.empty()) {
+      headers.push_back(header);
+    }
+  }
+  return headers;
+}
+
+}  // namespace chromeos_update_engine::internal
+
+int main(int argc, char** argv) {
+  android::base::InitLogging(argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  // Unlike other update_engine* processes that uses message loops,
+  // update_engine_stable_client uses a thread pool model. However, number of
+  // threads is limited to 1; that is, 0 additional threads should be spawned.
+  // This avoids some race conditions.
+  if (!ABinderProcess_setThreadPoolMaxThreadCount(0)) {
+    LOG(ERROR) << "Cannot set thread pool max thread count";
+    return EX_SOFTWARE;
+  }
+  ABinderProcess_startThreadPool();
+
+  chromeos_update_engine::internal::UpdateEngineClientAndroid client{};
+  int code = client.Run();
+  if (code != EX_OK)
+    return code;
+
+  ABinderProcess_joinThreadPool();
+  LOG(ERROR) << "Exited from joinThreadPool.";
+  return EX_SOFTWARE;
+}