// // 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 #include #include #include #include #include #include #include #include #include #include #include #include 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 ParseHeaders(const std::string& arg); const ndk::ScopedAIBinder_DeathRecipient death_recipient_{ AIBinder_DeathRecipient_new(&UpdateEngineServiceDied)}; std::shared_ptr service_; std::shared_ptr 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(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(); 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 UpdateEngineClientAndroid::ParseHeaders( const std::string& arg) { std::vector lines = android::base::Split(arg, "\n"); std::vector 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; }