Compare commits

..

14 Commits

Author SHA1 Message Date
e512b5bd69 Simplify adb_execute_p()
Only pass the stdout pipe as parameter, we never need stdin or stderr.
2021-11-17 21:59:59 +01:00
b7328a75e3 Make "adb get-serialno" interruptible
All process execution from the server must be interruptible, so that
Ctrl+c reacts immediately.
2021-11-17 21:59:48 +01:00
7516c0d1c5 Add interruptible function to read from pipe
This will avoid to block Ctrl+c if the process we read from takes too
much time.
2021-11-17 21:59:48 +01:00
6b4761820f Generalize string trunctation util function
Add an additional argument to let the client pass the possible end
chars.
2021-11-17 21:59:48 +01:00
e83562a6fe Expose util function to truncate first line
Move the local implementation from adb functions to the string util
functions.
2021-11-17 21:59:48 +01:00
921044e8a4 Always retrieve device serial
This allows to execute all adb commands with the specific -s parameter,
even if it is not provided by the user.

In practice, calling adb without -s works if there is exactly one device
connected. But some adb commands could be executed after another device
is executed (for example on drag & drop), so they need the specific
device serial.
2021-11-17 21:59:48 +01:00
b49f4e795e Add missing error handling
If "adb get-serialno" fails, attempting to read from the uninitialized
pipe is incorrect.
2021-11-17 21:59:19 +01:00
3eb8202adf Configure init and cleanup asynchronously
Accessing the settings (like --show-touches) on start should not delay
screen mirroring.
2021-11-17 20:09:22 +01:00
83f66ea06c Do not quit on cleanup configuration failure
Cleanup is used for some options like --show-touches to restore the
state on exit.

If the configuration fails, do not crash the whole process. Just log an
error.
2021-11-17 20:09:22 +01:00
3cc0921e61 Move init and cleanup to a separate method 2021-11-17 20:09:22 +01:00
d8358ce857 Read/write settings via command on Android >= 12
Before Android 8, executing the "settings" command from a shell was
very slow (~1 second), because it spawned a new app_process to execute
Java code. Therefore, to access settings without performance issues,
scrcpy used private APIs to read from and write to settings.

However, since Android 12, this is not possible anymore, due to
permissions changes.

To make it work again, execute the "settings" command on Android 12 (or
on previous version if the other method failed). This method is faster
than before Android 8 (~100ms).

Fixes #2671 <https://github.com/Genymobile/scrcpy/issues/2671>
Fixes #2788 <https://github.com/Genymobile/scrcpy/issues/2788>
2021-11-17 20:08:54 +01:00
4bc5baeeb5 Add throwable parameter to Log.w()
When an exception occurs, we might want to log a warning instead of an
error.
2021-11-17 19:59:43 +01:00
0672f7e405 Report settings errors via Exceptions
Settings read/write errors were silently ignored. Report them via a
SettingsException so that the caller can handle them.

This allows to log a proper error message, and will also allow to
fallback to a different settings method in case of failure.
2021-11-17 19:59:27 +01:00
6d6a436563 Wrap settings management into a Settings class
Until now, the code that needed to read/write the Android settings had
to explicitly open and close a ContentProvider.

Wrap these details into a Settings class.

This paves the way to provide an alternative implementation of settings
read/write for Android >= 12.
2021-11-17 19:52:04 +01:00
17 changed files with 223 additions and 308 deletions

View File

@ -416,27 +416,17 @@ autoadb scrcpy -s '{}'
To connect to a remote device, it is possible to connect a local `adb` client to To connect to a remote device, it is possible to connect a local `adb` client to
a remote `adb` server (provided they use the same version of the _adb_ a remote `adb` server (provided they use the same version of the _adb_
protocol). protocol):
First, make sure the ADB server is running on the remote computer:
```bash ```bash
adb start-server adb kill-server # kill the local adb server on 5037
``` ssh -CN -L5037:localhost:5037 -R27183:localhost:27183 your_remote_computer
Then, establish a SSH tunnel:
```bash
# local 5038 --> remote 5037
# local 27183 <-- remote 27183
ssh -CN -L5038:localhost:5037 -R27183:localhost:27183 your_remote_computer
# keep this open # keep this open
``` ```
From another terminal, run scrcpy: From another terminal:
```bash ```bash
export ADB_SERVER_SOCKET=tcp:localhost:5038
scrcpy scrcpy
``` ```
@ -444,16 +434,14 @@ To avoid enabling remote port forwarding, you could force a forward connection
instead (notice the `-L` instead of `-R`): instead (notice the `-L` instead of `-R`):
```bash ```bash
# local 5038 --> remote 5037 adb kill-server # kill the local adb server on 5037
# local 27183 --> remote 27183 ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 your_remote_computer
ssh -CN -L5038:localhost:5037 -L27183:localhost:27183 your_remote_computer
# keep this open # keep this open
``` ```
From another terminal, run scrcpy: From another terminal:
```bash ```bash
export ADB_SERVER_SOCKET=tcp:localhost:5038
scrcpy --force-adb-forward scrcpy --force-adb-forward
``` ```

View File

@ -7,7 +7,6 @@
#include "util/file.h" #include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#include "util/str.h" #include "util/str.h"
static const char *adb_command; static const char *adb_command;
@ -110,9 +109,9 @@ show_adb_err_msg(enum sc_process_result err, const char *const argv[]) {
free(buf); free(buf);
} }
static sc_pid sc_pid
adb_execute_p(const char *serial, const char *const adb_cmd[], adb_execute_p(const char *serial, const char *const adb_cmd[],
size_t len, unsigned inherit, sc_pipe *pout) { size_t len, sc_pipe *pout) {
int i; int i;
sc_pid pid; sc_pid pid;
@ -133,7 +132,7 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
memcpy(&argv[i], adb_cmd, len * sizeof(const char *)); memcpy(&argv[i], adb_cmd, len * sizeof(const char *));
argv[len + i] = NULL; argv[len + i] = NULL;
enum sc_process_result r = enum sc_process_result r =
sc_process_execute_p(argv, &pid, inherit, NULL, pout, NULL); sc_process_execute_p(argv, &pid, NULL, pout, NULL);
if (r != SC_PROCESS_SUCCESS) { if (r != SC_PROCESS_SUCCESS) {
show_adb_err_msg(r, argv); show_adb_err_msg(r, argv);
pid = SC_PROCESS_NONE; pid = SC_PROCESS_NONE;
@ -144,54 +143,50 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
} }
sc_pid sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len, adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {
unsigned inherit) { return adb_execute_p(serial, adb_cmd, len, NULL);
return adb_execute_p(serial, adb_cmd, len, inherit, NULL);
} }
static sc_pid sc_pid
adb_exec_forward(const char *serial, uint16_t local_port, adb_forward(const char *serial, uint16_t local_port,
const char *device_socket_name, unsigned inherit) { const char *device_socket_name) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"forward", local, remote}; const char *const adb_cmd[] = {"forward", local, remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_forward_remove(const char *serial, uint16_t local_port, adb_forward_remove(const char *serial, uint16_t local_port) {
unsigned inherit) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
const char *const adb_cmd[] = {"forward", "--remove", local}; const char *const adb_cmd[] = {"forward", "--remove", local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_reverse(const char *serial, const char *device_socket_name, adb_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port, unsigned inherit) { uint16_t local_port) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", remote, local}; const char *const adb_cmd[] = {"reverse", remote, local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_reverse_remove(const char *serial, const char *device_socket_name, adb_reverse_remove(const char *serial, const char *device_socket_name) {
unsigned inherit) {
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", "--remove", remote}; const char *const adb_cmd[] = {"reverse", "--remove", remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_push(const char *serial, const char *local, const char *remote, adb_push(const char *serial, const char *local, const char *remote) {
unsigned inherit) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the paths must be quoted // Windows will parse the string, so the paths must be quoted
// (see sys/win/command.c) // (see sys/win/command.c)
@ -207,7 +202,7 @@ adb_exec_push(const char *serial, const char *local, const char *remote,
#endif #endif
const char *const adb_cmd[] = {"push", local, remote}; const char *const adb_cmd[] = {"push", local, remote};
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__ #ifdef __WINDOWS__
free((void *) remote); free((void *) remote);
@ -217,8 +212,8 @@ adb_exec_push(const char *serial, const char *local, const char *remote,
return pid; return pid;
} }
static sc_pid sc_pid
adb_exec_install(const char *serial, const char *local, unsigned inherit) { adb_install(const char *serial, const char *local) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the local name must be quoted // Windows will parse the string, so the local name must be quoted
// (see sys/win/command.c) // (see sys/win/command.c)
@ -229,7 +224,7 @@ adb_exec_install(const char *serial, const char *local, unsigned inherit) {
#endif #endif
const char *const adb_cmd[] = {"install", "-r", local}; const char *const adb_cmd[] = {"install", "-r", local};
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__ #ifdef __WINDOWS__
free((void *) local); free((void *) local);
@ -238,79 +233,8 @@ adb_exec_install(const char *serial, const char *local, unsigned inherit) {
return pid; return pid;
} }
static sc_pid sc_pid
adb_exec_get_serialno(unsigned inherit, sc_pipe *pout) { adb_get_serialno(sc_pipe *pout) {
const char *const adb_cmd[] = {"get-serialno"}; const char *const adb_cmd[] = {"get-serialno"};
return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), inherit, pout); return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), pout);
}
bool
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
const char *device_socket_name, unsigned inherit) {
sc_pid pid =
adb_exec_forward(serial, local_port, device_socket_name, inherit);
return sc_process_check_success_intr(intr, pid, "adb forward", true);
}
bool
adb_forward_remove(struct sc_intr *intr, const char *serial,
uint16_t local_port, unsigned inherit) {
sc_pid pid = adb_exec_forward_remove(serial, local_port, inherit);
return sc_process_check_success_intr(intr, pid, "adb forward --remove",
true);
}
bool
adb_reverse(struct sc_intr *intr, const char *serial,
const char *device_socket_name, uint16_t local_port,
unsigned inherit) {
sc_pid pid =
adb_exec_reverse(serial, device_socket_name, local_port, inherit);
return sc_process_check_success_intr(intr, pid, "adb reverse", true);
}
bool
adb_reverse_remove(struct sc_intr *intr, const char *serial,
const char *device_socket_name, unsigned inherit) {
sc_pid pid = adb_exec_reverse_remove(serial, device_socket_name, inherit);
return sc_process_check_success_intr(intr, pid, "adb reverse --remove",
true);
}
bool
adb_push(struct sc_intr *intr, const char *serial, const char *local,
const char *remote, unsigned inherit) {
sc_pid pid = adb_exec_push(serial, local, remote, inherit);
return sc_process_check_success_intr(intr, pid, "adb push", true);
}
bool
adb_install(struct sc_intr *intr, const char *serial, const char *local,
unsigned inherit) {
sc_pid pid = adb_exec_install(serial, local, inherit);
return sc_process_check_success_intr(intr, pid, "adb install", true);
}
char *
adb_get_serialno(struct sc_intr *intr, unsigned inherit) {
sc_pipe pout;
sc_pid pid = adb_exec_get_serialno(inherit, &pout);
if (pid == SC_PROCESS_NONE) {
LOGE("Could not execute \"adb get-serialno\"");
return NULL;
}
char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf));
sc_pipe_close(pout);
bool ok =
sc_process_check_success_intr(intr, pid, "adb get-serialno", true);
if (!ok) {
return NULL;
}
sc_str_truncate(buf, r, " \r\n");
return strdup(buf);
} }

View File

@ -6,43 +6,37 @@
#include <stdbool.h> #include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#include "util/intr.h" #include "util/process.h"
sc_pid sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len, adb_execute(const char *serial, const char *const adb_cmd[], size_t len);
unsigned inherit);
bool sc_pid
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port, adb_execute_p(const char *serial, const char *const adb_cmd[], size_t len,
const char *device_socket_name, unsigned inherit); sc_pipe *pout);
bool sc_pid
adb_forward_remove(struct sc_intr *intr, const char *serial, adb_forward(const char *serial, uint16_t local_port,
uint16_t local_port, unsigned inherit); const char *device_socket_name);
bool sc_pid
adb_reverse(struct sc_intr *intr, const char *serial, adb_forward_remove(const char *serial, uint16_t local_port);
const char *device_socket_name, uint16_t local_port,
unsigned inherit);
bool sc_pid
adb_reverse_remove(struct sc_intr *intr, const char *serial, adb_reverse(const char *serial, const char *device_socket_name,
const char *device_socket_name, unsigned inherit); uint16_t local_port);
bool sc_pid
adb_push(struct sc_intr *intr, const char *serial, const char *local, adb_reverse_remove(const char *serial, const char *device_socket_name);
const char *remote, unsigned inherit);
bool sc_pid
adb_install(struct sc_intr *intr, const char *serial, const char *local, adb_push(const char *serial, const char *local, const char *remote);
unsigned inherit);
/** sc_pid
* Execute `adb get-serialno` adb_install(const char *serial, const char *local);
*
* Return the result, to be freed by the caller, or NULL on error. // Execute "adb get-serialno" and give a pipe to get the result
*/ sc_pid
char * adb_get_serialno(sc_pipe *pout);
adb_get_serialno(struct sc_intr *intr, unsigned inherit);
#endif #endif

View File

@ -9,6 +9,33 @@
#define SC_SOCKET_NAME "scrcpy" #define SC_SOCKET_NAME "scrcpy"
static bool
enable_tunnel_reverse(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_reverse(serial, SC_SOCKET_NAME, local_port);
return sc_process_check_success_intr(intr, pid, "adb reverse");
}
static bool
disable_tunnel_reverse(struct sc_intr *intr, const char *serial) {
sc_pid pid = adb_reverse_remove(serial, SC_SOCKET_NAME);
return sc_process_check_success_intr(intr, pid, "adb reverse --remove");
}
static bool
enable_tunnel_forward(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_forward(serial, local_port, SC_SOCKET_NAME);
return sc_process_check_success_intr(intr, pid, "adb forward");
}
static bool
disable_tunnel_forward(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_forward_remove(serial, local_port);
return sc_process_check_success_intr(intr, pid, "adb forward --remove");
}
static bool static bool
listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) { listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) {
return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1); return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1);
@ -20,7 +47,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
struct sc_port_range port_range) { struct sc_port_range port_range) {
uint16_t port = port_range.first; uint16_t port = port_range.first;
for (;;) { for (;;) {
if (!adb_reverse(intr, serial, SC_SOCKET_NAME, port, SC_STDERR)) { if (!enable_tunnel_reverse(intr, serial, port)) {
// the command itself failed, it will fail on any port // the command itself failed, it will fail on any port
return false; return false;
} }
@ -51,7 +78,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
} }
// failure, disable tunnel and try another port // failure, disable tunnel and try another port
if (!adb_reverse_remove(intr, serial, SC_SOCKET_NAME, SC_STDERR)) { if (!disable_tunnel_reverse(intr, serial)) {
LOGW("Could not remove reverse tunnel on port %" PRIu16, port); LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
} }
@ -81,7 +108,7 @@ enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel,
uint16_t port = port_range.first; uint16_t port = port_range.first;
for (;;) { for (;;) {
if (adb_forward(intr, serial, port, SC_SOCKET_NAME, SC_STDERR)) { if (enable_tunnel_forward(intr, serial, port)) {
// success // success
tunnel->local_port = port; tunnel->local_port = port;
tunnel->enabled = true; tunnel->enabled = true;
@ -146,9 +173,9 @@ sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
bool ret; bool ret;
if (tunnel->forward) { if (tunnel->forward) {
ret = adb_forward_remove(intr, serial, tunnel->local_port, SC_STDERR); ret = disable_tunnel_forward(intr, serial, tunnel->local_port);
} else { } else {
ret = adb_reverse_remove(intr, serial, SC_SOCKET_NAME, SC_STDERR); ret = disable_tunnel_reverse(intr, serial);
assert(tunnel->server_socket != SC_SOCKET_NONE); assert(tunnel->server_socket != SC_SOCKET_NONE);
if (!net_close(tunnel->server_socket)) { if (!net_close(tunnel->server_socket)) {

View File

@ -569,10 +569,6 @@ sc_getopt_adapter_create_longopts(void) {
size_t out_idx = 0; size_t out_idx = 0;
for (size_t i = 0; i < ARRAY_LEN(options); ++i) { for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
const struct sc_option *in = &options[i]; const struct sc_option *in = &options[i];
// If longopt_id is set, then longopt must be set
assert(!in->longopt_id || in->longopt);
if (!in->longopt) { if (!in->longopt) {
// The longopts array must only contain long options // The longopts array must only contain long options
continue; continue;
@ -675,12 +671,12 @@ print_option_usage_header(const struct sc_option *opt) {
} }
} }
printf("\n %s\n", buf.s); fprintf(stderr, "\n %s\n", buf.s);
free(buf.s); free(buf.s);
return; return;
error: error:
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
} }
static void static void
@ -696,11 +692,11 @@ print_option_usage(const struct sc_option *opt, unsigned cols) {
char *text = sc_str_wrap_lines(opt->text, cols, 8); char *text = sc_str_wrap_lines(opt->text, cols, 8);
if (!text) { if (!text) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", text); fprintf(stderr, "%s\n", text);
free(text); free(text);
} }
@ -711,11 +707,11 @@ print_shortcuts_intro(unsigned cols) {
"(left) Alt or (left) Super, but it can be configured by " "(left) Alt or (left) Super, but it can be configured by "
"--shortcut-mod (see above).", cols, 4); "--shortcut-mod (see above).", cols, 4);
if (!intro) { if (!intro) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", intro); fprintf(stderr, "%s\n", intro);
free(intro); free(intro);
} }
@ -725,21 +721,21 @@ print_shortcut(const struct sc_shortcut *shortcut, unsigned cols) {
assert(shortcut->shortcuts[0]); // At least one shortcut assert(shortcut->shortcuts[0]); // At least one shortcut
assert(shortcut->text); assert(shortcut->text);
printf("\n"); fprintf(stderr, "\n");
unsigned i = 0; unsigned i = 0;
while (shortcut->shortcuts[i]) { while (shortcut->shortcuts[i]) {
printf(" %s\n", shortcut->shortcuts[i]); fprintf(stderr, " %s\n", shortcut->shortcuts[i]);
++i; ++i;
}; };
char *text = sc_str_wrap_lines(shortcut->text, cols, 8); char *text = sc_str_wrap_lines(shortcut->text, cols, 8);
if (!text) { if (!text) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", text); fprintf(stderr, "%s\n", text);
free(text); free(text);
} }
@ -763,14 +759,14 @@ scrcpy_print_usage(const char *arg0) {
} }
} }
printf("Usage: %s [options]\n\n" fprintf(stderr, "Usage: %s [options]\n\n"
"Options:\n", arg0); "Options:\n", arg0);
for (size_t i = 0; i < ARRAY_LEN(options); ++i) { for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
print_option_usage(&options[i], cols); print_option_usage(&options[i], cols);
} }
// Print shortcuts section // Print shortcuts section
printf("\nShortcuts:\n\n"); fprintf(stderr, "\nShortcuts:\n\n");
print_shortcuts_intro(cols); print_shortcuts_intro(cols);
for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) { for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) {
print_shortcut(&shortcuts[i], cols); print_shortcut(&shortcuts[i], cols);

View File

@ -5,7 +5,6 @@
#include "adb.h" #include "adb.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#define DEFAULT_PUSH_TARGET "/sdcard/Download/" #define DEFAULT_PUSH_TARGET "/sdcard/Download/"
@ -17,7 +16,6 @@ file_handler_request_destroy(struct file_handler_request *req) {
bool bool
file_handler_init(struct file_handler *file_handler, const char *serial, file_handler_init(struct file_handler *file_handler, const char *serial,
const char *push_target) { const char *push_target) {
assert(serial);
cbuf_init(&file_handler->queue); cbuf_init(&file_handler->queue);
@ -32,26 +30,23 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
return false; return false;
} }
ok = sc_intr_init(&file_handler->intr); if (serial) {
if (!ok) {
LOGE("Could not create intr");
sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex);
}
file_handler->serial = strdup(serial); file_handler->serial = strdup(serial);
if (!file_handler->serial) { if (!file_handler->serial) {
LOGE("Could not strdup serial"); LOGW("Could not strdup serial");
sc_intr_destroy(&file_handler->intr);
sc_cond_destroy(&file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
return false; return false;
} }
} else {
file_handler->serial = NULL;
}
// lazy initialization // lazy initialization
file_handler->initialized = false; file_handler->initialized = false;
file_handler->stopped = false; file_handler->stopped = false;
file_handler->current_process = SC_PROCESS_NONE;
file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET; file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET;
@ -62,7 +57,6 @@ void
file_handler_destroy(struct file_handler *file_handler) { file_handler_destroy(struct file_handler *file_handler) {
sc_cond_destroy(&file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
sc_intr_destroy(&file_handler->intr);
free(file_handler->serial); free(file_handler->serial);
struct file_handler_request req; struct file_handler_request req;
@ -71,6 +65,16 @@ file_handler_destroy(struct file_handler *file_handler) {
} }
} }
static sc_pid
install_apk(const char *serial, const char *file) {
return adb_install(serial, file);
}
static sc_pid
push_file(const char *serial, const char *file, const char *push_target) {
return adb_push(serial, file, push_target);
}
bool bool
file_handler_request(struct file_handler *file_handler, file_handler_request(struct file_handler *file_handler,
file_handler_action_t action, char *file) { file_handler_action_t action, char *file) {
@ -102,16 +106,10 @@ file_handler_request(struct file_handler *file_handler,
static int static int
run_file_handler(void *data) { run_file_handler(void *data) {
struct file_handler *file_handler = data; struct file_handler *file_handler = data;
struct sc_intr *intr = &file_handler->intr;
const char *serial = file_handler->serial;
assert(serial);
const char *push_target = file_handler->push_target;
assert(push_target);
for (;;) { for (;;) {
sc_mutex_lock(&file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->current_process = SC_PROCESS_NONE;
while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) { while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) {
sc_cond_wait(&file_handler->event_cond, &file_handler->mutex); sc_cond_wait(&file_handler->event_cond, &file_handler->mutex);
} }
@ -124,28 +122,43 @@ run_file_handler(void *data) {
bool non_empty = cbuf_take(&file_handler->queue, &req); bool non_empty = cbuf_take(&file_handler->queue, &req);
assert(non_empty); assert(non_empty);
(void) non_empty; (void) non_empty;
sc_pid pid;
if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file);
pid = install_apk(file_handler->serial, req.file);
} else {
LOGI("Pushing %s...", req.file);
pid = push_file(file_handler->serial, req.file,
file_handler->push_target);
}
file_handler->current_process = pid;
sc_mutex_unlock(&file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
if (req.action == ACTION_INSTALL_APK) { if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file); if (sc_process_check_success(pid, "adb install", false)) {
bool ok =
adb_install(intr, serial, req.file, SC_STDOUT | SC_STDERR);
if (ok) {
LOGI("%s successfully installed", req.file); LOGI("%s successfully installed", req.file);
} else { } else {
LOGE("Failed to install %s", req.file); LOGE("Failed to install %s", req.file);
} }
} else { } else {
LOGI("Pushing %s...", req.file); if (sc_process_check_success(pid, "adb push", false)) {
bool ok = adb_push(intr, serial, req.file, push_target, LOGI("%s successfully pushed to %s", req.file,
SC_STDOUT | SC_STDERR); file_handler->push_target);
if (ok) {
LOGI("%s successfully pushed to %s", req.file, push_target);
} else { } else {
LOGE("Failed to push %s to %s", req.file, push_target); LOGE("Failed to push %s to %s", req.file,
file_handler->push_target);
} }
} }
sc_mutex_lock(&file_handler->mutex);
// Close the process (it is necessarily already terminated)
// Execute this call with mutex locked to avoid race conditions with
// file_handler_stop()
sc_process_close(file_handler->current_process);
file_handler->current_process = SC_PROCESS_NONE;
sc_mutex_unlock(&file_handler->mutex);
file_handler_request_destroy(&req); file_handler_request_destroy(&req);
} }
return 0; return 0;
@ -170,7 +183,11 @@ file_handler_stop(struct file_handler *file_handler) {
sc_mutex_lock(&file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->stopped = true; file_handler->stopped = true;
sc_cond_signal(&file_handler->event_cond); sc_cond_signal(&file_handler->event_cond);
sc_intr_interrupt(&file_handler->intr); if (file_handler->current_process != SC_PROCESS_NONE) {
if (!sc_process_terminate(file_handler->current_process)) {
LOGW("Could not terminate push/install process");
}
}
sc_mutex_unlock(&file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
} }

View File

@ -8,7 +8,6 @@
#include "adb.h" #include "adb.h"
#include "util/cbuf.h" #include "util/cbuf.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/intr.h"
typedef enum { typedef enum {
ACTION_INSTALL_APK, ACTION_INSTALL_APK,
@ -30,9 +29,8 @@ struct file_handler {
sc_cond event_cond; sc_cond event_cond;
bool stopped; bool stopped;
bool initialized; bool initialized;
sc_pid current_process;
struct file_handler_request_queue queue; struct file_handler_request_queue queue;
struct sc_intr intr;
}; };
bool bool

View File

@ -47,9 +47,6 @@ main(int argc, char *argv[]) {
setbuf(stderr, NULL); setbuf(stderr, NULL);
#endif #endif
printf("scrcpy " SCRCPY_VERSION
" <https://github.com/Genymobile/scrcpy>\n");
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = scrcpy_options_default, .opts = scrcpy_options_default,
.help = false, .help = false,
@ -76,6 +73,8 @@ main(int argc, char *argv[]) {
return 0; return 0;
} }
LOGI("scrcpy " SCRCPY_VERSION " <https://github.com/Genymobile/scrcpy>");
#ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL #ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL
av_register_all(); av_register_all();
#endif #endif

View File

@ -112,10 +112,9 @@ push_server(struct sc_intr *intr, const char *serial) {
free(server_path); free(server_path);
return false; return false;
} }
bool ok = adb_push(intr, serial, server_path, SC_DEVICE_SERVER_PATH, sc_pid pid = adb_push(serial, server_path, SC_DEVICE_SERVER_PATH);
SC_STDERR);
free(server_path); free(server_path);
return ok; return sc_process_check_success_intr(intr, pid, "adb push");
} }
static const char * static const char *
@ -199,8 +198,7 @@ execute_server(struct sc_server *server,
// Port: 5005 // Port: 5005
// Then click on "Debug" // Then click on "Debug"
#endif #endif
// Inherit both stdout and stderr (all server logs are printed to stdout) return adb_execute(serial, cmd, ARRAY_LEN(cmd));
return adb_execute(serial, cmd, ARRAY_LEN(cmd), SC_STDOUT | SC_STDERR);
} }
static bool static bool
@ -424,6 +422,29 @@ sc_server_on_terminated(void *userdata) {
LOGD("Server terminated"); LOGD("Server terminated");
} }
static char *
sc_server_get_serialno(struct sc_intr *intr) {
sc_pipe pout;
sc_pid pid = adb_get_serialno(&pout);
if (pid == SC_PROCESS_NONE) {
return false;
}
char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf));
sc_pipe_close(pout);
bool ok = sc_process_check_success_intr(intr, pid, "adb get-serialno");
sc_process_close(pid);
if (!ok) {
return NULL;
}
sc_str_truncate(buf, r, " \r\n");
return strdup(buf);
}
static bool static bool
sc_server_fill_serial(struct sc_server *server) { sc_server_fill_serial(struct sc_server *server) {
// Retrieve the actual device immediately if not provided, so that all // Retrieve the actual device immediately if not provided, so that all
@ -432,7 +453,7 @@ sc_server_fill_serial(struct sc_server *server) {
// device/emulator" error) // device/emulator" error)
if (!server->params.serial) { if (!server->params.serial) {
// The serial is owned by sc_server_params, and will be freed on destroy // The serial is owned by sc_server_params, and will be freed on destroy
server->params.serial = adb_get_serialno(&server->intr, SC_STDERR); server->params.serial = sc_server_get_serialno(&server->intr);
if (!server->params.serial) { if (!server->params.serial) {
LOGE("Could not get device serial"); LOGE("Could not get device serial");
return false; return false;
@ -459,8 +480,6 @@ run_server(void *data) {
goto error_connection_failed; goto error_connection_failed;
} }
LOGI("Server pushed");
ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, params->serial, ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, params->serial,
params->port_range, params->force_adb_forward); params->port_range, params->force_adb_forward);
if (!ok) { if (!ok) {

View File

@ -11,16 +11,8 @@
#include "util/log.h" #include "util/log.h"
enum sc_process_result enum sc_process_result
sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit, sc_process_execute_p(const char *const argv[], sc_pid *pid,
int *pin, int *pout, int *perr) { int *pin, int *pout, int *perr) {
bool inherit_stdout = inherit & SC_STDOUT;
bool inherit_stderr = inherit & SC_STDERR;
// If pout is defined, then inherit MUST NOT contain SC_STDOUT.
assert(!pout || !inherit_stdout);
// If perr is defined, then inherit MUST NOT contain SC_STDERR.
assert(!perr || !inherit_stderr);
int in[2]; int in[2];
int out[2]; int out[2];
int err[2]; int err[2];
@ -98,31 +90,20 @@ sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit,
} }
close(in[1]); close(in[1]);
} }
// Do not close stdin in the child process, this makes adb fail on
// Linux
if (pout) { if (pout) {
if (out[1] != STDOUT_FILENO) { if (out[1] != STDOUT_FILENO) {
dup2(out[1], STDOUT_FILENO); dup2(out[1], STDOUT_FILENO);
close(out[1]); close(out[1]);
} }
close(out[0]); close(out[0]);
} else if (!inherit_stdout) {
// Close stdout in the child process
close(STDOUT_FILENO);
} }
if (perr) { if (perr) {
if (err[1] != STDERR_FILENO) { if (err[1] != STDERR_FILENO) {
dup2(err[1], STDERR_FILENO); dup2(err[1], STDERR_FILENO);
close(err[1]); close(err[1]);
} }
close(err[0]); close(err[0]);
} else if (!inherit_stderr) {
// Close stderr in the child process
close(STDERR_FILENO);
} }
close(internal[0]); close(internal[0]);
enum sc_process_result err; enum sc_process_result err;
if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) { if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) {

View File

@ -27,22 +27,12 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
} }
enum sc_process_result enum sc_process_result
sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit, sc_process_execute_p(const char *const argv[], HANDLE *handle,
HANDLE *pin, HANDLE *pout, HANDLE *perr) { HANDLE *pin, HANDLE *pout, HANDLE *perr) {
bool inherit_stdout = inherit & SC_STDOUT; enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
bool inherit_stderr = inherit & SC_STDERR;
// If pout is defined, then inherit MUST NOT contain SC_STDOUT.
assert(!pout || !inherit_stdout);
// If perr is defined, then inherit MUST NOT contain SC_STDERR.
assert(!perr || !inherit_stderr);
// Add 1 per non-NULL pointer // Add 1 per non-NULL pointer
unsigned handle_count = !!pin unsigned handle_count = !!pin + !!pout + !!perr;
+ (pout || inherit_stdout)
+ (perr || inherit_stderr);
enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.nLength = sizeof(SECURITY_ATTRIBUTES);
@ -90,25 +80,16 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit,
HANDLE handles[3]; HANDLE handles[3];
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
// Must be set even if handle_count == 0, so that stdin, stdout and stderr
// are NOT inherited in that case
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (handle_count) { if (handle_count) {
unsigned i = 0; si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (pin) { if (pin) {
si.StartupInfo.hStdInput = stdin_read_handle; si.StartupInfo.hStdInput = stdin_read_handle;
handles[i++] = si.StartupInfo.hStdInput;
} }
if (pout || inherit_stdout) { if (pout) {
si.StartupInfo.hStdOutput = pout ? stdout_write_handle si.StartupInfo.hStdOutput = stdout_write_handle;
: GetStdHandle(STD_OUTPUT_HANDLE);
handles[i++] = si.StartupInfo.hStdOutput;
} }
if (perr || inherit_stderr) { if (perr) {
si.StartupInfo.hStdError = perr ? stderr_write_handle si.StartupInfo.hStdError = stderr_write_handle;
: GetStdHandle(STD_ERROR_HANDLE);
handles[i++] = si.StartupInfo.hStdError;
} }
SIZE_T size; SIZE_T size;
@ -131,6 +112,17 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit,
goto error_close_stderr; goto error_close_stderr;
} }
// Explicitly pass the HANDLEs that must be inherited
unsigned i = 0;
if (pin) {
handles[i++] = stdin_read_handle;
}
if (pout) {
handles[i++] = stdout_write_handle;
}
if (perr) {
handles[i++] = stderr_write_handle;
}
ok = UpdateProcThreadAttribute(lpAttributeList, 0, ok = UpdateProcThreadAttribute(lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
handles, handle_count * sizeof(HANDLE), handles, handle_count * sizeof(HANDLE),

View File

@ -5,8 +5,8 @@
#include "log.h" #include "log.h"
enum sc_process_result enum sc_process_result
sc_process_execute(const char *const argv[], sc_pid *pid, unsigned inherit) { sc_process_execute(const char *const argv[], sc_pid *pid) {
return sc_process_execute_p(argv, pid, inherit, NULL, NULL, NULL); return sc_process_execute_p(argv, pid, NULL, NULL, NULL);
} }
bool bool

View File

@ -67,31 +67,20 @@ enum sc_process_result {
SC_PROCESS_ERROR_MISSING_BINARY, SC_PROCESS_ERROR_MISSING_BINARY,
}; };
#define SC_STDOUT (1 << 0)
#define SC_STDERR (1 << 1)
/** /**
* Execute the command and write the process id to `pid` * Execute the command and write the process id to `pid`
*
* The parameter `inherit` is a OR of any of SC_STDOUT and SC_STDERR. It
* indicates if stdout and stderr must be inherited from the scrcpy process (in
* other words, if the process must output to the scrcpy console).
*/ */
enum sc_process_result enum sc_process_result
sc_process_execute(const char *const argv[], sc_pid *pid, unsigned inherit); sc_process_execute(const char *const argv[], sc_pid *pid);
/** /**
* Execute the command and write the process id to `pid` * Execute the command and write the process id to `pid`
* *
* If not NULL, provide a pipe for stdin (`pin`), stdout (`pout`) and stderr * If not NULL, provide a pipe for stdin (`pin`), stdout (`pout`) and stderr
* (`perr`). * (`perr`).
*
* The parameter `inherit` has the same semantics as in `sc_process_execute()`.
* If `pout` is not NULL, then `inherit` MUST NOT contain SC_STDOUT.
* If `perr` is not NULL, then `inherit` MUST NOT contain SC_STDERR.
*/ */
enum sc_process_result enum sc_process_result
sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit, sc_process_execute_p(const char *const argv[], sc_pid *pid,
sc_pipe *pin, sc_pipe *pout, sc_pipe *perr); sc_pipe *pin, sc_pipe *pout, sc_pipe *perr);
/** /**

View File

@ -2,7 +2,7 @@
bool bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid, sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
const char *name, bool close) { const char *name) {
if (!sc_intr_set_process(intr, pid)) { if (!sc_intr_set_process(intr, pid)) {
// Already interrupted // Already interrupted
return false; return false;
@ -12,12 +12,6 @@ sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
bool ret = sc_process_check_success(pid, name, false); bool ret = sc_process_check_success(pid, name, false);
sc_intr_set_process(intr, SC_PROCESS_NONE); sc_intr_set_process(intr, SC_PROCESS_NONE);
if (close) {
// Close separately
sc_process_close(pid);
}
return ret; return ret;
} }

View File

@ -8,7 +8,7 @@
bool bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid, sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
const char *name, bool close); const char *name);
ssize_t ssize_t
sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data, sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,

View File

@ -295,7 +295,10 @@ error:
size_t size_t
sc_str_truncate(char *data, size_t len, const char *endchars) { sc_str_truncate(char *data, size_t len, const char *endchars) {
data[len - 1] = '\0'; data[len - 1] = '\0';
size_t idx = strcspn(data, endchars); char *eol = strpbrk(data, endchars);
data[idx] = '\0'; if (eol) {
return idx; *eol = '\0';
len = eol - data;
}
return len;
} }

View File

@ -339,28 +339,22 @@ static void test_wrap_lines(void) {
static void test_truncate(void) { static void test_truncate(void) {
char s[] = "hello\nworld\n!"; char s[] = "hello\nworld\n!";
size_t len = sc_str_truncate(s, sizeof(s), "\n"); size_t line_len = sc_str_truncate(s, sizeof(s), "\n");
assert(len == 5); assert(line_len == 5);
assert(!strcmp("hello", s)); assert(!strcmp("hello", s));
char s2[] = "hello\r\nworkd\r\n!"; char s2[] = "hello\r\nworkd\r\n!";
len = sc_str_truncate(s2, sizeof(s2), "\n\r"); line_len = sc_str_truncate(s2, sizeof(s2), "\n\r");
assert(len == 5); assert(line_len == 5);
assert(!strcmp("hello", s)); assert(!strcmp("hello", s));
char s3[] = "hello world\n!"; char s3[] = "hello world\n!";
len = sc_str_truncate(s3, sizeof(s3), " \n\r"); line_len = sc_str_truncate(s3, sizeof(s3), " \n\r");
assert(len == 5); assert(line_len == 5);
assert(!strcmp("hello", s3)); assert(!strcmp("hello", s3));
char s4[] = "hello ";
len = sc_str_truncate(s4, sizeof(s4), " \n\r");
assert(len == 5);
assert(!strcmp("hello", s4));
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {