| // | 
 | // 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; | 
 | } |