blob: c466c4268d12b413c74fc23feb09ce37e71a7e56 [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
53int Exit(int return_code) {
54 LOG(INFO) << "Exit: " << return_code;
55 exit(return_code);
56 __builtin_unreachable();
57}
58// Called whenever the UpdateEngine daemon dies.
59void UpdateEngineServiceDied(void*) {
60 LOG(ERROR) << "UpdateEngineService died.";
61 Exit(EX_SOFTWARE);
62}
63
64class UpdateEngineClientAndroid {
65 public:
66 UpdateEngineClientAndroid() = default;
67 int Run();
68
69 private:
70 class UECallback : public aidl::android::os::BnUpdateEngineStableCallback {
71 public:
72 UECallback() = default;
73
74 // android::os::BnUpdateEngineStableCallback overrides.
75 ndk::ScopedAStatus onStatusUpdate(int status_code, float progress) override;
76 ndk::ScopedAStatus onPayloadApplicationComplete(int error_code) override;
77 };
78
79 static std::vector<std::string> ParseHeaders(const std::string& arg);
80
81 const ndk::ScopedAIBinder_DeathRecipient death_recipient_{
82 AIBinder_DeathRecipient_new(&UpdateEngineServiceDied)};
83 std::shared_ptr<aidl::android::os::IUpdateEngineStable> service_;
84 std::shared_ptr<aidl::android::os::BnUpdateEngineStableCallback> callback_;
85};
86
87ndk::ScopedAStatus UpdateEngineClientAndroid::UECallback::onStatusUpdate(
88 int status_code, float progress) {
89 LOG(INFO) << "onStatusUpdate(" << status_code << ", " << progress << ")";
90 return ndk::ScopedAStatus::ok();
91}
92
93ndk::ScopedAStatus
94UpdateEngineClientAndroid::UECallback::onPayloadApplicationComplete(
95 int error_code) {
96 LOG(INFO) << "onPayloadApplicationComplete(" << error_code << ")";
97 auto code = static_cast<ErrorCode>(error_code);
98 Exit((code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
99 ? EX_OK
100 : EX_SOFTWARE);
101 __builtin_unreachable();
102}
103
104int UpdateEngineClientAndroid::Run() {
105 service_ = aidl::android::os::IUpdateEngineStable::fromBinder(ndk::SpAIBinder(
106 AServiceManager_getService("android.os.UpdateEngineStableService")));
107 if (service_ == nullptr) {
108 LOG(ERROR)
109 << "Failed to get IUpdateEngineStable binder from service manager.";
110 return EX_SOFTWARE;
111 }
112
113 // Register a callback object with the service.
114 callback_ = ndk::SharedRefBase::make<UECallback>();
115 bool bound;
116 if (!service_->bind(callback_, &bound).isOk() || !bound) {
117 LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
118 return EX_SOFTWARE;
119 }
120
121 auto headers = ParseHeaders(FLAGS_headers);
122 ndk::ScopedAStatus status;
123 const char* payload_path;
124 std::string file_prefix = "file://";
125 if (android::base::StartsWith(FLAGS_payload, file_prefix)) {
126 payload_path = FLAGS_payload.data() + file_prefix.length();
127 } else {
128 payload_path = FLAGS_payload.data();
129 }
130 ndk::ScopedFileDescriptor ufd(
131 TEMP_FAILURE_RETRY(open(payload_path, O_RDONLY)));
132 if (ufd.get() < 0) {
133 PLOG(ERROR) << "Can't open " << payload_path;
134 return EX_SOFTWARE;
135 }
136 status = service_->applyPayloadFd(ufd, FLAGS_offset, FLAGS_size, headers);
137 if (!status.isOk()) {
138 LOG(ERROR) << "Cannot apply payload: " << status.getDescription();
139 return EX_SOFTWARE;
140 }
141
142 // When following updates status changes, exit if the update_engine daemon
143 // dies.
144 if (AIBinder_linkToDeath(service_->asBinder().get(),
145 death_recipient_.get(),
146 nullptr) != STATUS_OK) {
147 return EX_SOFTWARE;
148 }
149
150 return EX_OK;
151}
152
153std::vector<std::string> UpdateEngineClientAndroid::ParseHeaders(
154 const std::string& arg) {
155 std::vector<std::string> lines = android::base::Split(arg, "\n");
156 std::vector<std::string> headers;
157 for (const auto& line : lines) {
158 auto header = android::base::Trim(line);
159 if (!header.empty()) {
160 headers.push_back(header);
161 }
162 }
163 return headers;
164}
165
166} // namespace chromeos_update_engine::internal
167
168int main(int argc, char** argv) {
169 android::base::InitLogging(argv);
170 gflags::ParseCommandLineFlags(&argc, &argv, true);
171
172 // Unlike other update_engine* processes that uses message loops,
173 // update_engine_stable_client uses a thread pool model. However, number of
174 // threads is limited to 1; that is, 0 additional threads should be spawned.
175 // This avoids some race conditions.
176 if (!ABinderProcess_setThreadPoolMaxThreadCount(0)) {
177 LOG(ERROR) << "Cannot set thread pool max thread count";
178 return EX_SOFTWARE;
179 }
180 ABinderProcess_startThreadPool();
181
182 chromeos_update_engine::internal::UpdateEngineClientAndroid client{};
183 int code = client.Run();
184 if (code != EX_OK)
185 return code;
186
187 ABinderProcess_joinThreadPool();
188 LOG(ERROR) << "Exited from joinThreadPool.";
189 return EX_SOFTWARE;
190}