| /* |
| * Copyright (C) 2007 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 "minadbd_services.h" |
| |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <functional> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <thread> |
| #include <unordered_set> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/memory.h> |
| #include <android-base/parseint.h> |
| #include <android-base/properties.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| |
| #include "adb.h" |
| #include "adb_unique_fd.h" |
| #include "adb_utils.h" |
| #include "fdevent.h" |
| #include "fuse_adb_provider.h" |
| #include "fuse_sideload.h" |
| #include "minadbd_types.h" |
| #include "services.h" |
| #include "sysdeps.h" |
| |
| static int minadbd_socket = -1; |
| static bool rescue_mode = false; |
| static std::string sideload_mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT; |
| |
| void SetMinadbdSocketFd(int socket_fd) { |
| minadbd_socket = socket_fd; |
| } |
| |
| void SetMinadbdRescueMode(bool rescue) { |
| rescue_mode = rescue; |
| } |
| |
| void SetSideloadMountPoint(const std::string& path) { |
| sideload_mount_point = path; |
| } |
| |
| static bool WriteCommandToFd(MinadbdCommand cmd, int fd) { |
| char message[kMinadbdMessageSize]; |
| memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); |
| android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); |
| |
| if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { |
| PLOG(ERROR) << "Failed to write message " << message; |
| return false; |
| } |
| return true; |
| } |
| |
| // Blocks and reads the command status from |fd|. Returns false if the received message has a |
| // format error. |
| static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) { |
| char buffer[kMinadbdMessageSize]; |
| if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) { |
| PLOG(ERROR) << "Failed to response status from socket"; |
| exit(kMinadbdSocketIOError); |
| } |
| |
| std::string message(buffer, buffer + kMinadbdMessageSize); |
| if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) { |
| LOG(ERROR) << "Failed to parse status in " << message; |
| return false; |
| } |
| |
| *status = android::base::get_unaligned<MinadbdCommandStatus>( |
| message.substr(strlen(kMinadbdStatusPrefix)).c_str()); |
| return true; |
| } |
| |
| static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, |
| MinadbdCommandStatus* status) { |
| auto pieces = android::base::Split(args, ":"); |
| int64_t file_size; |
| int block_size; |
| if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 || |
| !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) { |
| LOG(ERROR) << "bad sideload-host arguments: " << args; |
| return kMinadbdPackageSizeError; |
| } |
| |
| LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; |
| |
| if (!WriteCommandToFd(MinadbdCommand::kInstall, minadbd_socket)) { |
| return kMinadbdSocketIOError; |
| } |
| |
| auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size); |
| if (int result = run_fuse_sideload(std::move(adb_data_reader), sideload_mount_point.c_str()); |
| result != 0) { |
| LOG(ERROR) << "Failed to start fuse"; |
| return kMinadbdFuseStartError; |
| } |
| |
| if (!WaitForCommandStatus(minadbd_socket, status)) { |
| return kMinadbdMessageFormatError; |
| } |
| |
| // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess |
| // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on |
| // install error. |
| if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) { |
| if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess, |
| strlen(kSideloadServiceExitSuccess))) { |
| return kMinadbdHostSocketIOError; |
| } |
| } else { |
| if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure, |
| strlen(kSideloadServiceExitFailure))) { |
| return kMinadbdHostSocketIOError; |
| } |
| } |
| |
| return kMinadbdSuccess; |
| } |
| |
| // Sideload service always exits after serving an install command. |
| static void SideloadHostService(unique_fd sfd, const std::string& args) { |
| MinadbdCommandStatus status; |
| exit(RunAdbFuseSideload(sfd.get(), args, &status)); |
| } |
| |
| // Rescue service waits for the next command after an install command. |
| static void RescueInstallHostService(unique_fd sfd, const std::string& args) { |
| MinadbdCommandStatus status; |
| if (auto result = RunAdbFuseSideload(sfd.get(), args, &status); result != kMinadbdSuccess) { |
| exit(result); |
| } |
| } |
| |
| static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { |
| static const std::unordered_set<std::string> kGetpropAllowedProps = { |
| "ro.build.fingerprint", |
| "ro.build.date.utc", |
| }; |
| auto allowed = kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end(); |
| if (!allowed) { |
| return; |
| } |
| |
| auto result = android::base::GetProperty(prop, ""); |
| if (result.empty()) { |
| return; |
| } |
| if (!android::base::WriteFully(sfd, result.data(), result.size())) { |
| exit(kMinadbdHostSocketIOError); |
| } |
| } |
| |
| // Reboots into the given target. We don't reboot directly from minadbd, but going through recovery |
| // instead. This allows recovery to finish all the pending works (clear BCB, save logs etc) before |
| // the reboot. |
| static void RebootHostService(unique_fd /* sfd */, const std::string& target) { |
| MinadbdCommand command; |
| if (target == "bootloader") { |
| command = MinadbdCommand::kRebootBootloader; |
| } else if (target == "rescue") { |
| command = MinadbdCommand::kRebootRescue; |
| } else if (target == "recovery") { |
| command = MinadbdCommand::kRebootRecovery; |
| } else if (target == "fastboot") { |
| command = MinadbdCommand::kRebootFastboot; |
| } else { |
| command = MinadbdCommand::kRebootAndroid; |
| } |
| if (!WriteCommandToFd(command, minadbd_socket)) { |
| exit(kMinadbdSocketIOError); |
| } |
| MinadbdCommandStatus status; |
| if (!WaitForCommandStatus(minadbd_socket, &status)) { |
| exit(kMinadbdMessageFormatError); |
| } |
| } |
| |
| unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { |
| // Common services that are supported both in sideload and rescue modes. |
| if (ConsumePrefix(&name, "reboot:")) { |
| // "reboot:<target>", where target must be one of the following. |
| std::string args(name); |
| if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" || |
| args == "fastboot") { |
| return create_service_thread("reboot", |
| std::bind(RebootHostService, std::placeholders::_1, args)); |
| } |
| return unique_fd{}; |
| } |
| |
| // Rescue-specific services. |
| if (rescue_mode) { |
| if (ConsumePrefix(&name, "rescue-install:")) { |
| // rescue-install:<file-size>:<block-size> |
| std::string args(name); |
| return create_service_thread( |
| "rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args)); |
| } else if (ConsumePrefix(&name, "rescue-getprop:")) { |
| // rescue-getprop:<prop> |
| std::string args(name); |
| return create_service_thread( |
| "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); |
| } |
| return unique_fd{}; |
| } |
| |
| // Sideload-specific services. |
| if (name.starts_with("sideload:")) { |
| // This exit status causes recovery to print a special error message saying to use a newer adb |
| // (that supports sideload-host). |
| exit(kMinadbdAdbVersionError); |
| } else if (ConsumePrefix(&name, "sideload-host:")) { |
| // sideload-host:<file-size>:<block-size> |
| std::string args(name); |
| return create_service_thread("sideload-host", |
| std::bind(SideloadHostService, std::placeholders::_1, args)); |
| } |
| return unique_fd{}; |
| } |