1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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;
}
|