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;
+}