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