2021-01-03 21:55:15 +08:00
|
|
|
#include "adb.h"
|
2017-12-12 22:12:07 +08:00
|
|
|
|
2019-05-12 21:11:16 +08:00
|
|
|
#include <assert.h>
|
2017-12-12 22:12:07 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2018-02-13 17:10:18 +08:00
|
|
|
|
2021-11-26 05:11:59 +08:00
|
|
|
#include "adb_parser.h"
|
2021-11-11 23:12:17 +08:00
|
|
|
#include "util/file.h"
|
2019-11-24 18:53:00 +08:00
|
|
|
#include "util/log.h"
|
2021-11-19 05:08:15 +08:00
|
|
|
#include "util/process_intr.h"
|
2021-11-13 06:12:51 +08:00
|
|
|
#include "util/str.h"
|
2017-12-12 22:12:07 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
/* Convenience macro to expand:
|
|
|
|
*
|
|
|
|
* const char *const argv[] =
|
|
|
|
* SC_ADB_COMMAND("shell", "echo", "hello");
|
|
|
|
*
|
|
|
|
* to:
|
|
|
|
*
|
|
|
|
* const char *const argv[] =
|
|
|
|
* { sc_adb_get_executable(), "shell", "echo", "hello", NULL };
|
|
|
|
*/
|
|
|
|
#define SC_ADB_COMMAND(...) { sc_adb_get_executable(), __VA_ARGS__, NULL }
|
|
|
|
|
2022-02-05 16:21:53 +08:00
|
|
|
static const char *adb_executable;
|
2018-02-01 01:46:56 +08:00
|
|
|
|
2022-02-05 16:37:00 +08:00
|
|
|
const char *
|
|
|
|
sc_adb_get_executable(void) {
|
2022-02-05 16:21:53 +08:00
|
|
|
if (!adb_executable) {
|
|
|
|
adb_executable = getenv("ADB");
|
|
|
|
if (!adb_executable)
|
|
|
|
adb_executable = "adb";
|
2018-02-01 01:46:56 +08:00
|
|
|
}
|
2022-02-05 16:21:53 +08:00
|
|
|
return adb_executable;
|
2018-02-01 01:46:56 +08:00
|
|
|
}
|
|
|
|
|
2019-05-12 21:11:16 +08:00
|
|
|
// serialize argv to string "[arg1], [arg2], [arg3]"
|
|
|
|
static size_t
|
|
|
|
argv_to_string(const char *const *argv, char *buf, size_t bufsize) {
|
|
|
|
size_t idx = 0;
|
|
|
|
bool first = true;
|
|
|
|
while (*argv) {
|
|
|
|
const char *arg = *argv;
|
|
|
|
size_t len = strlen(arg);
|
|
|
|
// count space for "[], ...\0"
|
|
|
|
if (idx + len + 8 >= bufsize) {
|
|
|
|
// not enough space, truncate
|
|
|
|
assert(idx < bufsize - 4);
|
|
|
|
memcpy(&buf[idx], "...", 3);
|
|
|
|
idx += 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
buf[idx++] = ',';
|
|
|
|
buf[idx++] = ' ';
|
|
|
|
}
|
|
|
|
buf[idx++] = '[';
|
|
|
|
memcpy(&buf[idx], arg, len);
|
|
|
|
idx += len;
|
|
|
|
buf[idx++] = ']';
|
|
|
|
argv++;
|
|
|
|
}
|
|
|
|
assert(idx < bufsize);
|
|
|
|
buf[idx] = '\0';
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2020-01-16 02:47:34 +08:00
|
|
|
static void
|
|
|
|
show_adb_installation_msg() {
|
|
|
|
#ifndef __WINDOWS__
|
|
|
|
static const struct {
|
|
|
|
const char *binary;
|
|
|
|
const char *command;
|
|
|
|
} pkg_managers[] = {
|
|
|
|
{"apt", "apt install adb"},
|
|
|
|
{"apt-get", "apt-get install adb"},
|
|
|
|
{"brew", "brew cask install android-platform-tools"},
|
|
|
|
{"dnf", "dnf install android-tools"},
|
|
|
|
{"emerge", "emerge dev-util/android-tools"},
|
|
|
|
{"pacman", "pacman -S android-tools"},
|
|
|
|
};
|
|
|
|
for (size_t i = 0; i < ARRAY_LEN(pkg_managers); ++i) {
|
2021-11-11 23:21:07 +08:00
|
|
|
if (sc_file_executable_exists(pkg_managers[i].binary)) {
|
2020-01-16 02:47:34 +08:00
|
|
|
LOGI("You may install 'adb' by \"%s\"", pkg_managers[i].command);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
2021-11-12 00:48:41 +08:00
|
|
|
show_adb_err_msg(enum sc_process_result err, const char *const argv[]) {
|
2021-06-19 23:47:57 +08:00
|
|
|
#define MAX_COMMAND_STRING_LEN 1024
|
|
|
|
char *buf = malloc(MAX_COMMAND_STRING_LEN);
|
|
|
|
if (!buf) {
|
2021-11-25 05:06:11 +08:00
|
|
|
LOG_OOM();
|
|
|
|
LOGE("Failed to execute");
|
2021-06-19 23:47:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-01 09:18:06 +08:00
|
|
|
switch (err) {
|
2021-11-12 00:48:41 +08:00
|
|
|
case SC_PROCESS_ERROR_GENERIC:
|
2021-06-19 23:47:57 +08:00
|
|
|
argv_to_string(argv, buf, MAX_COMMAND_STRING_LEN);
|
2019-05-12 21:11:16 +08:00
|
|
|
LOGE("Failed to execute: %s", buf);
|
2018-09-01 09:18:06 +08:00
|
|
|
break;
|
2021-11-12 00:48:41 +08:00
|
|
|
case SC_PROCESS_ERROR_MISSING_BINARY:
|
2021-06-19 23:47:57 +08:00
|
|
|
argv_to_string(argv, buf, MAX_COMMAND_STRING_LEN);
|
2019-05-12 21:11:16 +08:00
|
|
|
LOGE("Command not found: %s", buf);
|
|
|
|
LOGE("(make 'adb' accessible from your PATH or define its full"
|
|
|
|
"path in the ADB environment variable)");
|
2020-01-16 02:47:34 +08:00
|
|
|
show_adb_installation_msg();
|
2018-09-01 09:18:06 +08:00
|
|
|
break;
|
2021-11-12 00:48:41 +08:00
|
|
|
case SC_PROCESS_SUCCESS:
|
2019-06-01 04:33:39 +08:00
|
|
|
// do nothing
|
2018-09-01 09:18:06 +08:00
|
|
|
break;
|
|
|
|
}
|
2021-06-19 23:47:57 +08:00
|
|
|
|
|
|
|
free(buf);
|
2018-09-01 09:18:06 +08:00
|
|
|
}
|
|
|
|
|
2021-11-25 16:37:53 +08:00
|
|
|
static bool
|
2021-11-26 05:05:38 +08:00
|
|
|
process_check_success_internal(sc_pid pid, const char *name, bool close,
|
|
|
|
unsigned flags) {
|
|
|
|
bool log_errors = !(flags & SC_ADB_NO_LOGERR);
|
|
|
|
|
2021-11-25 16:37:53 +08:00
|
|
|
if (pid == SC_PROCESS_NONE) {
|
2021-11-26 05:05:38 +08:00
|
|
|
if (log_errors) {
|
|
|
|
LOGE("Could not execute \"%s\"", name);
|
|
|
|
}
|
2021-11-25 16:37:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
sc_exit_code exit_code = sc_process_wait(pid, close);
|
|
|
|
if (exit_code) {
|
2021-11-26 05:05:38 +08:00
|
|
|
if (log_errors) {
|
|
|
|
if (exit_code != SC_EXIT_CODE_NONE) {
|
|
|
|
LOGE("\"%s\" returned with value %" SC_PRIexitcode, name,
|
|
|
|
exit_code);
|
|
|
|
} else {
|
|
|
|
LOGE("\"%s\" exited unexpectedly", name);
|
|
|
|
}
|
2021-11-25 16:37:53 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2021-11-26 05:05:38 +08:00
|
|
|
process_check_success_intr(struct sc_intr *intr, sc_pid pid, const char *name,
|
|
|
|
unsigned flags) {
|
2021-11-25 16:37:53 +08:00
|
|
|
if (!sc_intr_set_process(intr, pid)) {
|
|
|
|
// Already interrupted
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always pass close=false, interrupting would be racy otherwise
|
2021-11-26 05:05:38 +08:00
|
|
|
bool ret = process_check_success_internal(pid, name, false, flags);
|
2021-11-25 16:37:53 +08:00
|
|
|
|
|
|
|
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
|
|
|
|
|
|
|
// Close separately
|
|
|
|
sc_process_close(pid);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-11-25 15:57:31 +08:00
|
|
|
static sc_pid
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_adb_execute_p(const char *const argv[], unsigned flags, sc_pipe *pout) {
|
2021-11-25 05:27:28 +08:00
|
|
|
unsigned process_flags = 0;
|
|
|
|
if (flags & SC_ADB_NO_STDOUT) {
|
|
|
|
process_flags |= SC_PROCESS_NO_STDOUT;
|
|
|
|
}
|
|
|
|
if (flags & SC_ADB_NO_STDERR) {
|
|
|
|
process_flags |= SC_PROCESS_NO_STDERR;
|
|
|
|
}
|
|
|
|
|
2021-11-25 15:57:31 +08:00
|
|
|
sc_pid pid;
|
2021-11-12 00:48:41 +08:00
|
|
|
enum sc_process_result r =
|
2021-11-25 05:27:28 +08:00
|
|
|
sc_process_execute_p(argv, &pid, process_flags, NULL, pout, NULL);
|
2021-11-12 00:48:41 +08:00
|
|
|
if (r != SC_PROCESS_SUCCESS) {
|
2021-11-25 05:27:28 +08:00
|
|
|
// If the execution itself failed (not the command exit code), log the
|
|
|
|
// error in all cases
|
2021-06-19 23:47:57 +08:00
|
|
|
show_adb_err_msg(r, argv);
|
2021-11-12 00:48:41 +08:00
|
|
|
pid = SC_PROCESS_NONE;
|
2018-09-01 09:18:06 +08:00
|
|
|
}
|
2021-06-19 23:47:57 +08:00
|
|
|
|
2021-11-12 00:48:41 +08:00
|
|
|
return pid;
|
2017-12-12 22:12:07 +08:00
|
|
|
}
|
|
|
|
|
2021-11-12 00:48:41 +08:00
|
|
|
sc_pid
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_adb_execute(const char *const argv[], unsigned flags) {
|
|
|
|
return sc_adb_execute_p(argv, flags, NULL);
|
2021-10-17 22:14:33 +08:00
|
|
|
}
|
|
|
|
|
2022-02-06 22:28:23 +08:00
|
|
|
bool
|
|
|
|
sc_adb_start_server(struct sc_intr *intr, unsigned flags) {
|
|
|
|
const char *const argv[] = SC_ADB_COMMAND("start-server");
|
|
|
|
|
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
|
|
|
return process_check_success_intr(intr, pid, "adb start-server", flags);
|
|
|
|
}
|
|
|
|
|
2021-11-26 04:58:51 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
|
|
|
const char *device_socket_name, unsigned flags) {
|
2017-12-12 22:12:07 +08:00
|
|
|
char local[4 + 5 + 1]; // tcp:PORT
|
|
|
|
char remote[108 + 14 + 1]; // localabstract:NAME
|
|
|
|
sprintf(local, "tcp:%" PRIu16, local_port);
|
|
|
|
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
|
2022-02-05 16:51:18 +08:00
|
|
|
|
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "forward", local, remote);
|
2021-11-26 04:58:51 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2021-11-26 05:05:38 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb forward", flags);
|
2017-12-12 22:12:07 +08:00
|
|
|
}
|
|
|
|
|
2021-11-26 04:58:51 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_forward_remove(struct sc_intr *intr, const char *serial,
|
|
|
|
uint16_t local_port, unsigned flags) {
|
2018-03-12 15:35:51 +08:00
|
|
|
char local[4 + 5 + 1]; // tcp:PORT
|
|
|
|
sprintf(local, "tcp:%" PRIu16, local_port);
|
2022-02-05 16:51:18 +08:00
|
|
|
|
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "forward", "--remove", local);
|
2021-11-26 04:58:51 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2021-11-26 05:05:38 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb forward --remove", flags);
|
2018-03-12 15:35:51 +08:00
|
|
|
}
|
|
|
|
|
2021-11-26 04:58:51 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_reverse(struct sc_intr *intr, const char *serial,
|
|
|
|
const char *device_socket_name, uint16_t local_port,
|
|
|
|
unsigned flags) {
|
2017-12-12 22:12:07 +08:00
|
|
|
char local[4 + 5 + 1]; // tcp:PORT
|
|
|
|
char remote[108 + 14 + 1]; // localabstract:NAME
|
|
|
|
sprintf(local, "tcp:%" PRIu16, local_port);
|
|
|
|
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
|
2022-02-05 16:51:18 +08:00
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "reverse", remote, local);
|
2021-11-26 04:58:51 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2021-11-26 05:05:38 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb reverse", flags);
|
2017-12-12 22:12:07 +08:00
|
|
|
}
|
|
|
|
|
2021-11-26 04:58:51 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_reverse_remove(struct sc_intr *intr, const char *serial,
|
|
|
|
const char *device_socket_name, unsigned flags) {
|
2017-12-12 22:12:07 +08:00
|
|
|
char remote[108 + 14 + 1]; // localabstract:NAME
|
|
|
|
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
|
2022-02-05 16:51:18 +08:00
|
|
|
|
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "reverse", "--remove", remote);
|
2021-11-26 04:58:51 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2021-11-26 05:05:38 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb reverse --remove", flags);
|
2017-12-12 22:12:07 +08:00
|
|
|
}
|
|
|
|
|
2021-11-26 04:58:51 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_push(struct sc_intr *intr, const char *serial, const char *local,
|
|
|
|
const char *remote, unsigned flags) {
|
2018-10-05 02:47:53 +08:00
|
|
|
#ifdef __WINDOWS__
|
|
|
|
// Windows will parse the string, so the paths must be quoted
|
|
|
|
// (see sys/win/command.c)
|
2021-11-13 06:08:19 +08:00
|
|
|
local = sc_str_quote(local);
|
2018-10-05 02:47:53 +08:00
|
|
|
if (!local) {
|
2021-11-12 00:48:41 +08:00
|
|
|
return SC_PROCESS_NONE;
|
2018-10-05 02:47:53 +08:00
|
|
|
}
|
2021-11-13 06:08:19 +08:00
|
|
|
remote = sc_str_quote(remote);
|
2018-10-05 02:47:53 +08:00
|
|
|
if (!remote) {
|
2021-01-24 22:14:53 +08:00
|
|
|
free((void *) local);
|
2021-11-12 00:48:41 +08:00
|
|
|
return SC_PROCESS_NONE;
|
2018-10-05 02:47:53 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-02-05 16:51:18 +08:00
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "push", local, remote);
|
2022-02-05 16:51:18 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2018-10-05 02:47:53 +08:00
|
|
|
|
|
|
|
#ifdef __WINDOWS__
|
2021-01-24 22:14:53 +08:00
|
|
|
free((void *) remote);
|
|
|
|
free((void *) local);
|
2018-10-05 02:47:53 +08:00
|
|
|
#endif
|
|
|
|
|
2021-11-26 05:05:38 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb push", flags);
|
2017-12-12 22:12:07 +08:00
|
|
|
}
|
2018-02-08 22:16:27 +08:00
|
|
|
|
2021-11-26 04:58:51 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_install(struct sc_intr *intr, const char *serial, const char *local,
|
|
|
|
unsigned flags) {
|
2018-05-26 20:43:42 +08:00
|
|
|
#ifdef __WINDOWS__
|
2018-10-05 02:47:53 +08:00
|
|
|
// Windows will parse the string, so the local name must be quoted
|
|
|
|
// (see sys/win/command.c)
|
2021-11-13 06:08:19 +08:00
|
|
|
local = sc_str_quote(local);
|
2018-10-05 02:47:53 +08:00
|
|
|
if (!local) {
|
2021-11-12 00:48:41 +08:00
|
|
|
return SC_PROCESS_NONE;
|
2018-10-05 02:47:53 +08:00
|
|
|
}
|
2018-05-26 20:43:42 +08:00
|
|
|
#endif
|
2018-10-05 02:47:53 +08:00
|
|
|
|
2022-02-05 16:51:18 +08:00
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "install", "-r", local);
|
2022-02-05 16:51:18 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2018-10-05 02:47:53 +08:00
|
|
|
|
|
|
|
#ifdef __WINDOWS__
|
2021-01-24 22:14:53 +08:00
|
|
|
free((void *) local);
|
2018-10-05 02:47:53 +08:00
|
|
|
#endif
|
|
|
|
|
2021-11-26 05:05:38 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb install", flags);
|
2021-11-19 05:08:15 +08:00
|
|
|
}
|
|
|
|
|
2021-11-26 05:13:39 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_tcpip(struct sc_intr *intr, const char *serial, uint16_t port,
|
|
|
|
unsigned flags) {
|
2021-11-26 05:13:39 +08:00
|
|
|
char port_string[5 + 1];
|
|
|
|
sprintf(port_string, "%" PRIu16, port);
|
2022-02-05 16:51:18 +08:00
|
|
|
|
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "tcpip", port_string);
|
2021-11-26 05:13:39 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2021-11-26 05:13:39 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb tcpip", flags);
|
|
|
|
}
|
|
|
|
|
2021-11-25 05:39:46 +08:00
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_connect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] = SC_ADB_COMMAND("connect", ip_port);
|
2021-11-25 05:39:46 +08:00
|
|
|
|
2021-11-26 04:33:50 +08:00
|
|
|
sc_pipe pout;
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
|
2021-11-26 04:33:50 +08:00
|
|
|
if (pid == SC_PROCESS_NONE) {
|
|
|
|
LOGE("Could not execute \"adb connect\"");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "adb connect" always returns successfully (with exit code 0), even in
|
|
|
|
// case of failure. As a workaround, check if its output starts with
|
|
|
|
// "connected".
|
|
|
|
char buf[128];
|
2022-02-06 19:22:17 +08:00
|
|
|
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
2021-11-26 04:33:50 +08:00
|
|
|
sc_pipe_close(pout);
|
|
|
|
|
|
|
|
bool ok = process_check_success_intr(intr, pid, "adb connect", flags);
|
|
|
|
if (!ok) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-06 19:22:17 +08:00
|
|
|
assert((size_t) r < sizeof(buf));
|
|
|
|
buf[r] = '\0';
|
|
|
|
|
2021-11-26 04:33:50 +08:00
|
|
|
ok = !strncmp("connected", buf, sizeof("connected") - 1);
|
|
|
|
if (!ok && !(flags & SC_ADB_NO_STDERR)) {
|
|
|
|
// "adb connect" also prints errors to stdout. Since we capture it,
|
|
|
|
// re-print the error to stderr.
|
2022-02-06 19:22:17 +08:00
|
|
|
size_t len = strcspn(buf, "\r\n");
|
|
|
|
buf[len] = '\0';
|
2021-11-26 04:33:50 +08:00
|
|
|
fprintf(stderr, "%s\n", buf);
|
|
|
|
}
|
|
|
|
return ok;
|
2021-11-25 05:39:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
|
2022-02-05 17:11:13 +08:00
|
|
|
assert(ip_port);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] = SC_ADB_COMMAND("disconnect", ip_port);
|
2021-11-25 05:39:46 +08:00
|
|
|
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute(argv, flags);
|
2021-11-25 05:39:46 +08:00
|
|
|
return process_check_success_intr(intr, pid, "adb disconnect", flags);
|
|
|
|
}
|
|
|
|
|
2022-02-06 22:04:00 +08:00
|
|
|
static ssize_t
|
|
|
|
sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
|
|
|
|
struct sc_adb_device *devices, size_t len) {
|
|
|
|
const char *const argv[] = SC_ADB_COMMAND("devices", "-l");
|
|
|
|
|
|
|
|
sc_pipe pout;
|
|
|
|
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
|
|
|
|
if (pid == SC_PROCESS_NONE) {
|
|
|
|
LOGE("Could not execute \"adb devices -l\"");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[4096];
|
|
|
|
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
|
|
|
sc_pipe_close(pout);
|
|
|
|
|
|
|
|
bool ok = process_check_success_intr(intr, pid, "adb devices -l", flags);
|
|
|
|
if (!ok) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert((size_t) r < sizeof(buf));
|
|
|
|
if (r == sizeof(buf) - 1) {
|
|
|
|
// The implementation assumes that the output of "adb devices -l" fits
|
|
|
|
// in the buffer in a single pass
|
|
|
|
LOGW("Result of \"adb devices -l\" does not fit in 4Kb. "
|
|
|
|
"Please report an issue.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// It is parsed as a NUL-terminated string
|
|
|
|
buf[r] = '\0';
|
|
|
|
|
|
|
|
// List all devices to the output list directly
|
|
|
|
return sc_adb_parse_devices(buf, devices, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
sc_adb_accept_device(const struct sc_adb_device *device, const char *serial) {
|
|
|
|
if (!serial) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-02-06 22:11:35 +08:00
|
|
|
char *device_serial_colon = strchr(device->serial, ':');
|
|
|
|
if (device_serial_colon) {
|
|
|
|
// The device serial is an IP:port...
|
|
|
|
char *serial_colon = strchr(serial, ':');
|
|
|
|
if (!serial_colon) {
|
|
|
|
// But the requested serial has no ':', so only consider the IP part
|
|
|
|
// of the device serial. This allows to use "192.168.1.1" to match
|
|
|
|
// any "192.168.1.1:port".
|
|
|
|
size_t serial_len = strlen(serial);
|
|
|
|
size_t device_ip_len = device_serial_colon - device->serial;
|
|
|
|
if (serial_len != device_ip_len) {
|
|
|
|
// They are not equal, they don't even have the same length
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return !strncmp(serial, device->serial, device_ip_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-06 22:04:00 +08:00
|
|
|
return !strcmp(serial, device->serial);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
sc_adb_devices_select(struct sc_adb_device *devices, size_t len,
|
|
|
|
const char *serial, size_t *idx_out) {
|
|
|
|
size_t count = 0;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
|
|
struct sc_adb_device *device = &devices[i];
|
|
|
|
device->selected = sc_adb_accept_device(device, serial);
|
|
|
|
if (device->selected) {
|
|
|
|
if (idx_out && !count) {
|
|
|
|
*idx_out = i;
|
|
|
|
}
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_adb_devices_log(enum sc_log_level level, struct sc_adb_device *devices,
|
|
|
|
size_t count) {
|
|
|
|
for (size_t i = 0; i < count; ++i) {
|
|
|
|
struct sc_adb_device *d = &devices[i];
|
|
|
|
const char *selection = d->selected ? "-->" : " ";
|
|
|
|
const char *type = sc_adb_is_serial_tcpip(d->serial) ? "(tcpip)"
|
|
|
|
: " (usb)";
|
|
|
|
LOG(level, " %s %s %-20s %16s %s",
|
|
|
|
selection, type, d->serial, d->state, d->model ? d->model : "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
sc_adb_device_check_state(struct sc_adb_device *device,
|
|
|
|
struct sc_adb_device *devices, size_t count) {
|
|
|
|
const char *state = device->state;
|
|
|
|
|
|
|
|
if (!strcmp("device", state)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp("unauthorized", state)) {
|
|
|
|
LOGE("Device is unauthorized:");
|
|
|
|
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
|
|
|
LOGE("A popup should open on the device to request authorization.");
|
|
|
|
LOGE("Check the FAQ: "
|
|
|
|
"<https://github.com/Genymobile/scrcpy/blob/master/FAQ.md>");
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
sc_adb_select_device(struct sc_intr *intr, const char *serial, unsigned flags,
|
|
|
|
struct sc_adb_device *out_device) {
|
|
|
|
struct sc_adb_device devices[16];
|
|
|
|
ssize_t count =
|
|
|
|
sc_adb_list_devices(intr, flags, devices, ARRAY_LEN(devices));
|
|
|
|
if (count == -1) {
|
|
|
|
LOGE("Could not list ADB devices");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
LOGE("Could not find any ADB device");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t sel_idx; // index of the single matching device if sel_count == 1
|
|
|
|
size_t sel_count = sc_adb_devices_select(devices, count, serial, &sel_idx);
|
|
|
|
|
|
|
|
if (sel_count == 0) {
|
|
|
|
// if count > 0 && sel_count == 0, then necessarily a serial is provided
|
|
|
|
assert(serial);
|
|
|
|
LOGE("Could not find ADB device %s", serial);
|
|
|
|
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
|
|
|
sc_adb_devices_destroy_all(devices, count);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sel_count > 1) {
|
|
|
|
if (serial) {
|
|
|
|
LOGE("Multiple (%" SC_PRIsizet ") ADB devices with serial %s:",
|
|
|
|
sel_count, serial);
|
|
|
|
} else {
|
|
|
|
LOGE("Multiple (%" SC_PRIsizet ") ADB devices:", sel_count);
|
|
|
|
}
|
|
|
|
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
|
|
|
LOGE("Select a device via -s (--serial)");
|
|
|
|
sc_adb_devices_destroy_all(devices, count);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
|
|
|
|
struct sc_adb_device *device = &devices[sel_idx];
|
|
|
|
|
|
|
|
bool ok = sc_adb_device_check_state(device, devices, count);
|
|
|
|
if (!ok) {
|
|
|
|
sc_adb_devices_destroy_all(devices, count);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGD("ADB device found:");
|
|
|
|
sc_adb_devices_log(SC_LOG_LEVEL_DEBUG, devices, count);
|
|
|
|
|
|
|
|
// Move devics into out_device (do not destroy device)
|
|
|
|
sc_adb_device_move(out_device, device);
|
|
|
|
sc_adb_devices_destroy_all(devices, count);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-26 05:18:36 +08:00
|
|
|
char *
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
|
|
|
|
unsigned flags) {
|
2022-02-05 16:51:18 +08:00
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "shell", "getprop", prop);
|
2021-11-26 05:18:36 +08:00
|
|
|
|
|
|
|
sc_pipe pout;
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
|
2021-11-26 05:18:36 +08:00
|
|
|
if (pid == SC_PROCESS_NONE) {
|
|
|
|
LOGE("Could not execute \"adb getprop\"");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[128];
|
2022-02-06 19:17:39 +08:00
|
|
|
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
2021-11-26 05:18:36 +08:00
|
|
|
sc_pipe_close(pout);
|
|
|
|
|
|
|
|
bool ok = process_check_success_intr(intr, pid, "adb getprop", flags);
|
|
|
|
if (!ok) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == -1) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-02-06 19:17:39 +08:00
|
|
|
assert((size_t) r < sizeof(buf));
|
|
|
|
buf[r] = '\0';
|
|
|
|
size_t len = strcspn(buf, " \r\n");
|
|
|
|
buf[len] = '\0';
|
2021-11-26 05:18:36 +08:00
|
|
|
|
|
|
|
return strdup(buf);
|
|
|
|
}
|
|
|
|
|
2021-11-26 05:11:59 +08:00
|
|
|
char *
|
2022-02-04 06:04:01 +08:00
|
|
|
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
2022-02-05 16:51:18 +08:00
|
|
|
assert(serial);
|
2022-02-05 17:07:13 +08:00
|
|
|
const char *const argv[] =
|
|
|
|
SC_ADB_COMMAND("-s", serial, "shell", "ip", "route");
|
2021-11-26 05:11:59 +08:00
|
|
|
|
|
|
|
sc_pipe pout;
|
2022-02-05 17:07:13 +08:00
|
|
|
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
|
2021-11-26 05:11:59 +08:00
|
|
|
if (pid == SC_PROCESS_NONE) {
|
|
|
|
LOGD("Could not execute \"ip route\"");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "adb shell ip route" output should contain only a few lines
|
|
|
|
char buf[1024];
|
2022-02-06 17:52:55 +08:00
|
|
|
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
2021-11-26 05:11:59 +08:00
|
|
|
sc_pipe_close(pout);
|
|
|
|
|
|
|
|
bool ok = process_check_success_intr(intr, pid, "ip route", flags);
|
|
|
|
if (!ok) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == -1) {
|
2022-01-27 19:26:43 +08:00
|
|
|
return NULL;
|
2021-11-26 05:11:59 +08:00
|
|
|
}
|
|
|
|
|
2022-02-06 17:52:55 +08:00
|
|
|
assert((size_t) r < sizeof(buf));
|
|
|
|
if (r == sizeof(buf) - 1) {
|
2021-11-26 05:11:59 +08:00
|
|
|
// The implementation assumes that the output of "ip route" fits in the
|
|
|
|
// buffer in a single pass
|
|
|
|
LOGW("Result of \"ip route\" does not fit in 1Kb. "
|
|
|
|
"Please report an issue.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-02-06 17:52:55 +08:00
|
|
|
// It is parsed as a NUL-terminated string
|
|
|
|
buf[r] = '\0';
|
|
|
|
|
|
|
|
return sc_adb_parse_device_ip_from_output(buf);
|
2021-11-26 05:11:59 +08:00
|
|
|
}
|
2022-02-06 22:02:29 +08:00
|
|
|
|
|
|
|
bool
|
|
|
|
sc_adb_is_serial_tcpip(const char *serial) {
|
|
|
|
return strchr(serial, ':');
|
|
|
|
}
|