blob: da203c4c6d17af2c0d64227af456b5bf0694d3a5 [file] [log] [blame]
Yifan Hongd51738c2020-07-23 17:06:25 -07001//
2// Copyright (C) 2020 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17// update_engine console client installed to APEXes for scripts to invoke
18// directly. Uses the stable API.
19
20#include <fcntl.h>
21#include <sysexits.h>
22#include <unistd.h>
23
24#include <vector>
25
26#include <aidl/android/os/BnUpdateEngineStableCallback.h>
27#include <aidl/android/os/IUpdateEngineStable.h>
28#include <android-base/logging.h>
29#include <android-base/strings.h>
30#include <android/binder_manager.h>
31#include <android/binder_process.h>
32#include <android/binder_ibinder.h>
33#include <common/error_code.h>
34#include <gflags/gflags.h>
35#include <utils/StrongPointer.h>
36
37namespace chromeos_update_engine::internal {
38
39DEFINE_string(payload,
40 "file:///path/to/payload.bin",
41 "The file URI to the update payload to use, or path to the file");
42DEFINE_int64(offset,
43 0,
44 "The offset in the payload where the CrAU update starts.");
45DEFINE_int64(size,
46 0,
47 "The size of the CrAU part of the payload. If 0 is passed, it "
48 "will be autodetected.");
49DEFINE_string(headers,
50 "",
51 "A list of key-value pairs, one element of the list per line.");
52
Yifan Hong5d7a1de2020-08-07 11:49:03 -070053[[noreturn]] int Exit(int return_code) {
Yifan Hongd51738c2020-07-23 17:06:25 -070054 LOG(INFO) << "Exit: " << return_code;
55 exit(return_code);
Yifan Hongd51738c2020-07-23 17:06:25 -070056}
57// Called whenever the UpdateEngine daemon dies.
58void UpdateEngineServiceDied(void*) {
59 LOG(ERROR) << "UpdateEngineService died.";
60 Exit(EX_SOFTWARE);
61}
62
63class UpdateEngineClientAndroid {
64 public:
65 UpdateEngineClientAndroid() = default;
66 int Run();
67
68 private:
69 class UECallback : public aidl::android::os::BnUpdateEngineStableCallback {
70 public:
71 UECallback() = default;
72
73 // android::os::BnUpdateEngineStableCallback overrides.
74 ndk::ScopedAStatus onStatusUpdate(int status_code, float progress) override;
75 ndk::ScopedAStatus onPayloadApplicationComplete(int error_code) override;
76 };
77
78 static std::vector<std::string> ParseHeaders(const std::string& arg);
79
80 const ndk::ScopedAIBinder_DeathRecipient death_recipient_{
81 AIBinder_DeathRecipient_new(&UpdateEngineServiceDied)};
82 std::shared_ptr<aidl::android::os::IUpdateEngineStable> service_;
83 std::shared_ptr<aidl::android::os::BnUpdateEngineStableCallback> callback_;
84};
85
86ndk::ScopedAStatus UpdateEngineClientAndroid::UECallback::onStatusUpdate(
87 int status_code, float progress) {
88 LOG(INFO) << "onStatusUpdate(" << status_code << ", " << progress << ")";
89 return ndk::ScopedAStatus::ok();
90}
91
92ndk::ScopedAStatus
93UpdateEngineClientAndroid::UECallback::onPayloadApplicationComplete(
94 int error_code) {
95 LOG(INFO) << "onPayloadApplicationComplete(" << error_code << ")";
96 auto code = static_cast<ErrorCode>(error_code);
97 Exit((code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
98 ? EX_OK
99 : EX_SOFTWARE);
Yifan Hongd51738c2020-07-23 17:06:25 -0700100}
101
102int UpdateEngineClientAndroid::Run() {
103 service_ = aidl::android::os::IUpdateEngineStable::fromBinder(ndk::SpAIBinder(
104 AServiceManager_getService("android.os.UpdateEngineStableService")));
105 if (service_ == nullptr) {
106 LOG(ERROR)
107 << "Failed to get IUpdateEngineStable binder from service manager.";
108 return EX_SOFTWARE;
109 }
110
111 // Register a callback object with the service.
112 callback_ = ndk::SharedRefBase::make<UECallback>();
113 bool bound;
114 if (!service_->bind(callback_, &bound).isOk() || !bound) {
115 LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
116 return EX_SOFTWARE;
117 }
118
119 auto headers = ParseHeaders(FLAGS_headers);
120 ndk::ScopedAStatus status;
121 const char* payload_path;
122 std::string file_prefix = "file://";
123 if (android::base::StartsWith(FLAGS_payload, file_prefix)) {
124 payload_path = FLAGS_payload.data() + file_prefix.length();
125 } else {
126 payload_path = FLAGS_payload.data();
127 }
128 ndk::ScopedFileDescriptor ufd(
129 TEMP_FAILURE_RETRY(open(payload_path, O_RDONLY)));
130 if (ufd.get() < 0) {
131 PLOG(ERROR) << "Can't open " << payload_path;
132 return EX_SOFTWARE;
133 }
134 status = service_->applyPayloadFd(ufd, FLAGS_offset, FLAGS_size, headers);
135 if (!status.isOk()) {
136 LOG(ERROR) << "Cannot apply payload: " << status.getDescription();
137 return EX_SOFTWARE;
138 }
139
140 // When following updates status changes, exit if the update_engine daemon
141 // dies.
142 if (AIBinder_linkToDeath(service_->asBinder().get(),
143 death_recipient_.get(),
144 nullptr) != STATUS_OK) {
145 return EX_SOFTWARE;
146 }
147
148 return EX_OK;
149}
150
151std::vector<std::string> UpdateEngineClientAndroid::ParseHeaders(
152 const std::string& arg) {
153 std::vector<std::string> lines = android::base::Split(arg, "\n");
154 std::vector<std::string> headers;
155 for (const auto& line : lines) {
156 auto header = android::base::Trim(line);
157 if (!header.empty()) {
158 headers.push_back(header);
159 }
160 }
161 return headers;
162}
163
164} // namespace chromeos_update_engine::internal
165
166int main(int argc, char** argv) {
167 android::base::InitLogging(argv);
168 gflags::ParseCommandLineFlags(&argc, &argv, true);
169
170 // Unlike other update_engine* processes that uses message loops,
171 // update_engine_stable_client uses a thread pool model. However, number of
172 // threads is limited to 1; that is, 0 additional threads should be spawned.
173 // This avoids some race conditions.
174 if (!ABinderProcess_setThreadPoolMaxThreadCount(0)) {
175 LOG(ERROR) << "Cannot set thread pool max thread count";
176 return EX_SOFTWARE;
177 }
178 ABinderProcess_startThreadPool();
179
180 chromeos_update_engine::internal::UpdateEngineClientAndroid client{};
181 int code = client.Run();
182 if (code != EX_OK)
183 return code;
184
185 ABinderProcess_joinThreadPool();
186 LOG(ERROR) << "Exited from joinThreadPool.";
187 return EX_SOFTWARE;
188}