| /* | |
| * Copyright (C) 2018 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. | |
| */ | |
| #include "fastboot_device.h" | |
| #include <algorithm> | |
| #include <BootControlClient.h> | |
| #include <android-base/logging.h> | |
| #include <android-base/properties.h> | |
| #include <android-base/strings.h> | |
| #include <android/binder_manager.h> | |
| #include <android/hardware/boot/1.0/IBootControl.h> | |
| #include <android/hardware/fastboot/1.1/IFastboot.h> | |
| #include <fastbootshim.h> | |
| #include <fs_mgr.h> | |
| #include <fs_mgr/roots.h> | |
| #include <health-shim/shim.h> | |
| #include <healthhalutils/HealthHalUtils.h> | |
| #include "constants.h" | |
| #include "flashing.h" | |
| #include "tcp_client.h" | |
| #include "usb_client.h" | |
| using std::string_literals::operator""s; | |
| using android::fs_mgr::EnsurePathUnmounted; | |
| using android::fs_mgr::Fstab; | |
| using ::android::hardware::hidl_string; | |
| using ::android::hardware::fastboot::V1_1::IFastboot; | |
| using BootControlClient = FastbootDevice::BootControlClient; | |
| namespace sph = std::placeholders; | |
| std::shared_ptr<aidl::android::hardware::health::IHealth> get_health_service() { | |
| using aidl::android::hardware::health::IHealth; | |
| using HidlHealth = android::hardware::health::V2_0::IHealth; | |
| using aidl::android::hardware::health::HealthShim; | |
| auto service_name = IHealth::descriptor + "/default"s; | |
| if (AServiceManager_isDeclared(service_name.c_str())) { | |
| ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str())); | |
| std::shared_ptr<IHealth> health = IHealth::fromBinder(binder); | |
| if (health != nullptr) return health; | |
| LOG(WARNING) << "AIDL health service is declared, but it cannot be retrieved."; | |
| } | |
| LOG(INFO) << "Unable to get AIDL health service, trying HIDL..."; | |
| android::sp<HidlHealth> hidl_health = android::hardware::health::V2_0::get_health_service(); | |
| if (hidl_health != nullptr) { | |
| return ndk::SharedRefBase::make<HealthShim>(hidl_health); | |
| } | |
| LOG(WARNING) << "No health implementation is found."; | |
| return nullptr; | |
| } | |
| std::shared_ptr<aidl::android::hardware::fastboot::IFastboot> get_fastboot_service() { | |
| using aidl::android::hardware::fastboot::IFastboot; | |
| using HidlFastboot = android::hardware::fastboot::V1_1::IFastboot; | |
| using aidl::android::hardware::fastboot::FastbootShim; | |
| auto service_name = IFastboot::descriptor + "/default"s; | |
| if (AServiceManager_isDeclared(service_name.c_str())) { | |
| ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str())); | |
| std::shared_ptr<IFastboot> fastboot = IFastboot::fromBinder(binder); | |
| if (fastboot != nullptr) { | |
| LOG(INFO) << "Found and using AIDL fastboot service"; | |
| return fastboot; | |
| } | |
| LOG(WARNING) << "AIDL fastboot service is declared, but it cannot be retrieved."; | |
| } | |
| LOG(INFO) << "Unable to get AIDL fastboot service, trying HIDL..."; | |
| android::sp<HidlFastboot> hidl_fastboot = HidlFastboot::getService(); | |
| if (hidl_fastboot != nullptr) { | |
| LOG(INFO) << "Found and now using fastboot HIDL implementation"; | |
| return ndk::SharedRefBase::make<FastbootShim>(hidl_fastboot); | |
| } | |
| LOG(WARNING) << "No fastboot implementation is found."; | |
| return nullptr; | |
| } | |
| FastbootDevice::FastbootDevice() | |
| : kCommandMap({ | |
| {FB_CMD_SET_ACTIVE, SetActiveHandler}, | |
| {FB_CMD_DOWNLOAD, DownloadHandler}, | |
| {FB_CMD_GETVAR, GetVarHandler}, | |
| {FB_CMD_SHUTDOWN, ShutDownHandler}, | |
| {FB_CMD_REBOOT, RebootHandler}, | |
| {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler}, | |
| {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler}, | |
| {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler}, | |
| {FB_CMD_ERASE, EraseHandler}, | |
| {FB_CMD_FLASH, FlashHandler}, | |
| {FB_CMD_CREATE_PARTITION, CreatePartitionHandler}, | |
| {FB_CMD_DELETE_PARTITION, DeletePartitionHandler}, | |
| {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler}, | |
| {FB_CMD_UPDATE_SUPER, UpdateSuperHandler}, | |
| {FB_CMD_OEM, OemCmdHandler}, | |
| {FB_CMD_GSI, GsiHandler}, | |
| {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler}, | |
| {FB_CMD_FETCH, FetchHandler}, | |
| }), | |
| boot_control_hal_(BootControlClient::WaitForService()), | |
| health_hal_(get_health_service()), | |
| fastboot_hal_(get_fastboot_service()), | |
| active_slot_("") { | |
| if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") { | |
| transport_ = std::make_unique<ClientTcpTransport>(); | |
| } else { | |
| transport_ = std::make_unique<ClientUsbTransport>(); | |
| } | |
| // Make sure cache is unmounted, since recovery will have mounted it for | |
| // logging. | |
| Fstab fstab; | |
| if (ReadDefaultFstab(&fstab)) { | |
| EnsurePathUnmounted(&fstab, "/cache"); | |
| } | |
| } | |
| FastbootDevice::~FastbootDevice() { | |
| CloseDevice(); | |
| } | |
| void FastbootDevice::CloseDevice() { | |
| transport_->Close(); | |
| } | |
| std::string FastbootDevice::GetCurrentSlot() { | |
| // Check if a set_active ccommand was issued earlier since the boot control HAL | |
| // returns the slot that is currently booted into. | |
| if (!active_slot_.empty()) { | |
| return active_slot_; | |
| } | |
| // Non-A/B devices must not have boot control HALs. | |
| if (!boot_control_hal_) { | |
| return ""; | |
| } | |
| std::string suffix = boot_control_hal_->GetSuffix(boot_control_hal_->GetCurrentSlot()); | |
| return suffix; | |
| } | |
| BootControlClient* FastbootDevice::boot1_1() const { | |
| if (boot_control_hal_ && | |
| boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) { | |
| return boot_control_hal_.get(); | |
| } | |
| return nullptr; | |
| } | |
| bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) { | |
| constexpr size_t kResponseReasonSize = 4; | |
| constexpr size_t kNumResponseTypes = 4; // "FAIL", "OKAY", "INFO", "DATA" | |
| char buf[FB_RESPONSE_SZ]; | |
| constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize; | |
| size_t msg_len = std::min(kMaxMessageSize, message.size()); | |
| constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL, | |
| RESPONSE_INFO, RESPONSE_DATA}; | |
| if (static_cast<size_t>(result) >= kNumResponseTypes) { | |
| return false; | |
| } | |
| memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize); | |
| memcpy(buf + kResponseReasonSize, message.c_str(), msg_len); | |
| size_t response_len = kResponseReasonSize + msg_len; | |
| auto write_ret = this->get_transport()->Write(buf, response_len); | |
| if (write_ret != static_cast<ssize_t>(response_len)) { | |
| PLOG(ERROR) << "Failed to write " << message; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool FastbootDevice::HandleData(bool read, std::vector<char>* data) { | |
| return HandleData(read, data->data(), data->size()); | |
| } | |
| bool FastbootDevice::HandleData(bool read, char* data, uint64_t size) { | |
| auto read_write_data_size = read ? this->get_transport()->Read(data, size) | |
| : this->get_transport()->Write(data, size); | |
| if (read_write_data_size == -1) { | |
| LOG(ERROR) << (read ? "read from" : "write to") << " transport failed"; | |
| return false; | |
| } | |
| if (static_cast<size_t>(read_write_data_size) != size) { | |
| LOG(ERROR) << (read ? "read" : "write") << " expected " << size << " bytes, got " | |
| << read_write_data_size; | |
| return false; | |
| } | |
| return true; | |
| } | |
| void FastbootDevice::ExecuteCommands() { | |
| char command[FB_RESPONSE_SZ + 1]; | |
| for (;;) { | |
| auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ); | |
| if (bytes_read == -1) { | |
| PLOG(ERROR) << "Couldn't read command"; | |
| return; | |
| } | |
| if (std::count_if(command, command + bytes_read, iscntrl) != 0) { | |
| WriteStatus(FastbootResult::FAIL, | |
| "Command contains control character"); | |
| continue; | |
| } | |
| command[bytes_read] = '\0'; | |
| LOG(INFO) << "Fastboot command: " << command; | |
| std::vector<std::string> args; | |
| std::string cmd_name; | |
| if (android::base::StartsWith(command, "oem ")) { | |
| args = {command}; | |
| cmd_name = FB_CMD_OEM; | |
| } else { | |
| args = android::base::Split(command, ":"); | |
| cmd_name = args[0]; | |
| } | |
| auto found_command = kCommandMap.find(cmd_name); | |
| if (found_command == kCommandMap.end()) { | |
| WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]); | |
| continue; | |
| } | |
| if (!found_command->second(this, args)) { | |
| return; | |
| } | |
| } | |
| } | |
| bool FastbootDevice::WriteOkay(const std::string& message) { | |
| return WriteStatus(FastbootResult::OKAY, message); | |
| } | |
| bool FastbootDevice::WriteFail(const std::string& message) { | |
| return WriteStatus(FastbootResult::FAIL, message); | |
| } | |
| bool FastbootDevice::WriteInfo(const std::string& message) { | |
| return WriteStatus(FastbootResult::INFO, message); | |
| } |