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