You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
6.1 KiB

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