Adding Android Binder Bridge (abb) utility launched from adbd.
Once launched, abb will listen for incoming Binder cli requests. Executing in-process provides 6x latency improvement (125ms vs 25ms on PixelXL) for commands like 'package path' Intended usage by Android Studio for fast deployment and patching of APKs. Test: manual BUG: 111621042 Change-Id: Ica84eb2ec9628efa441ecd627b119f3361feaf9f
This commit is contained in:
parent
3009be5cb6
commit
640407d632
11 changed files with 535 additions and 93 deletions
|
@ -355,6 +355,7 @@ cc_library {
|
|||
compile_multilib: "both",
|
||||
|
||||
srcs: [
|
||||
"daemon/abb_service.cpp",
|
||||
"daemon/file_sync_service.cpp",
|
||||
"daemon/framebuffer_service.cpp",
|
||||
"daemon/mdns.cpp",
|
||||
|
@ -391,6 +392,14 @@ cc_library {
|
|||
"libmdnssd",
|
||||
"libselinux",
|
||||
],
|
||||
|
||||
target: {
|
||||
recovery: {
|
||||
exclude_srcs: [
|
||||
"daemon/abb_service.cpp",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library {
|
||||
|
@ -455,6 +464,40 @@ cc_binary {
|
|||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "abb",
|
||||
|
||||
defaults: ["adb_defaults"],
|
||||
recovery_available: false,
|
||||
|
||||
srcs: [
|
||||
"daemon/abb.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_GNU_SOURCE",
|
||||
"-Wno-deprecated-declarations",
|
||||
],
|
||||
|
||||
strip: {
|
||||
keep_symbols: true,
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libadbd_core",
|
||||
"libadbd_services",
|
||||
"libcmd",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libselinux",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "adbd_test",
|
||||
defaults: ["adb_defaults"],
|
||||
|
|
|
@ -152,6 +152,10 @@ asocket* host_service_to_socket(const char* name, const char* serial, TransportI
|
|||
asocket* daemon_service_to_socket(std::string_view name);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
unique_fd execute_binder_command(std::string_view command);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
int init_jdwp(void);
|
||||
asocket* create_jdwp_service_socket();
|
||||
|
@ -202,6 +206,9 @@ extern const char* adb_device_banner;
|
|||
|
||||
#define CHUNK_SIZE (64 * 1024)
|
||||
|
||||
// Argument delimeter for adb abb command.
|
||||
#define ABB_ARG_DELIMETER ('\0')
|
||||
|
||||
#if !ADB_HOST
|
||||
#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
|
||||
#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
@ -182,3 +187,79 @@ bool ReadOrderlyShutdown(int fd) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
bool SendFileDescriptor(int socket_fd, int fd) {
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char dummy = '!';
|
||||
union {
|
||||
cmsghdr cm;
|
||||
char buffer[CMSG_SPACE(sizeof(int))];
|
||||
} cm_un;
|
||||
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = cm_un.buffer;
|
||||
msg.msg_controllen = sizeof(cm_un.buffer);
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)CMSG_DATA(cmsg))[0] = fd;
|
||||
|
||||
int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
|
||||
if (ret < 0) {
|
||||
D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
D("sent file descriptor %d to via socket %d", fd, socket_fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
|
||||
char dummy = '!';
|
||||
union {
|
||||
cmsghdr cm;
|
||||
char buffer[CMSG_SPACE(sizeof(int))];
|
||||
} cm_un;
|
||||
|
||||
iovec iov;
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
|
||||
msghdr msg;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = cm_un.buffer;
|
||||
msg.msg_controllen = sizeof(cm_un.buffer);
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)(CMSG_DATA(cmsg)))[0] = -1;
|
||||
|
||||
int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
|
||||
if (rc <= 0) {
|
||||
*error = perror_str("receiving file descriptor via socket failed");
|
||||
D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
|
||||
D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
|
10
adb/adb_io.h
10
adb/adb_io.h
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
// Sends the protocol "OKAY" message.
|
||||
bool SendOkay(int fd);
|
||||
|
||||
|
@ -73,4 +75,12 @@ bool WriteFdExactly(int fd, const std::string& s);
|
|||
// Same as above, but formats the string to send.
|
||||
bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
|
||||
|
||||
#if !ADB_HOST
|
||||
// Sends an FD via Unix domain socket.
|
||||
bool SendFileDescriptor(int socket_fd, int fd);
|
||||
|
||||
// Receives an FD via Unix domain socket.
|
||||
bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
|
||||
#endif
|
||||
|
||||
#endif /* ADB_IO_H */
|
||||
|
|
|
@ -579,12 +579,8 @@ static std::string ShellServiceString(bool use_shell_protocol,
|
|||
//
|
||||
// On success returns the remote exit code if |use_shell_protocol| is true,
|
||||
// 0 otherwise. On failure returns 1.
|
||||
static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
|
||||
char escape_char,
|
||||
const std::string& command) {
|
||||
std::string service_string = ShellServiceString(use_shell_protocol,
|
||||
type_arg, command);
|
||||
|
||||
static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, char escape_char,
|
||||
bool empty_command, const std::string& service_string) {
|
||||
// Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
|
||||
// Use |use_shell_protocol| to determine whether to allow a command longer than that.
|
||||
if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
|
||||
|
@ -595,8 +591,7 @@ static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
|
|||
// Make local stdin raw if the device allocates a PTY, which happens if:
|
||||
// 1. We are explicitly asking for a PTY shell, or
|
||||
// 2. We don't specify shell type and are starting an interactive session.
|
||||
bool raw_stdin = (type_arg == kShellServiceArgPty ||
|
||||
(type_arg.empty() && command.empty()));
|
||||
bool raw_stdin = (type_arg == kShellServiceArgPty || (type_arg.empty() && empty_command));
|
||||
|
||||
std::string error;
|
||||
int fd = adb_connect(service_string, &error);
|
||||
|
@ -756,7 +751,29 @@ static int adb_shell(int argc, const char** argv) {
|
|||
command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
|
||||
}
|
||||
|
||||
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
|
||||
std::string service_string = ShellServiceString(use_shell_protocol, shell_type_arg, command);
|
||||
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command.empty(),
|
||||
service_string);
|
||||
}
|
||||
|
||||
static int adb_abb(int argc, const char** argv) {
|
||||
// Defaults.
|
||||
constexpr char escape_char = '~'; // -e
|
||||
constexpr bool use_shell_protocol = true;
|
||||
constexpr auto shell_type_arg = kShellServiceArgRaw;
|
||||
constexpr bool empty_command = false;
|
||||
|
||||
std::string service_string("abb:");
|
||||
for (auto i = optind; i < argc; ++i) {
|
||||
service_string.append(argv[i]);
|
||||
service_string.push_back(ABB_ARG_DELIMETER);
|
||||
}
|
||||
|
||||
D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
|
||||
service_string.data());
|
||||
|
||||
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, empty_command,
|
||||
service_string);
|
||||
}
|
||||
|
||||
static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
|
||||
|
@ -1546,14 +1563,13 @@ int adb_commandline(int argc, const char** argv) {
|
|||
std::string query = android::base::StringPrintf("host:disconnect:%s",
|
||||
(argc == 2) ? argv[1] : "");
|
||||
return adb_query_command(query);
|
||||
}
|
||||
else if (!strcmp(argv[0], "emu")) {
|
||||
} else if (!strcmp(argv[0], "abb")) {
|
||||
return adb_abb(argc, argv);
|
||||
} else if (!strcmp(argv[0], "emu")) {
|
||||
return adb_send_emulator_command(argc, argv, serial);
|
||||
}
|
||||
else if (!strcmp(argv[0], "shell")) {
|
||||
} else if (!strcmp(argv[0], "shell")) {
|
||||
return adb_shell(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
|
||||
} else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
|
||||
int exec_in = !strcmp(argv[0], "exec-in");
|
||||
|
||||
if (argc < 2) error_exit("usage: adb %s command", argv[0]);
|
||||
|
@ -1581,11 +1597,9 @@ int adb_commandline(int argc, const char** argv) {
|
|||
|
||||
adb_close(fd);
|
||||
return 0;
|
||||
}
|
||||
else if (!strcmp(argv[0], "kill-server")) {
|
||||
} else if (!strcmp(argv[0], "kill-server")) {
|
||||
return adb_kill_server() ? 0 : 1;
|
||||
}
|
||||
else if (!strcmp(argv[0], "sideload")) {
|
||||
} else if (!strcmp(argv[0], "sideload")) {
|
||||
if (argc != 2) error_exit("sideload requires an argument");
|
||||
if (adb_sideload_host(argv[1])) {
|
||||
return 1;
|
||||
|
@ -1695,8 +1709,7 @@ int adb_commandline(int argc, const char** argv) {
|
|||
else if (!strcmp(argv[0], "ls")) {
|
||||
if (argc != 2) error_exit("ls requires an argument");
|
||||
return do_sync_ls(argv[1]) ? 0 : 1;
|
||||
}
|
||||
else if (!strcmp(argv[0], "push")) {
|
||||
} else if (!strcmp(argv[0], "push")) {
|
||||
bool copy_attrs = false;
|
||||
bool sync = false;
|
||||
std::vector<const char*> srcs;
|
||||
|
@ -1705,8 +1718,7 @@ int adb_commandline(int argc, const char** argv) {
|
|||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
|
||||
if (srcs.empty() || !dst) error_exit("push requires an argument");
|
||||
return do_sync_push(srcs, dst, sync) ? 0 : 1;
|
||||
}
|
||||
else if (!strcmp(argv[0], "pull")) {
|
||||
} else if (!strcmp(argv[0], "pull")) {
|
||||
bool copy_attrs = false;
|
||||
std::vector<const char*> srcs;
|
||||
const char* dst = ".";
|
||||
|
@ -1714,20 +1726,16 @@ int adb_commandline(int argc, const char** argv) {
|
|||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
|
||||
if (srcs.empty()) error_exit("pull requires an argument");
|
||||
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
|
||||
}
|
||||
else if (!strcmp(argv[0], "install")) {
|
||||
} else if (!strcmp(argv[0], "install")) {
|
||||
if (argc < 2) error_exit("install requires an argument");
|
||||
return install_app(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "install-multiple")) {
|
||||
} else if (!strcmp(argv[0], "install-multiple")) {
|
||||
if (argc < 2) error_exit("install-multiple requires an argument");
|
||||
return install_multiple_app(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "uninstall")) {
|
||||
} else if (!strcmp(argv[0], "uninstall")) {
|
||||
if (argc < 2) error_exit("uninstall requires an argument");
|
||||
return uninstall_app(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "sync")) {
|
||||
} else if (!strcmp(argv[0], "sync")) {
|
||||
std::string src;
|
||||
bool list_only = false;
|
||||
if (argc < 2) {
|
||||
|
@ -1757,34 +1765,28 @@ int adb_commandline(int argc, const char** argv) {
|
|||
return 0;
|
||||
}
|
||||
/* passthrough commands */
|
||||
else if (!strcmp(argv[0],"get-state") ||
|
||||
!strcmp(argv[0],"get-serialno") ||
|
||||
!strcmp(argv[0],"get-devpath"))
|
||||
{
|
||||
else if (!strcmp(argv[0], "get-state") || !strcmp(argv[0], "get-serialno") ||
|
||||
!strcmp(argv[0], "get-devpath")) {
|
||||
return adb_query_command(format_host_command(argv[0]));
|
||||
}
|
||||
/* other commands */
|
||||
else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
|
||||
else if (!strcmp(argv[0], "logcat") || !strcmp(argv[0], "lolcat") ||
|
||||
!strcmp(argv[0], "longcat")) {
|
||||
return logcat(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0],"ppp")) {
|
||||
} else if (!strcmp(argv[0], "ppp")) {
|
||||
return ppp(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "start-server")) {
|
||||
} else if (!strcmp(argv[0], "start-server")) {
|
||||
std::string error;
|
||||
const int result = adb_connect("host:start-server", &error);
|
||||
if (result < 0) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (!strcmp(argv[0], "backup")) {
|
||||
} else if (!strcmp(argv[0], "backup")) {
|
||||
return backup(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "restore")) {
|
||||
} else if (!strcmp(argv[0], "restore")) {
|
||||
return restore(argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "keygen")) {
|
||||
} else if (!strcmp(argv[0], "keygen")) {
|
||||
if (argc != 2) error_exit("keygen requires an argument");
|
||||
// Always print key generation information for keygen command.
|
||||
adb_trace_enable(AUTH);
|
||||
|
|
92
adb/daemon/abb.cpp
Normal file
92
adb/daemon/abb.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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 "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "shell_service.h"
|
||||
|
||||
#include "cmd.h"
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
namespace {
|
||||
|
||||
class AdbFdTextOutput : public android::TextOutput {
|
||||
public:
|
||||
explicit AdbFdTextOutput(int fd) : mFD(fd) {}
|
||||
|
||||
private:
|
||||
android::status_t print(const char* txt, size_t len) override {
|
||||
return WriteFdExactly(mFD, txt, len) ? android::OK : -errno;
|
||||
}
|
||||
void moveIndent(int delta) override { /*not implemented*/
|
||||
}
|
||||
|
||||
void pushBundle() override { /*not implemented*/
|
||||
}
|
||||
void popBundle() override { /*not implemented*/
|
||||
}
|
||||
|
||||
private:
|
||||
int mFD;
|
||||
};
|
||||
|
||||
std::vector<std::string_view> parseCmdArgs(std::string_view args) {
|
||||
std::vector<std::string_view> argv;
|
||||
|
||||
char delim = ABB_ARG_DELIMETER;
|
||||
size_t size = args.size();
|
||||
size_t base = 0;
|
||||
while (base < size) {
|
||||
size_t found;
|
||||
for (found = base; found < size && args[found] && args[found] != delim; ++found)
|
||||
;
|
||||
if (found > base) {
|
||||
argv.emplace_back(args.substr(base, found - base));
|
||||
}
|
||||
base = found + 1;
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static int execCmd(std::string_view args, int in, int out, int err) {
|
||||
AdbFdTextOutput oin(out);
|
||||
AdbFdTextOutput oerr(err);
|
||||
return cmdMain(parseCmdArgs(args), oin, oerr, in, out, err, RunMode::kLibrary);
|
||||
}
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
int fd = STDIN_FILENO;
|
||||
std::string data;
|
||||
while (true) {
|
||||
std::string error;
|
||||
if (!ReadProtocolString(fd, &data, &error)) {
|
||||
PLOG(ERROR) << "Failed to read message: " << error;
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = StartCommandInProcess(std::move(data), &execCmd);
|
||||
if (!SendFileDescriptor(fd, result)) {
|
||||
PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
88
adb/daemon/abb_service.cpp
Normal file
88
adb/daemon/abb_service.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "shell_service.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct AbbProcess;
|
||||
static auto& abbp = *new std::unique_ptr<AbbProcess>(std::make_unique<AbbProcess>());
|
||||
|
||||
struct AbbProcess {
|
||||
unique_fd sendCommand(std::string_view command);
|
||||
|
||||
private:
|
||||
static unique_fd startAbbProcess(unique_fd* error_fd);
|
||||
|
||||
static constexpr auto kRetries = 2;
|
||||
static constexpr auto kErrorProtocol = SubprocessProtocol::kShell;
|
||||
|
||||
std::mutex locker_;
|
||||
unique_fd socket_fd_;
|
||||
};
|
||||
|
||||
unique_fd AbbProcess::sendCommand(std::string_view command) {
|
||||
std::unique_lock lock{locker_};
|
||||
|
||||
for (int i = 0; i < kRetries; ++i) {
|
||||
unique_fd error_fd;
|
||||
if (socket_fd_ == -1) {
|
||||
socket_fd_ = startAbbProcess(&error_fd);
|
||||
}
|
||||
if (socket_fd_ == -1) {
|
||||
LOG(ERROR) << "failed to start abb process";
|
||||
return error_fd;
|
||||
}
|
||||
|
||||
if (!SendProtocolString(socket_fd_, std::string(command))) {
|
||||
PLOG(ERROR) << "failed to send command to abb";
|
||||
socket_fd_.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
unique_fd fd;
|
||||
std::string error;
|
||||
if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
|
||||
LOG(ERROR) << "failed to receive FD from abb: " << error;
|
||||
socket_fd_.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "abb is unavailable";
|
||||
socket_fd_.reset();
|
||||
return ReportError(kErrorProtocol, "abb is unavailable");
|
||||
}
|
||||
|
||||
unique_fd AbbProcess::startAbbProcess(unique_fd* error_fd) {
|
||||
constexpr auto abb_process_type = SubprocessType::kRaw;
|
||||
constexpr auto abb_protocol = SubprocessProtocol::kNone;
|
||||
constexpr auto make_pty_raw = false;
|
||||
return StartSubprocess("abb", "dumb", abb_process_type, abb_protocol, make_pty_raw,
|
||||
kErrorProtocol, error_fd);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
unique_fd execute_binder_command(std::string_view command) {
|
||||
return abbp->sendCommand(command);
|
||||
}
|
|
@ -237,30 +237,7 @@ static void jdwp_process_event(int socket, unsigned events, void* _proc) {
|
|||
CHECK(!proc->out_fds.empty());
|
||||
|
||||
int fd = proc->out_fds.back().get();
|
||||
struct cmsghdr* cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char dummy = '!';
|
||||
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
|
||||
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = buffer;
|
||||
msg.msg_controllen = sizeof(buffer);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = msg.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)CMSG_DATA(cmsg))[0] = fd;
|
||||
|
||||
int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
|
||||
if (ret < 0) {
|
||||
if (!SendFileDescriptor(socket, fd)) {
|
||||
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
|
|
@ -328,6 +328,13 @@ asocket* daemon_service_to_socket(std::string_view name) {
|
|||
}
|
||||
|
||||
unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
|
||||
#ifndef __ANDROID_RECOVERY__
|
||||
if (name.starts_with("abb:")) {
|
||||
name.remove_prefix(strlen("abb:"));
|
||||
return execute_binder_command(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (name.starts_with("dev:")) {
|
||||
name.remove_prefix(strlen("dev:"));
|
||||
return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
|
||||
|
|
|
@ -141,7 +141,7 @@ bool CreateSocketpair(unique_fd* fd1, unique_fd* fd2) {
|
|||
class Subprocess {
|
||||
public:
|
||||
Subprocess(std::string command, const char* terminal_type, SubprocessType type,
|
||||
SubprocessProtocol protocol);
|
||||
SubprocessProtocol protocol, bool make_pty_raw);
|
||||
~Subprocess();
|
||||
|
||||
const std::string& command() const { return command_; }
|
||||
|
@ -154,6 +154,10 @@ class Subprocess {
|
|||
// and exec's the child. Returns false and sets error on failure.
|
||||
bool ForkAndExec(std::string* _Nonnull error);
|
||||
|
||||
// Sets up FDs, starts a thread executing command and the manager thread,
|
||||
// Returns false and sets error on failure.
|
||||
bool ExecInProcess(Command command, std::string* _Nonnull error);
|
||||
|
||||
// Start the subprocess manager thread. Consumes the subprocess, regardless of success.
|
||||
// Returns false and sets error on failure.
|
||||
static bool StartThread(std::unique_ptr<Subprocess> subprocess,
|
||||
|
@ -177,9 +181,9 @@ class Subprocess {
|
|||
|
||||
const std::string command_;
|
||||
const std::string terminal_type_;
|
||||
bool make_pty_raw_ = false;
|
||||
SubprocessType type_;
|
||||
SubprocessProtocol protocol_;
|
||||
bool make_pty_raw_;
|
||||
pid_t pid_ = -1;
|
||||
unique_fd local_socket_sfd_;
|
||||
|
||||
|
@ -192,24 +196,12 @@ class Subprocess {
|
|||
};
|
||||
|
||||
Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
|
||||
SubprocessProtocol protocol)
|
||||
SubprocessProtocol protocol, bool make_pty_raw)
|
||||
: command_(std::move(command)),
|
||||
terminal_type_(terminal_type ? terminal_type : ""),
|
||||
type_(type),
|
||||
protocol_(protocol) {
|
||||
// If we aren't using the shell protocol we must allocate a PTY to properly close the
|
||||
// subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
|
||||
// of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
|
||||
// e.g. screenrecord, will never notice the broken pipe and terminate.
|
||||
// The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
|
||||
// with select() and will send SIGHUP manually to the child process.
|
||||
if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
|
||||
// Disable PTY input/output processing since the client is expecting raw data.
|
||||
D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
|
||||
type_ = SubprocessType::kPty;
|
||||
make_pty_raw_ = true;
|
||||
}
|
||||
}
|
||||
protocol_(protocol),
|
||||
make_pty_raw_(make_pty_raw) {}
|
||||
|
||||
Subprocess::~Subprocess() {
|
||||
WaitForExit();
|
||||
|
@ -430,6 +422,67 @@ bool Subprocess::ForkAndExec(std::string* error) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
|
||||
unique_fd child_stdinout_sfd, child_stderr_sfd;
|
||||
|
||||
CHECK(type_ == SubprocessType::kRaw);
|
||||
CHECK(protocol_ == SubprocessProtocol::kShell);
|
||||
|
||||
__android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
|
||||
|
||||
if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
|
||||
*error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
// Raw subprocess + shell protocol allows for splitting stderr.
|
||||
if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
|
||||
*error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
|
||||
stderr_sfd_.get());
|
||||
|
||||
// Required for shell protocol: create another socketpair to intercept data.
|
||||
if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
|
||||
*error = android::base::StringPrintf("failed to create socketpair to intercept data: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
D("protocol FD = %d", protocol_sfd_.get());
|
||||
|
||||
input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
|
||||
output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
|
||||
if (!input_ || !output_) {
|
||||
*error = "failed to allocate shell protocol objects";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let reads/writes to the subprocess block our thread. This isn't
|
||||
// likely but could happen under unusual circumstances, such as if we
|
||||
// write a ton of data to stdin but the subprocess never reads it and
|
||||
// the pipe fills up.
|
||||
for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
|
||||
if (fd >= 0) {
|
||||
if (!set_file_block_mode(fd, false)) {
|
||||
*error = android::base::StringPrintf("failed to set non-blocking mode for fd %d",
|
||||
fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
|
||||
command = std::move(command),
|
||||
args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
|
||||
.detach();
|
||||
|
||||
D("execinprocess: completed");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
|
||||
Subprocess* raw = subprocess.release();
|
||||
std::thread(ThreadHandler, raw).detach();
|
||||
|
@ -512,7 +565,9 @@ void Subprocess::PassDataStreams() {
|
|||
// needed (e.g. SIGINT), pass those through the shell protocol
|
||||
// and only fall back on this for unexpected closures.
|
||||
D("protocol FD died, sending SIGHUP to pid %d", pid_);
|
||||
kill(pid_, SIGHUP);
|
||||
if (pid_ != -1) {
|
||||
kill(pid_, SIGHUP);
|
||||
}
|
||||
|
||||
// We also need to close the pipes connected to the child process
|
||||
// so that if it ignores SIGHUP and continues to write data it
|
||||
|
@ -682,7 +737,7 @@ void Subprocess::WaitForExit() {
|
|||
int exit_code = 1;
|
||||
|
||||
D("waiting for pid %d", pid_);
|
||||
while (true) {
|
||||
while (pid_ != -1) {
|
||||
int status;
|
||||
if (pid_ == waitpid(pid_, &status, 0)) {
|
||||
D("post waitpid (pid=%d) status=%04x", pid_, status);
|
||||
|
@ -716,7 +771,7 @@ void Subprocess::WaitForExit() {
|
|||
} // namespace
|
||||
|
||||
// Create a pipe containing the error.
|
||||
static unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
|
||||
unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
|
||||
unique_fd read, write;
|
||||
if (!Pipe(&read, &write)) {
|
||||
PLOG(ERROR) << "failed to create pipe to report error";
|
||||
|
@ -747,20 +802,49 @@ static unique_fd ReportError(SubprocessProtocol protocol, const std::string& mes
|
|||
|
||||
unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
|
||||
SubprocessProtocol protocol) {
|
||||
// If we aren't using the shell protocol we must allocate a PTY to properly close the
|
||||
// subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
|
||||
// of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
|
||||
// e.g. screenrecord, will never notice the broken pipe and terminate.
|
||||
// The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
|
||||
// with select() and will send SIGHUP manually to the child process.
|
||||
bool make_pty_raw = false;
|
||||
if (protocol == SubprocessProtocol::kNone && type == SubprocessType::kRaw) {
|
||||
// Disable PTY input/output processing since the client is expecting raw data.
|
||||
D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
|
||||
type = SubprocessType::kPty;
|
||||
make_pty_raw = true;
|
||||
}
|
||||
|
||||
unique_fd error_fd;
|
||||
unique_fd fd = StartSubprocess(std::move(name), terminal_type, type, protocol, make_pty_raw,
|
||||
protocol, &error_fd);
|
||||
if (fd == -1) {
|
||||
return error_fd;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
|
||||
SubprocessProtocol protocol, bool make_pty_raw,
|
||||
SubprocessProtocol error_protocol, unique_fd* error_fd) {
|
||||
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
|
||||
type == SubprocessType::kRaw ? "raw" : "PTY",
|
||||
protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
|
||||
|
||||
auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol);
|
||||
auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
|
||||
make_pty_raw);
|
||||
if (!subprocess) {
|
||||
LOG(ERROR) << "failed to allocate new subprocess";
|
||||
return ReportError(protocol, "failed to allocate new subprocess");
|
||||
*error_fd = ReportError(error_protocol, "failed to allocate new subprocess");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string error;
|
||||
if (!subprocess->ForkAndExec(&error)) {
|
||||
LOG(ERROR) << "failed to start subprocess: " << error;
|
||||
return ReportError(protocol, error);
|
||||
*error_fd = ReportError(error_protocol, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
unique_fd local_socket(subprocess->ReleaseLocalSocket());
|
||||
|
@ -769,6 +853,40 @@ unique_fd StartSubprocess(std::string name, const char* terminal_type, Subproces
|
|||
|
||||
if (!Subprocess::StartThread(std::move(subprocess), &error)) {
|
||||
LOG(ERROR) << "failed to start subprocess management thread: " << error;
|
||||
*error_fd = ReportError(error_protocol, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
return local_socket;
|
||||
}
|
||||
|
||||
unique_fd StartCommandInProcess(std::string name, Command command) {
|
||||
LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
|
||||
|
||||
constexpr auto terminal_type = "";
|
||||
constexpr auto type = SubprocessType::kRaw;
|
||||
constexpr auto protocol = SubprocessProtocol::kShell;
|
||||
constexpr auto make_pty_raw = false;
|
||||
|
||||
auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
|
||||
make_pty_raw);
|
||||
if (!subprocess) {
|
||||
LOG(ERROR) << "failed to allocate new subprocess";
|
||||
return ReportError(protocol, "failed to allocate new subprocess");
|
||||
}
|
||||
|
||||
std::string error;
|
||||
if (!subprocess->ExecInProcess(std::move(command), &error)) {
|
||||
LOG(ERROR) << "failed to start subprocess: " << error;
|
||||
return ReportError(protocol, error);
|
||||
}
|
||||
|
||||
unique_fd local_socket(subprocess->ReleaseLocalSocket());
|
||||
D("inprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
|
||||
subprocess->pid());
|
||||
|
||||
if (!Subprocess::StartThread(std::move(subprocess), &error)) {
|
||||
LOG(ERROR) << "failed to start inprocess management thread: " << error;
|
||||
return ReportError(protocol, error);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "adb_unique_fd.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
enum class SubprocessType {
|
||||
kPty,
|
||||
kRaw,
|
||||
|
@ -36,3 +38,18 @@ enum class SubprocessProtocol {
|
|||
// Returns an open FD connected to the subprocess or -1 on failure.
|
||||
unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
|
||||
SubprocessProtocol protocol);
|
||||
|
||||
// The same as above but with more fined grained control and custom error handling.
|
||||
unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
|
||||
SubprocessProtocol protocol, bool make_pty_raw,
|
||||
SubprocessProtocol error_protocol, unique_fd* error_fd);
|
||||
|
||||
// Executes |command| in a separate thread.
|
||||
// Sets up in/out and error streams to emulate shell-like behavior.
|
||||
//
|
||||
// Returns an open FD connected to the thread or -1 on failure.
|
||||
using Command = int(std::string_view args, int in, int out, int err);
|
||||
unique_fd StartCommandInProcess(std::string name, Command command);
|
||||
|
||||
// Create a pipe containing the error.
|
||||
unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
|
||||
|
|
Loading…
Reference in a new issue