diff options
Diffstat (limited to 'stable/update_engine_stable_client.cc')
-rw-r--r-- | stable/update_engine_stable_client.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/stable/update_engine_stable_client.cc b/stable/update_engine_stable_client.cc new file mode 100644 index 00000000..17f66b67 --- /dev/null +++ b/stable/update_engine_stable_client.cc @@ -0,0 +1,187 @@ +// +// 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> + +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."); + +[[noreturn]] int Exit(int return_code) { + LOG(INFO) << "Exit: " << return_code; + exit(return_code); +} +// 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); +} + +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; +} |